【问题标题】:Memory efficient alternative to rbind - in-place rbind?rbind 的内存高效替代方案 - 就地 rbind?
【发布时间】:2011-10-28 23:15:26
【问题描述】:

我需要 rbind 两个大数据帧。现在我用

df <- rbind(df, df.extension)

但我(几乎)立即耗尽了内存。我猜是因为 df 在内存中保存了两次。将来我可能会看到更大的数据帧,所以我需要某种就地 rbind。

所以我的问题是:有没有办法在使用 rbind 时避免内存中的数据重复?

我找到了这个question,它使用了SqlLite,但我真的很想避免将硬盘驱动器用作缓存。

【问题讨论】:

  • @Dwin:你付钱了吗?如果是这样,你也可以给我买一些吗? ;-)
  • 如果我为自己工作,它会为提高生产力而付出代价,当我向我现在的雇主提出这个论点时,它被接受为“商业案例”。
  • @DWin:两个问题:1:我了解到(重新)编码时间需要 TARDIS。 2:超出一个特定的最佳位置,内存映射比获得更多 RAM 更好。通常,HPC 的目标函数是多维的。
  • 告诉我们两个 dfs 的尺寸。好像object.size(df) &gt;&gt; object.size(df.extension),对吧?另外,我们可以安全地假设它们的两列在数量、名称、类型、因子水平上都相同吗?所以我们不需要检查、填充缺失的列、NA 等?

标签: r dataframe rbind


【解决方案1】:

data.table是你的朋友!

参考http://www.mail-archive.com/r-help@r-project.org/msg175877.html


跟进 nikola 的评论,这里是 ?rbindlist 的描述(v1.8.2 中的新内容):

do.call("rbind",l) 相同,但速度更快。

【讨论】:

  • 另外,data.table 的 1.8.2 版具有 rbindlist 功能,这将有助于那里。
  • 请注意,rbindlist 不检查列名,这是它更快的部分原因。
  • 请注意,rbindlist 不检查列名,这是它更快的部分原因。 dplyr 的 rbind_all 稍微慢一些,但会检查列名,所以有时它会更有用。
【解决方案2】:

现在我想出了以下解决方案:

nextrow = nrow(df)+1
df[nextrow:(nextrow+nrow(df.extension)-1),] = df.extension
# we need to assure unique row names
row.names(df) = 1:nrow(df)

现在我没有用完内存。我认为是因为我存储

object.size(df) + 2 * object.size(df.extension)

而使用 rbind R 则需要

object.size(rbind(df,df.extension)) + object.size(df) + object.size(df.extension). 

之后我使用

rm(df.extension)
gc(reset=TRUE)

释放我不再需要的内存。

这暂时解决了我的问题,但我觉得有一种更高级的方法来执行内存高效的 rbind。我感谢任何有关此解决方案的 cmets。

【讨论】:

  • 这是尽可能“到位”的。它使用的内存量与我的解决方案大致相同,并且出现错误的机会更少。很不错。另外,你为什么想要更复杂的东西,因为它没有并发症?唯一的问题是您丢失了原始 df,但如果这不是问题,那么您的就是最好的解决方案。
  • @Joris:谢谢。我知道我丢失了原始 df,但这是我必须做出的妥协。为您的内存性能分析点赞。
  • 好像是object.size(df) &gt;&gt; object.size(df.extension),对吧?
  • 真正就地执行rbind 的更高级方法是data.table::rbind_all, per Ari Friedman's answer
【解决方案3】:

这是bigmemory 的完美候选人。请参阅the site 了解更多信息。以下是需要考虑的三个使用方面:

  1. 可以使用 HD:内存映射到 HD 比几乎任何其他访问都快得多,因此您可能看不到任何减速。有时我依赖大于 1TB 的内存映射矩阵,尽管大多数在 6 到 50GB 之间。此外,由于对象一个矩阵,这不需要为了使用该对象而重写代码的实际开销。
  2. 无论您是否使用文件支持的矩阵,您都可以使用separated = TRUE 将列分开。由于我的第三个提示,我没有使用这么多:
  3. 您可以过度分配 HD 空间以允许更大的潜在矩阵大小,但仅加载感兴趣的子矩阵。这样就不用rbind了。

