2011-05-12 69 views
8

我有一個dataframes的列表,我確定它們全部包含至少一行(實際上,一些只包含一行,其他包含給定數量的行),以及他們都有相同的列(名稱和類型)。萬一它很重要,我也肯定在行中的任何地方都沒有NA。rbind.data.frame的性能

的情況下,可以模擬這樣的:

#create one row 
onerowdfr<-do.call(data.frame, c(list(), rnorm(100) , lapply(sample(letters[1:2], 100, replace=TRUE), function(x){factor(x, levels=letters[1:2])}))) 
colnames(onerowdfr)<-c(paste("cnt", 1:100, sep=""), paste("cat", 1:100, sep="")) 
#reuse it in a list 
someParts<-lapply(rbinom(200, 1, 14/200)*6+1, function(reps){onerowdfr[rep(1, reps),]}) 

我設置的參數(的隨機化),讓他們接近我的真實情況。

現在,我想統一所有這些數據幀在一個數據幀中。我想用rbind會做的伎倆,就像這樣:

system.time(
result<-do.call(rbind, someParts) 
) 

現在,我的系統上(這是不是特別慢),與上面的設置,這需要的是system.time的輸出:

user system elapsed 
    5.61 0.00 5.62 

接近254秒(在我的情況下)行數爲200的變量?當然,必須有一種方法來改善這裏的表現?在我的代碼中,我必須經常做類似的事情(它來自多重插補),所以我需要儘可能快地做到這一點。

+0

在我的工作中,我使用來自Dominik的技術將數據框列表組合在一起http://stackoverflow.com/questions/7224938/can-i-rbind-be-parallelized-in-r/8071176#8071176這是相對來說要快得多,因爲它比較大,當我用字符而不是因素讀取原始列表數據時,發現性能更好。使用rbind在比賽上花了很多時間;我猜測這是檢查要添加的因子水平。 – ARobertson 2012-11-29 21:05:14

回答

13

你可以用數字變量建立矩陣,並在最後轉換爲一個因子嗎?數字矩陣上的rbind速度要快很多。

在我的系統,利用數據幀:

> system.time(result<-do.call(rbind, someParts)) 
    user system elapsed 
    2.628 0.000 2.636 

建設有所有的數字矩陣,而不是名單:

onerowdfr2 <- matrix(as.numeric(onerowdfr), nrow=1) 
someParts2<-lapply(rbinom(200, 1, 14/200)*6+1, 
        function(reps){onerowdfr2[rep(1, reps),]}) 

結果快了很多rbind

> system.time(result2<-do.call(rbind, someParts2)) 
    user system elapsed 
    0.001 0.000 0.001 

編輯:這是另一種可能性;它只是依次結合每一列。

> system.time({ 
+ n <- 1:ncol(someParts[[1]]) 
+ names(n) <- names(someParts[[1]]) 
+ result <- as.data.frame(lapply(n, function(i) 
+       unlist(lapply(someParts, `[[`, i)))) 
+ }) 
    user system elapsed 
    0.810 0.000 0.813 

儘管如此,仍然沒有像使用矩陣那麼快。

編輯2:

如果你只有數字和因素,它不是很難的一切轉換爲數字,rbind他們,並轉換必要的列回因素。這假定所有因素具有完全相同的水平。從一個整數轉換爲一個因子也比從一個數字更快,所以我首先強制整數。

someParts2 <- lapply(someParts, function(x) 
        matrix(unlist(x), ncol=ncol(x))) 
result<-as.data.frame(do.call(rbind, someParts2)) 
a <- someParts[[1]] 
f <- which(sapply(a, class)=="factor") 
for(i in f) { 
    lev <- levels(a[[i]]) 
    result[[i]] <- factor(as.integer(result[[i]]), levels=seq_along(lev), labels=lev) 
} 

我的系統上的時間是:

user system elapsed 
    0.090 0.00 0.091 
+1

@Aaron:數據是一個模擬,OP的問題始於數據框。 – 2011-05-12 16:10:58

+0

@Joris:它很接近;你可以提取每個類型到自己的矩陣列表中,'rbind'每個類型列表,然後創建一個data.frame。 – 2011-05-12 16:12:02

+0

@Joris:沒錯,這並不能回答海報的具體問題(我如何加快'rbind.data.frame'的速度?)。但是,也許知道矩陣矩陣更快的知識,他可以重寫他的代碼以避免使用數據幀,或者稍後轉換爲數據幀。我很想看到實際加速rbind.data.frame的方法。 – Aaron 2011-05-12 16:46:51

5

不是一個巨大的推動作用,但是從plyr包交換rbindrbind.fill敲約10%的運行時間(與樣品數據集上我的機器)。

3

這是〜25%更快,但必須有更好的方法...

system.time({ 
    N <- do.call(sum, lapply(someParts, nrow)) 
    SP <- as.data.frame(lapply(someParts[[1]], function(x) rep(x,N))) 
    k <- 0 
    for(i in 1:length(someParts)) { 
    j <- k+1 
    k <- k + nrow(someParts[[i]]) 
    SP[j:k,] <- someParts[[i]] 
    } 
}) 
+0

用匯編代碼重寫? – 2011-05-12 17:23:49

+0

構建這個,我試着用一個'lapply'從每個元素中抓取適當的列來逐列填充數據框;它似乎仍然更快。請參閱編輯我的答案。 – Aaron 2011-05-12 17:28:58

4

如果你真的想操縱你的data.frame速度比較快,我會建議使用包data.table和功能rbindlist()。我沒有執行廣泛的測試,但對於我的數據集(3000個數據幀,每行1000行x 40列)rbindlist()只需要20秒。

1

確保您將數據框綁定到數據框。將綁定列表綁定到數據框時,會導致巨大的性能下降。