Rbind可以在R中并行化吗?

时间:2011-08-29 00:17:08

标签: r

当我坐在这里等待一些R脚本运行时...我想知道...有没有办法在R中并行化rbind?

在我处理大量数据时,我正等待这个电话频繁完成。

do.call("rbind", LIST)

6 个答案:

答案 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.tabledata.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及其变体显然无法加入所有功能。