2013-10-03 26 views
2

關於讀取多個文件和內存管理有很多問題。我正在尋找能夠解決這兩個問題的信息。讀取多個文件時的內存管理

我經常需要將多個部分的數據作爲單獨的文件讀取,然後將它們組合成一個數據集,然後對其進行處理。我一直在使用類似下面的東西,直到現在 - rbinideddataset <- do.call("rbind", lapply(list.files(), read.csv, header = TRUE))

我很關心那個可以在每種方法中觀察到的凹凸。這可能是rbindeddataset和尚未結束的數據集一起存在於記憶中的實例,但我不知道足夠確定。有人能證實這一點嗎?

有什麼辦法可以將預先分配的原則擴展到這樣的任務嗎?或者任何人都知道的其他訣竅可能有助於避免碰撞? lapply的結果我也試過rbindlist,並沒有顯示凹凸。這是否意味着rbindlist足夠聰明來處理這個問題?

data.table和Base R解決方案優於某些軟件包的產品。

EDIT 07-OCT-2013基於與@Dwin討論和@mrip

> library(data.table) 
> filenames <- list.files() 
> 
> #APPROACH 1 ################################# 
> starttime <- proc.time() 
> test <- do.call("rbind", lapply(filenames, read.csv, header = TRUE)) 
> proc.time() - starttime 
    user system elapsed 
    44.60 1.11 45.98 
> 
> rm(test) 
> rm(starttime) 
> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 350556 18.8  741108 39.6 715234 38.2 
Vcells 1943837 14.9 153442940 1170.7 192055310 1465.3 
> 
> #APPROACH 2 ################################# 
> starttime <- proc.time() 
> test <- lapply(filenames, read.csv, header = TRUE) 
> test2 <- do.call("rbind", test) 
> proc.time() - starttime 
    user system elapsed 
    47.09 1.26 50.70 
> 
> rm(test) 
> rm(test2) 
> rm(starttime) 
> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 350559 18.8  741108 39.6 715234 38.2 
Vcells 1943849 14.9 157022756 1198.0 192055310 1465.3 
> 
> 
> #APPROACH 3 ################################# 
> starttime <- proc.time() 
> test <- lapply(filenames, read.csv, header = TRUE) 
> test <- do.call("rbind", test) 
> proc.time() - starttime 
    user system elapsed 
    48.61 1.93 51.16 
> rm(test) 
> rm(starttime) 
> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 350562 18.8  741108 39.6 715234 38.2 
Vcells 1943861 14.9 152965559 1167.1 192055310 1465.3 
> 
> 
> #APPROACH 4 ################################# 
> starttime <- proc.time() 
> test <- do.call("rbind", lapply(filenames, fread)) 

> proc.time() - starttime 
    user system elapsed 
    12.87 0.09 12.95 
> rm(test) 
> rm(starttime) 
> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 351067 18.8  741108 39.6 715234 38.2 
Vcells 1964791 15.0 122372447 933.7 192055310 1465.3 
> 
> 
> #APPROACH 5 ################################# 
> starttime <- proc.time() 
> test <- do.call("rbind", lapply(filenames, read.csv, header = TRUE)) 
> proc.time() - starttime 
    user system elapsed 
    51.12 1.62 54.16 
> rm(test) 
> rm(starttime) 
> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 350568 18.8  741108 39.6 715234 38.2 
Vcells 1943885 14.9 160270439 1222.8 192055310 1465.3 
> 
> 
> #APPROACH 6 ################################# 
> starttime <- proc.time() 
> test <- rbindlist(lapply(filenames, fread)) 

> proc.time() - starttime 
    user system elapsed 
    13.62 0.06 14.60 
> rm(test) 
> rm(starttime) 
> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 351078 18.8  741108 39.6 715234 38.2 
Vcells 1956397 15.0 128216351 978.3 192055310 1465.3 
> 
> 
> #APPROACH 7 ################################# 
> starttime <- proc.time() 
> test <- rbindlist(lapply(filenames, read.csv, header = TRUE)) 
> proc.time() - starttime 
    user system elapsed 
    48.44 0.83 51.70 
