2013-10-31 64 views
12

我應該從MS-SQL服務器上下載表格。如何加速rbind?

行數大於600萬。服務器無法一次返回全部數據。

所以,我寫了一個代碼,一次下載10,000行。並且它綁定循環中的行。

假設getData()函數一次返回一個數據幀一次包含10000行。 (僞代碼)

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

問題是隨着時間的推移它會變得越來越慢。

我覺得用這種方式使用rbind不是一個好主意。

任何建議將是非常有益的。先謝謝你。

+3

如果你知道你的最終數據框會有多大,那麼預分配它並按照你的要求填寫值會快很多。儘管使用不同的數據結構可能會有更快的解決方案,有人會發布。 – David

+0

wannagetValue < - rbind(wannagetValue,getData()); 。這可能會加快你的代碼的速度......但主要問題是,爲什麼有這麼多的數據? – Hackerman

+0

謝謝大衛!你救了我.. Robert //我不知道~~我只知道有超過500GB大小的數據。 –

回答

26

這裏是我敢肯定,可能是更好的幾個選項:

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 
+0

+1 - 請添加'do.call(rbind,lapply(1:25,function(...)getData()))' – flodel

+0

好主意,謝謝。 – David

+0

非常感謝您提供詳細信息〜它對我有很大的幫助。 –

4

,因爲它是上面指出的,R存儲所有它在默認情況下RAM的對象,所以用數據的量,你會碰到一些問題。

兩件事情我想補充: 1)一般情況下,如果你不希望使用data.table,您可以使用哈德利的plyr包,這是相當快的rbind.fill功能了。 從不使用rbind上面的方式,在'for'循環中,分別附加每行。它強制R在每次追加一行時創建數據框對象的副本,而且速度很慢。

2)與有R高於大-RAM數據的工作,看看部分大內存和外的內存數據http://cran.r-project.org/web/views/HighPerformanceComputing.html,也許bigmemory包是你所需要的。

+1

整個原始數據將超過500GB,但如果以二進制數據形式存儲,則總數將爲5〜10GB。此外,每個表格包含1〜2GB。我可以單獨處理它。幸運的是,我的電腦有64GB DDR3 ECC內存。我認爲這還不夠。順便說一句,謝謝你的建議。 –

+0

這個問題有點老了,但我今天仍然在搜索解決方案時發現它,所以我想補充一點,Hadley的新的'dplyr'包具有'bind_rows'功能,類似於'rbind.fill'。我對它進行了基準測試,運行速度比我機器上的do.call('rbind',...)快大約1000倍。看到[這個問題](https://stackoverflow.com/questions/44464441/r-is-there-a-good-replacement-for-plyrrbind-fill-in-dplyr)。 – qdread

0

也許你可以做SELECT COUNT(*) FROM table_name WHERE ...然後爲你的數據框預分配空間。

其實,我不認爲每10k行查詢數據庫是個好主意。嘗試通過將數據導出到本地磁盤並從那裏讀取來避免這種情況。它也會提高速度。存儲價格便宜,網絡帶寬和內存不是。