【问题标题】:Am I using plyr right? I seem to be using way too much memory我使用 plyr 对吗?我似乎使用了太多的内存
【发布时间】:2012-01-17 05:58:25
【问题描述】:

我有以下有点大的数据集:

 > dim(dset)
 [1] 422105     25
 > class(dset)
 [1] "data.frame"
 > 

不做任何事情,R 进程似乎需要大约 1GB 的 RAM。

我正在尝试运行以下代码:

  dset <- ddply(dset, .(tic), transform,
                date.min <- min(date),
                date.max <- max(date),
                daterange <- max(date) - min(date),
                .parallel = TRUE)

运行该代码,RAM 使用量猛增。它完全饱和了 60GB 的 RAM,在 32 核机器上运行。我做错了什么?

【问题讨论】:

  • 作为快速跟进,我尝试使用更小的数据子集进行此操作,但 RAM 使用量仍高达 17 GB
  • &lt;- 在我看来很有趣。如果您在括号中使用 = 会发生什么?

标签: r plyr data.table


【解决方案1】:

如果性能是一个问题,最好从同名包中切换到使用data.tables。它们。你会做一些大致相当于这样的事情:

library(data.table)
dat <- data.frame(x = runif(100),
                  dt = seq.Date(as.Date('2010-01-01'),as.Date('2011-01-01'),length.out = 100),
                  grp = rep(letters[1:4],each = 25))

dt <- as.data.table(dat)
key(dt) <- "grp"

dt[,mutate(.SD,date.min = min(dt),
               date.max = max(dt),
               daterange = max(dt) - min(dt)), by = grp]

【讨论】:

  • +1 -- 我会支持它。我刚刚对plyr 和一个略有不同的data.table 解决方案进行了计时,tic 有 30000 个级别。 ddply 用了 247.80 秒,data.table 用了 3.75 秒。此外,data.table 还被设计为使用更少的内存,对 data.frames 实现 pass-by-reference 而不是内存消耗的 pass-by-value。
  • mutate(.SD,... 应更改为 with(.SD,list(date.min = min(dt),... 以获得正确答案。
  • 感谢您的示例代码。现在在更适中的机器(核心 i5、6GB RAM)上尝试使用我的数据集
  • @stevejb 一定要试试 Josh 的策略,我只是拼凑了一个简单的例子来说明;他的表现可能会更好。
  • @joran 我做到了。很酷的代码。自从您先回答后,我就给了您答案,但是我的代码基于 Josh 的。谢谢!
【解决方案2】:

这是data.table 的另一种应用程序,说明了它的速度有多快。 (注意:这使用dset,由Brian Diggs 在他的回答中构建的data.frame,除了tic 的30000 层而不是10 层)。

(这比@joran 的解决方案快得多的原因是它避免使用.SD,而是直接使用列。样式与plyr 有点不同,但它通常会带来巨大的加速. 对于另一个示例,请参见 data.table Wiki,其中:(a) 将其作为建议 #1;并且 (b) 显示删除 .SD 的代码的速度提高了 50 倍。

library(data.table)
system.time({
    dt <- data.table(dset, key="tic")
    # Summarize by groups and store results in a summary data.table
    sumdt <- dt[ ,list(min.date=min(date), max.date=max(date)), by="tic"]
    sumdt[, daterange:= max.date-min.date]
    # Merge the summary data.table back into dt, based on key
    dt <- dt[sumdt]
})
# ELAPSED TIME IN SECONDS
# user  system elapsed 
# 1.45    0.25    1.77 

【讨论】:

  • 这确实非常快。对于我的数据集,经过时间为 2.621 秒。非常感谢!
【解决方案3】:

我想到了几件事。

首先,我会写成:

dset <- ddply(dset, .(tic), summarise,
                date.min = min(date),
                date.max = max(date),
                daterange = max(date) - min(date),
                .parallel = TRUE)

好吧,实际上,我可能会避免重复计算最小/最大日期并写

dset <- ddply(dset, .(tic), function(DF) {
              mutate(summarise(DF, date.min = min(date),
                               date.max = max(date)),
                     daterange = date.max - date.min)},
              .parallel = TRUE)

但这不是你要问的重点。

使用您的维度的虚拟数据集

n <- 422105
dset <- data.frame(date=as.Date("2000-01-01")+sample(3650, n, replace=TRUE),
    tic = factor(sample(10, n, replace=TRUE)))
for (i in 3:25) {
    dset[i] <- rnorm(n)
}

这可以在我的笔记本电脑上轻松运行(不到 1 分钟)。事实上,plyr 步骤比创建虚拟数据集花费的时间更少。所以它不可能换成你看到的大小。

第二种可能是tic 存在大量唯一值。这可能会增加所需的大小。但是,当我尝试将唯一 tic 值的可能数量增加到 1000 时,它并没有真正放慢速度。

最后,它可能是并行化中的某些东西。我没有为foreach 注册并行后端,所以它只是在做串行方法。也许这会导致你的内存爆炸。

【讨论】:

  • tic 大约有 30000 个级别。我在 plyr 上阅读了 JSS 文章,它说要小心不要制作额外的数据副本。这就是内存使用量如此惊人的原因。
  • @stevejb:我有过类似的经历; plyr 似乎不能很好地处理大量的分类级别(在我的情况下约为 25000)。我最终回到了 base R 的 splittapply,它们完成了这项工作,尽管方式要麻烦得多。
  • 这是因为 plyr 内部使用了数据帧,不幸的是,这些数据帧很慢而且很耗内存。
【解决方案4】:

数据框中是否有许多因子水平?我发现这种类型的过度内存使用在 adply 和可能的其他 plyr 函数中很常见,但可以通过删除不必要的因素和级别来补救。如果将大数据框读入 R,请确保在导入中将 stringsAsFactors 设置为 FALSE:

dat = read.csv(header=TRUE, sep="\t", file="dat.tsv", stringsAsFactors=FALSE)

然后分配你实际需要的因素。

我还没有调查 Hadley 的来源以找出原因。

【讨论】:

    猜你喜欢
    • 2012-01-12
    • 1970-01-01
    • 1970-01-01
    • 2014-09-20
    • 2017-06-18
    • 1970-01-01
    • 1970-01-01
    • 2013-03-23
    • 1970-01-01
    相关资源
    最近更新 更多