【问题标题】:Can rbind be parallelized in R?rbind 可以在 R 中并行化吗?
【发布时间】:2011-11-05 16:46:32
【问题描述】:

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

在处理大量数据时,我经常坐着等待此调用完成。

do.call("rbind", LIST)

【问题讨论】:

  • 来自 plyr 包的 rbind.fill 宣传它的运行速度比基本 rbind 快得多。也许你会在那里看到一些性能颠簸。如果您添加一些样本代表性数据,人们还可以提供其他解决方案以及测试时间的基准。
  • LIST(矩阵、data.frame 等)中有哪些类型的对象?
  • LIST 中有哪些类型的对象?数据帧。

标签: r


【解决方案1】:

到目前为止,我还没有找到并行执行此操作的方法。然而,对于我的数据集(这是一个包含大约 1500 个数据帧的列表,总共 450 万行),以下 sn-p 似乎有帮助:

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 倍。在每次迭代中,此代码将数据帧的数量减半。

【讨论】:

  • 该死,它确实跑得更快!
【解决方案2】:

因为你说你想 rbind data.frame 对象你应该使用 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

闪电般的速度......

【讨论】:

  • 哈必须是计算史上最快的加速!就我而言,从几小时到不到一秒。
【解决方案3】:

我怀疑你是否可以通过并行化让它更快地工作:除了你可能必须自己编写它(线程一个第一个 rbind 项目 1 和 2,而线程两个 rbind 项目 3 和 4 等等。 ,当它们完成时,结果是“反弹”,类似的东西 - 我看不到改进这一点的非 C 方法),这将涉及在线程之间复制大量数据,即通常是一开始就很慢的事情。

在 C 中,您可以在线程之间共享对象,因此您可以让所有线程写入同一内​​存中。祝你好运:-)

最后,顺便说一句:rbinding data.frames 很慢。如果您事先知道所有 data.frames 的结构完全相同,并且不包含纯字符列,您可能可以使用this answer to one of my questions 中的技巧。如果您的 data.frame 包含字符列,我怀疑您最好分别处理这些 (do.call(c, lapply(LIST, "[[", "myCharColName"))),然后与其余部分一起执行该技巧,之后您可以重新组合它们。

【讨论】:

    【解决方案4】:

    这里有一个解决方案,它自然会扩展到rbind.fill、merge等dataframe列表函数:

    但就像我所有的答案/问题一样验证:)

    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
    

    【讨论】:

    • @Arun,这是我已经修复的错误,但似乎没有保存。您也可以轻松地将其扩展到rbindlist 或其他基于列表的技术。我只是使用 Rs 标准 rbind 来简单地证明概念。
    • @Arun,我不是这个意思。我从未争辩说我的并行化解决方案将是最快的解决方案。我刚刚回答了这个问题:“rbind 可以并行化吗?”使用“是”以及您可以使用具有不同功能的这种方法的概念。使用rbindlist 解决方案,很高兴看到它是否通过并行化得到改进。可能不是因为读取库/函数“到核心”需要更多时间,而不是仅仅绑定几 10 千帧。但是 100k、1M、10M 呢?我现在在我的避暑别墅里,带着一台旧的 macbook。所以我要到星期一才能正确尝试。
    【解决方案5】:

    这是对@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]]))
        })
      }
    }
    

    【讨论】:

      【解决方案6】:

      看起来很多人已经很好地回答了这个问题,但是如果有人提出这个问题,这里有一个用于非 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 及其变体无法加入所有功能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-22
        相关资源
        最近更新 更多