如何在不耗尽内存的情况下合并两个巨大的数据框(data-frame)?

22

我有两个数据框df1和df2,每个数据框大约有1000万行和4列。我使用RODBC/sqlQuery在R中读入它们没有问题,但是当我尝试合并它们时,出现了最令人头疼的R错误信息:cannot allocate memory。肯定有更有效的方法来更高效地进行rbind - 有人想分享一下他们喜欢的技巧吗?例如,我在sqldf文档中发现了这个示例:

# rbind
a7r <- rbind(a5r, a6r)
a7s <- sqldf("select * from a5s union all select * from a6s")

这是最好/推荐的方法吗?

更新 我使用了sqldf调用中JD Long在这个问题中建议的关键参数dbname = tempfile(),使其正常运行。


好主意--我该怎么做--你是指使用类似 memory.limit(size = 4000) 这样的东西吗? - Prasad Chalasani
不过这只能在 Windows 上运行。请见下面我的回答。 - aL3xa
1
你可以使用 write.csv 将它们保存起来,然后使用 shell 组合数据。请参阅 https://dev59.com/jnE95IYBdhLWcg3wDpmg - James
1
为像rbind这样的函数创建基于sqldf的版本听起来对某些人来说是一个有趣的作业挑战。 - Richie Cotton
4个回答

29

你可以让SQLite在将它们发送到R之前读取并组合它们,而不是一开始就将它们读入R再进行组合。这样,文件就不会单独加载到R中。

# create two sample files
DF1 <- data.frame(A = 1:2, B = 2:3)
write.table(DF1, "data1.dat", sep = ",", quote = FALSE)
rm(DF1)

DF2 <- data.frame(A = 10:11, B = 12:13)
write.table(DF2, "data2.dat", sep = ",", quote = FALSE)
rm(DF2)

# now we do the real work
library(sqldf)

data1 <- file("data1.dat")
data2 <- file("data2.dat")

sqldf(c("select * from data1", 
 "insert into data1 select * from data2", 
 "select * from data1"), 
 dbname = tempfile())
这会产生以下结果:
>  sqldf(c("select * from data1", "insert into data1 select * from data2", "select * from data1"), dbname = tempfile())
   A  B
1  1  2
2  2  3
3 10 12
4 11 13

如果行顺序不重要,这个更短的版本也可以工作:

sqldf("select * from data1 union select * from data2", dbname = tempfile())

请查看 sqldf 主页 http://sqldf.googlecode.com?sqldf 获取更多信息。 特别注意文件格式参数,因为它们与 read.table 相似但不完全相同。 在这里我们使用了默认设置,所以这不是一个问题。


整洁的方法... SQL 绝对有能力处理那么大的东西! - aL3xa
非常有用,谢谢@Gabor... 我的数据实际上在一个SQL数据库中,使用一个查询读取整个数据会占用我的内存,这就是为什么我必须首先使用RODBC/sqlQuery将每一半读入R中(不要问我为什么它无法读取整个数据但读取每一半时却没有问题)。但是如果我的原始数据是两个平面文件,你的方法是最好的读取方式,并避免将两个部分存储在R内存中。 - Prasad Chalasani

22

注意使用 data.table R 包可以高效地处理包含数百万条记录的对象。

该包的 1.8.2 版本提供了 rbindlist 函数,通过它你可以非常高效地实现你想要的操作。因此,不需要使用 rbind(a5r, a6r) ,而是可以:

library(data.table)
rbindlist(list(a5r, a6r))

2
你能在不先将数据集加载到内存中的情况下完成这个吗? - statsNoob

1
尝试创建所需大小的data.frame,因此使用下标导入数据。
dtf <- as.data.frame(matrix(NA, 10, 10))
dtf1 <- as.data.frame(matrix(1:50, 5, 10, byrow=TRUE))
dtf2 <- as.data.frame(matrix(51:100, 5, 10, byrow=TRUE))
dtf[1:5, ] <- dtf1
dtf[6:10, ] <- dtf2

我猜测rbind在没有预先分配其维度的情况下增加对象...我不确定,这只是一个猜测。今晚我会仔细阅读《R地狱》或《使用R进行数据操作》。也许merge可以解决问题...

编辑

而且你应该记住,(也许)你的系统和/或R无法处理那么大的东西。尝试RevolutionR,也许你能节省一些时间/资源。


有趣的建议,谢谢。我会尝试一下。(但我不想超出免费的R,所以Revo对我来说不是一个选择) - Prasad Chalasani
1
有趣的建议,但它使用的内存比rbind多得多。 - Joris Meys

1

为了让这个关于合并大文件的主题更加完整,请尝试使用Shell命令来将文件组合在一起。在Windows中,可以使用"COPY"命令和"/B"标志。例如:

system(command =
         paste0(
           c("cmd.exe /c COPY /Y"
             , '"file_1.csv" /B'
             , '+ "file_2.csv" /B'
             , '"resulting_file.csv" /B'
           ), collapse = " "
         )
)#system

需要文件没有头部,并且有相同的分隔符等等。 命令行的速度和多功能性有时是一个巨大的优势,因此在制定数据流时不要忘记CLI命令。

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