注意:虽然最初的问题涉及数据帧和大内存适用于矩阵,但如果确实需要,可以轻松地为不同类型的数据创建不同的矩阵,然后组合 RAM 中的对象以创建数据帧。

【讨论】:

  • errr... 我们在这里谈论的是数据帧,而且远非每个数据帧都可以转换为矩阵。例如,考虑一个带有整数和因子的数据框......
  • @Joris:我们同时在想同样的想法,先生。 :) 查看我的更新。
  • 那么你会如何处理因素呢?另外,您会失去数据框的所有其他功能。
  • 只需单独存储因子水平并使用整数进行相互转换。我没有将因子发送到bigmatrix,因为我通常自己处理分层。无论如何,我经常被其他 R 代码破坏而无法真正使用它们。对于此类数据,我几乎总是坚持使用整数并使用变量名来指示类型。如果某些东西负责任地处理因素,我会在传递数据之前进行转换。
  • (续)一个例外是字符串因素。对它们的破坏更容易检测到。
【解决方案4】:

首先:如果您想安全,请使用您链接到的其他问题的解决方案。由于 R 是按值调用的,因此请忘记不会将数据帧复制到内存中的“就地”方法。

一种不建议节省大量内存的方法,是假装你的数据帧是列表,使用 for 循环强制列表(应用会像地狱一样吃掉内存)并让 R 相信它实际上是一个数据框。

我会再次警告您:在更复杂的数据帧上使用它会带来麻烦和难以发现的错误。因此,请确保您的测试足够好,如果可能,请尽可能避免这种情况。

您可以尝试以下方法:

n1 <- 1000000
n2 <- 1000000
ncols <- 20
dtf1 <- as.data.frame(matrix(sample(n1*ncols), n1, ncols))
dtf2 <- as.data.frame(matrix(sample(n2*ncols), n1, ncols))

dtf <- list()

for(i in names(dtf1)){
  dtf[[i]] <- c(dtf1[[i]],dtf2[[i]])
}

attr(dtf,"row.names") <- 1:(n1+n2)
attr(dtf,"class") <- "data.frame"

它会删除您实际拥有的行名(您可以重建它们,但检查是否有重复的行名!)。它也不执行 rbind 中包含的所有其他测试。

在我的测试中为您节省了大约一半的内存,并且在我的测试中 dtfcomb 和 dtf 是相等的。红框是 rbind,黄框是我的基于列表的方法。

测试脚本:

n1 <- 3000000
n2 <- 3000000
ncols <- 20

dtf1 <- as.data.frame(matrix(sample(n1*ncols), n1, ncols))
dtf2 <- as.data.frame(matrix(sample(n2*ncols), n1, ncols))

gc()
Sys.sleep(10)
dtfcomb <- rbind(dtf1,dtf2)
Sys.sleep(10)
gc()
Sys.sleep(10)
rm(dtfcomb)
gc()
Sys.sleep(10)
dtf <- list()
for(i in names(dtf1)){
  dtf[[i]] <- c(dtf1[[i]],dtf2[[i]])
}
attr(dtf,"row.names") <- 1:(n1+n2)
attr(dtf,"class") <- "data.frame"
Sys.sleep(10)
gc()
Sys.sleep(10)
rm(dtf)
gc()

【讨论】:

  • 虽然“不可取”,但它看起来很有趣。但是,您的情节缺少坐标轴和比例尺。 ;-)
  • +1 用于内存测量。由于 c(a,b) 删除了所有属性,因此需要更多的工作来处理因子(以及其他具有属性的列)。
  • @Tommy :当然可以。因此我的警告。我没有特别提到它,但我没有时间构建所有控件。用我最喜欢的一句话来表达:“我把它留给读者作为练习”;)
猜你喜欢
  • 1970-01-01
  • 2010-09-24
  • 1970-01-01
  • 2018-12-21
  • 1970-01-01
  • 2011-04-27
  • 1970-01-01
  • 2014-07-24
相关资源
最近更新 更多