> rm(test) 
> rm(starttime) 
> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 350620 18.8  741108 39.6 715234 38.2 
Vcells 1944204 14.9 102573080 782.6 192055310 1465.3 

正如所料,節省的時間是最高用fread。然而,方法4,6和7顯示了最小的內存開銷,我不是很確定爲什麼。

enter image description here

回答

4

看起來像rbindlist預分配內存並構建新的數據幀在一次,而do.call(rbind)將一次添加一個數據幀,每次都複製它。結果是rbind方法的運行時間爲O(n^2),而rbindlist則以線性時間運行。此外,rbindlist應該避免內存中的凹凸,因爲它不必在每次迭代或迭代期間分配新的數據幀。

一些實驗數據:

x<-data.frame(matrix(1:10000,1000,10)) 
ls<-list() 
for(i in 1:10000) 
    ls[[i]]<-x+i 

rbindtime<-function(i){ 
    gc() 
    system.time(do.call(rbind,ls[1:i]))[3] 
} 
rbindlisttime<-function(i){ 
    gc() 
    system.time(data.frame(rbindlist(ls[1:i])))[3] 
} 

ii<-unique(floor(10*1.5^(1:15))) 
## [1] 15 22 33 50 75 113 170 256 384 576 864 1297 1946 2919 4378 

times<-Vectorize(rbindtime)(ii) 
##elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed 
## 0.009 0.014 0.026 0.049 0.111 0.209 0.350 0.638 1.378 2.645 
##elapsed elapsed elapsed elapsed elapsed 
## 5.956 17.940 30.446 68.033 164.549 

timeslist<-Vectorize(rbindlisttime)(ii) 
##elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed elapsed 
## 0.001 0.001 0.001 0.002 0.002 0.003 0.004 0.008 0.009 0.015 
##elapsed elapsed elapsed elapsed elapsed 
## 0.023 0.031 0.046 0.099 0.249 

不僅是rbindlist快得多,特別是對於長期投入,但運行時間僅線性增加,而do.call(rbind)長約平方。我們可以通過將對數線性模型擬合到每一組時間來證實這一點。

> lm(log(times) ~ log(ii)) 

Call: 
lm(formula = log(times) ~ log(ii)) 

Coefficients: 
(Intercept)  log(ii) 
     -9.73   1.73 

> lm(log(timeslist) ~ log(ii)) 

Call: 
lm(formula = log(timeslist) ~ log(ii)) 

Coefficients: 
(Intercept)  log(ii) 
    -10.0550  0.9455 

所以,實驗中,do.call(rbind)運行時間的增長與n^1.73rbindlist大約是線性的。

+1

+5。做得太好了。這正是我想要在我的問題中提出的建議,也是@迪文的回答,它應該是rbindlist,應該在這裏有所作爲,而不是fread。 – TheComeOnMan

+1

很棒的迴應! @Codoremifa,順便說一句,你可以upvote以及給一個複選標記;) –

+1

我懷疑你會發現'fread'和'rbindlist'是協同。它也被認爲是不好的做法,用與標準函數相同的標記來命名對象,所以'ls'應該是禁止的。 –

2

試試這個:

require(data.table) 
system.time({ 
test3 <- do.call("rbind", lapply(filenames, fread, header = TRUE)) 
      }) 

你提到的預分配。 fread確實有'nrows'參數,但在事先知道行數的情況下(因爲它自動爲您自動預先計數行數,這非常快),它不會加速其操作。

+0

在閱讀文件時'fread'比'read.table' /'read.csv'更快,但我假設它不能解決內存碰撞的問題,是嗎? – TheComeOnMan

+0

您應該檢查。我認爲它也會修復一些困擾R操作的修改拷貝問題,因爲'data.table'通過就地修改方法工作。 –

+1

or better yet'rbindlist(lapply(filenames,fread))'(並且通常不需要將'header'指定爲「TRUE」或「FALSE」) – eddi