如何加快rbind?

Posted

技术标签:

【中文标题】如何加快rbind?【英文标题】:How to speed up rbind? 【发布时间】:2013-10-31 02:56:20 【问题描述】:

我应该从 MS-SQL 服务器下载一个表。

行数大于 600 万。服务器无法一次返回全部数据。

所以,我编写了一个一次下载 10,000 行的代码。并且,它在循环中绑定行。

假设getData() 函数返回的数据帧一次包含 10000 行。 (伪代码)

for(i in 1:600)

    tempValue <- getData()
    wannagetValue <- rbind(wannagetValue,tempValue)
    print(i)

问题是随着时间的推移它会变慢。

我认为像那样使用 rbind 不是一个好主意。

任何建议都会很有帮助。提前谢谢你。

【问题讨论】:

如果您知道最终的数据框有多大,那么预先分配它并随时填写值会快得多。尽管可能会有更快的解决方案使用不同的数据结构来发布。 wannagetValue 谢谢大卫!你救了我..罗伯特//我不知道~~我只知道有超过 500GB 的数据。 您确实意识到 R 将所有内容都存储在内存中,对吧?如果您有 500GB 的数据,则至少需要这么多内存,理想情况下是两倍。 @HongOoi // 整个原始数据将超过 500GB,但如果存储为二进制数据,总共将是 5~10GB。此外,每个表包含 1~2GB。我可以分开处理。幸运的是,我的电脑有 64GB DDR3 ECC RAM。我认为这还不够。顺便说一句,谢谢你的建议。 【参考方案1】:

以下是一些我确信可能会更好的选项:

library(data.table)
library(microbenchmark)

#function to generate your data
getData <- function()
  data.frame(x=rnorm(10000),y=rnorm(10000),z=rnorm(10000))


#using data table's rbindlist each iteration
fDT1 <- function(n)
  dat <- getData()
  for(i in 1:n)
    dat <- rbindlist(list(dat,getData()))
  
  return(data.frame(dat))


#using data table's rbindlist all at once
fDT2 <- function(n)
  return(data.frame(rbindlist(lapply(1:n,function(x) getData()))))


#pre-allocating a data frame
fPre <- function(n)
  dat <- data.frame(x=rep(0,n*10000),y=rep(0,n*10000),z=rep(0,n*10000))
  j <- 1
  for(i in 1:n)
    dat[j:(j+10000-1),] <- getData()
    j <- j + 10000
  
  return(dat)


#standard do.call rbind
f2 <- function(n)
  return(do.call(rbind,lapply(1:n,function(x) getData())))


#current approach
f <- function(n)
  dat <- getData()
  for(i in 1:n)
    dat <- rbind(dat,getData())
  
  return(dat)

正如您所见,使用 data.tablerbindlist() 是对基本 R 的 rbind() 的一大改进,并且一次追加行而不是交互有很大的好处,但是如果存在内存问题。您可能还会注意到,随着数据量的增加,速度的提高远非线性增长。

 > microbenchmark(fDT2(5),fDT1(5),fPre(5),f2(5),f(5),
+                fDT2(25),fDT1(25),fPre(25),f2(25),f(25),
+                fDT2(75),fDT1(75),fPre(75),f2(75),f(75),
+                times=10)
Unit: milliseconds
     expr        min         lq     median         uq         max neval
  fDT2(5)   18.31207   18.63969   24.09943   25.45590    72.01725    10
  fDT1(5)   27.65459   29.25147   36.34158   77.79446    88.82556    10
  fPre(5)   34.96257   39.39723   41.24445   43.30319    68.75897    10
    f2(5)   30.85883   33.00292   36.29100   43.53619    93.15869    10
     f(5)   87.40869   97.97500  134.50600  138.65354   147.67676    10
 fDT2(25)   89.42274   99.39819  103.90944  146.44160   156.01653    10
 fDT1(25)  224.65745  229.78129  261.52388  280.85499   300.93488    10
 fPre(25)  371.12569  412.79876  431.80571  485.37727  1046.96923    10
   f2(25)  221.03669  252.08998  265.17357  271.82414   281.47096    10
    f(25) 1446.32145 1481.01998 1491.59203 1634.99936  1849.00590    10
 fDT2(75)  326.66743  334.15669  367.83848  467.85480   520.27142    10
 fDT1(75) 1749.83842 1882.27091 2066.95241 2278.55589  2419.07205    10
 fPre(75) 3701.16220 3968.64643 4162.70585 4234.39716  4356.09462    10
   f2(75) 1174.47546 1183.98860 1314.64585 1421.09483  1537.42903    10
    f(75) 9139.36935 9349.24412 9510.90888 9977.24621 10861.51206    10

【讨论】:

+1 - 请添加do.call(rbind, lapply(1:25,function(...) getData())) 非常感谢您提供的详细信息。~它对我很有帮助。【参考方案2】:

正如上面所指出的,R 默认情况下将其所有对象都存储在 RAM 中,所以对于这么多的数据,你会遇到一些问题。

我想补充两点: 1)一般如果不想使用data.table,可以使用Hadley的plyr包中的rbind.fill函数,速度也挺快的。 永远不要像上面那样使用rbind,在“for”循环中分别附加每一行。每次追加一行时,它都会强制 R 复制数据框对象,这很慢。

2) 要使用 R 处理大于 RAM 的数据,请查看 http://cran.r-project.org/web/views/HighPerformanceComputing.htmlLarge memory and out-of-memory data 部分,可能是 bigmemory 包是你需要的。

【讨论】:

整个原始数据将超过500GB,但如果存储为二进制数据,总共将是5~10GB。此外,每个表包含 1~2GB。我可以分开处理。幸运的是,我的电脑有 64GB DDR3 ECC RAM。我认为这还不够。顺便说一句,谢谢你的建议。 这个问题有点老了,但我今天在寻找解决方案时仍然发现它,所以我想补充一下,Hadley 较新的dplyr 包具有bind_rows 的功能,类似于@ 987654329@。我对它进行了基准测试,它在我的机器上的运行速度比 do.call('rbind', ...) 快了大约 1000 倍。见this question。【参考方案3】:

也许您可以执行SELECT COUNT(*) FROM table_name WHERE ...,然后为您的数据框预分配空间。

实际上,我认为每 10k 行查询数据库并不是一个好主意。尝试通过将数据导出到本地磁盘并从那里读取来避免这种情况。它也会提高速度。存储很便宜,但网络带宽和内存却不便宜。

【讨论】:

以上是关于如何加快rbind?的主要内容,如果未能解决你的问题,请参考以下文章

R语言-merge和rbind

R语言数据纵向合并rbind函数实战(以及rbind.fill函数合并两个数据列不同的dataframe)

R语言数据合并-cbind(),rbind

如何让unity动画速度加快

用 rbind 附加数据帧的神秘问题

do.call(rbind, list) 用于奇数列