使用write.csv时出现内存错误

4

我有一个有9列的数据框(x),它有数百万行。我成功地将它读入R中,对其进行了一些修改,并且代码可以正常执行。然而,当我尝试使用“write”函数将其写入一个.csv文件时,出现了问题。

write.csv(x,file=argv[2],quote=F,row.names=F)

I get an error which says

Error: cannot allocate vector of size 1.2Gb

这没有意义,因为数据已经在内存中,计算也完成了,我只想将其写入磁盘。同时,在我监测内存时,虚拟内存大小在此写入阶段几乎增加了一倍。编写一个自定义的C函数来写出这个数据框是否有帮助?任何建议/帮助/指针都将不胜感激。
附:我在一个内存大约为24G的64位Ubuntu机器上运行所有这些操作。总体空间可能不是问题。数据大小约为10G。

2
最简单的方法是使用append = TRUE将其分成小块写入文件。另外,您计算机上安装的总RAM容量可能会误导您是否会遇到内存问题,因为R经常需要特定大小的连续内存块。即使有24GB的内存,有时也可能难以找到10GB连续内存块。 - joran
@joran所说的。你可以在此之前立即尝试gc(),但它不太可能有太大帮助。 - Ari B. Friedman
1
你可以尝试将对象保存为 .RData 图像,并在新会话中加载它。由于某种原因,它比计算后直接使用对象消耗更少的内存。当我遇到内存问题时,有时我会使用这个技巧。 - sus_mlm
1个回答

9

您需要理解的是,R函数通常会复制参数(如果它们被修改),因为R采用的函数式编程范例规定函数不会更改作为参数传递的对象;因此,在执行函数时需要进行更改时,R会复制它们。

如果您使用内存跟踪支持构建R,则可以在任何出现问题的操作中看到这种复制。使用airquality示例数据集,跟踪内存使用情况,我看到

> head(airquality)
  Ozone Solar.R Wind Temp Month Day
1    41     190  7.4   67     5   1
2    36     118  8.0   72     5   2
3    12     149 12.6   74     5   3
4    18     313 11.5   62     5   4
5    NA      NA 14.3   56     5   5
6    28      NA 14.9   66     5   6
> tracemem(airquality)
[1] "<0x12b4f78>"
> write.csv(airquality, "airquality.csv")
tracemem[0x12b4f78 -> 0x1aac0d8]: as.list.data.frame as.list lapply unlist which write.table eval eval eval.parent write.csv 
tracemem[0x12b4f78 -> 0x1aabf20]: as.list.data.frame as.list lapply sapply write.table eval eval eval.parent write.csv 
tracemem[0x12b4f78 -> 0xf8ae08]: as.list.data.frame as.list lapply write.table eval eval eval.parent write.csv 
tracemem[0x12b4f78 -> 0xf8aca8]: write.table eval eval eval.parent write.csv 
tracemem[0xf8aca8 -> 0xca7fe0]: [<-.data.frame [<- write.table eval eval eval.parent write.csv 
tracemem[0xca7fe0 -> 0xcaac50]: [<-.data.frame [<- write.table eval eval eval.parent write.csv

这表明在R准备写入文件时会制作6份数据副本。

显然,这将耗尽您可用的24GB RAM;错误消息表示R需要另外1.2GB RAM才能完成操作。

最简单的解决方案是分块写入文件。使用append = FALSE将第一组数据行写出,然后对于后续的write.csv()调用,使用append = TRUE写出其余的数据块。您可能需要尝试不同的块大小以避免超过可用内存。


3
在第一个代码块之后,你可能还想设置 col.names = FALSE,这样你的输出文件中就不会出现列名了。 - Chase
1
好的,看起来像@joran和你提到的那样,唯一的方法就是将文件分成较小的块,并以追加模式一个接一个地写出它们。 - broccoli
@broccoli 我提到这是最简单的方法。如果你经常处理如此大的数据,你可能会从探索 ffbigmemory 包中受益,它们旨在使处理非常大的数据更容易。我对它们不是很熟悉,但它们可能有一些专门用于将大对象写入文件的函数。 - joran
@broccoli,将I/O分块处理成可管理的大小在数据库操作中非常常见,上下文处理程序(块大小越小越好,并具有回滚功能)。如果你将write.csv(chunksize=...)泛化,请将你的代码贡献给社区-许多人会用到它。选择一个小的块大小(<1Gb):即使你的计算机总共有24Gb内存,突然占用大部分或全部内存不利于其他进程的合作,并且几乎肯定不能加快任务完成速度。 - smci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接