21

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

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

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

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

for(i in 1:600)
{
    tempValue <- getData()
    wannagetValue <- rbind(wannagetValue,tempValue)
    print(i)
}

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

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

任何建议都会非常有帮助。先感谢您。

4

3 回答 3

35

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

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.table'srbindlist()是对基本 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
于 2013-10-31T03:52:24.903 回答
5

正如上面所指出的,R 默认情况下将其所有对象存储在 RAM 中,因此使用这么多数据,您将遇到一些问题。

我想补充两点:1)一般情况下,如果你不想使用data.table,你可以使用rbind.fillHadley的plyr包中的函数,速度也很快。永远不要在“for”循环中使用rbind上面的方式,分别附加每一行。每次追加一行时,它都会强制 R 制作数据框对象的副本,这很慢。

2) 要使用 R 处理大于 RAM 的数据,请查看 http://cran.r-project.org/web/views/HighPerformanceComputing.html 上的大内存和内存不足数据部分,也许这个包是你需要的。bigmemory

于 2013-10-31T05:53:16.937 回答
0

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

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

于 2017-07-04T06:29:10.770 回答