当我坐在这里等待一些R脚本运行时...我想知道...有没有办法在R中并行化rbind?
在我处理大量数据时,我正等待这个电话频繁完成。
do.call("rbind", LIST)
答案 0 :(得分:25)
到目前为止,我还没有找到一种并行方法。但是对于我的数据集(这个是一个包含总共4.5M行的大约1500个数据帧的列表),以下代码段似乎有所帮助:
while(length(lst) > 1) {
idxlst <- seq(from=1, to=length(lst), by=2)
lst <- lapply(idxlst, function(i) {
if(i==length(lst)) { return(lst[[i]]) }
return(rbind(lst[[i]], lst[[i+1]]))
})
}
其中lst是列表。它似乎比使用do.call(rbind, lst)
或甚至do.call(rbind.fill, lst)
(使用plyr包中的rbind.fill)快4倍。在每次迭代中,此代码将数据帧的数量减半。
答案 1 :(得分:18)
因为您说要对data.frame
个对象进行rbind,所以应该使用data.table
包。它有一个名为rbindlist
的功能,可以大幅提升rbind
。我不是100%肯定,但我敢打赌,rbind
的任何使用都会在rbindlist
没有时触发副本。
无论如何,data.table
是data.frame
,所以你不会遗漏任何东西来尝试。
编辑:
library(data.table)
system.time(dt <- rbindlist(pieces))
utilisateur système écoulé
0.12 0.00 0.13
tables()
NAME NROW MB COLS KEY
[1,] dt 1,000 8 X1,X2,X3,X4,X5,X6,X7,X8,...
Total: 8MB
快速闪电......
答案 2 :(得分:17)
我怀疑你可以通过平行化来让它更快地工作:除了你可能必须自己编写它的事实(第一个线程第一和第二个项目,第二个线程第三个和第四个项目等。当它们完成时,结果是“反弹”,就像那样 - 我没有看到非C方式改进这个),它将涉及在线程之间复制大量数据,这是通常情况下,首先是缓慢的事情。
在C中,可以在线程之间共享对象,因此您可以将所有线程写入同一内存中。祝你好运: - )
最后,作为旁白:绑定data.frames只是很慢。如果您事先知道所有data.frames的结构完全相同,并且它不包含纯字符列,那么您可以使用this answer to one of my questions中的技巧。如果你的data.frame包含字符列,我怀疑你最好单独处理这些(do.call(c, lapply(LIST, "[[", "myCharColName"))
),然后用其余的处理技巧,之后你可以重新组合它们。
答案 3 :(得分:7)
这是一个解决方案,它自然地扩展到rbind.fill,merge和其他数据帧列表函数:
但与我的所有答案/问题一样,请确认:)
require(snowfall)
require(rbenchmark)
rbinder <- function(..., cores=NULL){
if(is.null(cores)){
do.call("rbind", ...)
}else{
sequ <- as.integer(seq(1, length(...), length.out=cores+1))
listOLists <- paste(paste("list", seq(cores), sep=""), " = ...[", c(1, sequ[2:cores]+1), ":", sequ[2:(cores+1)], "]", sep="", collapse=", ")
dfs <- eval(parse(text=paste("list(", listOLists, ")")))
suppressMessages(sfInit(parallel=TRUE, cores))
dfs <- sfLapply(dfs, function(x) do.call("rbind", x))
suppressMessages(sfStop())
do.call("rbind", dfs)
}
}
pieces <- lapply(seq(1000), function(.) data.frame(matrix(runif(1000), ncol=1000)))
benchmark(do.call("rbind", pieces), rbinder(pieces), rbinder(pieces, cores=4), replications = 10)
#test replications elapsed relative user.self sys.self user.child sys.child
#With intel i5 3570k
#1 do.call("rbind", pieces) 10 116.70 6.505 115.79 0.10 NA NA
#3 rbinder(pieces, cores = 4) 10 17.94 1.000 1.67 2.12 NA NA
#2 rbinder(pieces) 10 116.03 6.468 115.50 0.05 NA NA
答案 4 :(得分:1)
这正在扩大@Dominik的答案。
我们可以使用并行包中的mclapply来进一步提高速度。另外rbind.fill比rbind做得更好,所以这里是改进的代码。 注意:这只适用于mac / linux。 Windows不支持mclapply。 编辑:如果要查看进度,请取消注释print(i)行,并确保从终端运行,而不是从RStudio运行。从并行过程打印到RStudio,有点混乱RStudio。
library(parallel)
rbind.fill.parallel <- function(list){
while(length(list) > 1) {
idxlst <- seq(from=1, to=length(list), by=2)
list <- mclapply(idxlst, function(i) {
#print(i) #uncomment this if you want to see progress
if(i==length(list)) { return(list[[i]]) }
return(rbind.fill(list[[i]], list[[i+1]]))
})
}
}
答案 5 :(得分:1)
看起来这样的答案已经被很多人很好地回答了,但是如果有人提出来,这是针对非data.table / data.frame-esque对象的并行rbind的一种版本:>
rbind.parallel <- function(list,ncore)
{
library(parallel)
do.call.rbind<-function(x){do.call(rbind,x)}
cl<-makeCluster(ncore)
list.split<-split(list,rep(1:ncore,length(list)+1)[1:length(list)])
list.join<-parLapply(cl,list.split,do.call.rbind)
stopCluster(cl)
list.out<-do.call(rbind,list.join)
return(list.out)
}
这对sf类型的对象有效。例如,如果您使用lapply(.,st_read)
从目录中读取shapefile列表,则rbind.fill及其变体显然无法加入所有功能。