【问题标题】:nested lapply performance, how to optimise?嵌套 lapply 性能,如何优化?
【发布时间】:2013-01-31 11:56:56
【问题描述】:

我有一个data.frames 的列表。在我想通过分组 (z) 拆分的每个 data.frame 中运行一个函数,将结果放回一起,然后将嵌套 lapply 的所有结果放在一个 data.frame 中,然后展平结果列表data.frames 合并为一个data.frame

library(plyr)
df <- data.frame(x = sample(1:200, 30000, replace = TRUE), 
                y = sample(1:200, 30000, replace = TRUE), 
                z = sample(LETTERS, 30000, replace = TRUE))

alist <- list(df,df,df) # longer in real life
answer <- lapply(alist, function(q) {
    a <- split(q,q$z)
    result.1 <- lapply(a, function(w) {
        neww <- cbind(w[,1],w[,2])
        result.2 <- colSums(neww)
    })
    ldply(result.1)
})
# cor(neww) can actually be a variey of foos I just use cor() for easy reproducibility
ldply(answer)

这会占用大量内存,而且速度也很慢。感谢@Andrie,我知道如何在开始之前清理我的工作区:

 rm(list=setdiff(ls(), "alist"))

但是有没有办法修改我的方法,比如在第二个 lapply 中丢弃 w 等,以尝试减少内存使用并加快速度?在这种情况下,foo 喜欢矩阵,所以data.table 不会是我的答案。在其他foos 中,我将需要所有w,并且课程需要是data.frame

【问题讨论】:

  • 请使您的代码可重现。您需要显示foo
  • 一种策略可能是将所有data.frames 合并为一个data.table,然后拆分-应用-合并。
  • 我使用了很多 foos 作为参数,你可以使用 cor。我的重点是围绕foo 的代码,但也许我没有抓住重点。
  • 重点是让它容易回答。为此,我需要能够将我的结果与您的结果进行比较。这意味着您的代码应该在复制到我的 R 会话时运行。
  • @Roland,刚刚注意到您也提到了data.table 解决方案。

标签: r list lapply


【解决方案1】:

试试这样的:

ldply(alist, ddply, "z", summarize, xy.foo = foo(x, y))

如果您希望xy 显示在最终的data.frame 中,请将summarize 替换为transform。此外,查看您的foo 使用情况,您可能需要将(x, y) 替换为cbind(x, y)

另外,我建议您分析您的代码。最后,foo 可能是让你慢下来的原因,而不是拆分/合并部分。

【讨论】:

  • +1 感谢@flodel 这看起来显然不那么冗长和吸引人,并且似乎节省了ldplys,但我认为在考虑速度时我应该自然地避开ddply。这里节省的内存的一部分是否不太明确的对象创建?如果您想添加任何简短的解释,我将不胜感激。当然foo 是可怕的瓶颈,但很难改变。因此,围绕foo 的方法是我的重点。
  • @user1320502 如果foo 是真正的瓶颈,那么并行化可能是解决之道。
  • 如果并行化是要走的路,plyr 确实内置了这种能力。请参阅.parallel 参数和相关参数。
【解决方案2】:

您为什么不使用plyr 中的ddplyllply 而只使用ldply??

# Note: @Flodel has a very nice, simple one-line plyr solution
# Please use that.
out <- ldply(alist, function(q) {
    ddply(q, .(z), function(w) {
        neww <- w[, -3]
        result.2 <- colSums(neww) # dummy function
    })
})

第一个ldply 一个接一个地传递列表alist 的元素。因此,每次q 都是data.frame 包含在list 的每个元素中。然后,在此范围内,我们想按z 拆分。由于输入是qdata.frame 并且输出也应该是data.frame 我们使用ddply 和第二个参数.(z) 来分割z。在这里,您进行计算,返回您想要的任何内容(在本例中为colSums)。 ldply 返回为 data.frame

Data.table 解决方案:另一种快速 解决方案是在组合data.frame 上使用data.table,可以如下实现(@Roland 在他的 cmets 中也提到过) :

require(data.table)
# for creating a group 
group <- vapply(alist, nrow, integer(1))
dt <- data.table(do.call(rbind, alist))
# create group
dt[ , grp := rep(1:3, group)]
setkey(dt, "grp", "z")
# call your function (here column means)
dt[, lapply(.SD, mean), by="grp,z"]
# or if its correlation
dt[, list(cor_x_y = cor(x,y)), by="grp,z"]

【讨论】:

  • 不是ldply(llply(...))ldply(...)一样的东西,可以省去不必要的处理吗?
  • 另外,您不需要即时定义function(q)ddply(q, ...) 函数,只需像我一样直接使用ddply, ...
  • 你在这两种情况下都是对的。第一个,我以这种方式使用它,因为我观察到这比仅使用ldply 更快,但可能是一种错误的预感(我会测试它)。第二条评论,非常真实,没有必要按照我的方式去做!谢谢指点。
  • 我将保留这样的解决方案。否则它将与您的相同。它可以作为how-not-to 写一个plyr 函数:)
  • 刚刚将sapply 换成vapply,因为sapply 在输入列表为空时表现异常。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-10
  • 2020-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-09
相关资源
最近更新 更多