2010-12-22 56 views
42

很多時候我想轉換一個列表,其中每個索引具有與數據框相同的元素類型。例如,我可能有一個列表:什麼是最有效的方式將列表投入數據框?

> my.list 
[[1]] 
[[1]]$global_stdev_ppb 
[1] 24267673 

[[1]]$range 
[1] 0.03114799 

[[1]]$tok 
[1] "hello" 

[[1]]$global_freq_ppb 
[1] 211592.6 


[[2]] 
[[2]]$global_stdev_ppb 
[1] 11561448 

[[2]]$range 
[1] 0.08870838 

[[2]]$tok 
[1] "world" 

[[2]]$global_freq_ppb 
[1] 1002043 

我想將此列表轉換爲數據框,其中每個索引元素是一列。自然(我)的事情去是爲使用do.call:足夠

> my.matrix<-do.call("rbind", my.list) 
> my.matrix 
    global_stdev_ppb range  tok  global_freq_ppb 
[1,] 24267673   0.03114799 "hello" 211592.6  
[2,] 11561448   0.08870838 "world" 1002043 

簡單,但是當我試圖施放此矩陣作爲一個數據幀,列保持列表元素,而不是載體:

> my.df<-as.data.frame(my.matrix, stringsAsFactors=FALSE) 
> my.df[,1] 
[[1]] 
[1] 24267673 

[[2]] 
[1] 11561448 

目前,得到數據幀投妥善我使用unlistas.vector遍歷每個列,然後重鑄數據幀,例如:

new.list<-lapply(1:ncol(my.matrix), function(x) as.vector(unlist(my.matrix[,x]))) 
my.df<-as.data.frame(do.call(cbind, new.list), stringsAsFactors=FALSE) 

但是,這看起來效率很低。有沒有更好的方法來做到這一點?

+1

看`?data.table :: rbindlist` – marbel 2016-12-22 17:13:20

+0

2017年,你應該使用`your_list%>%減少(bind_rows)``從purrr` – Zafar 2017-03-06 21:32:29

回答

47

我想你想:

> do.call(rbind, lapply(my.list, data.frame, stringsAsFactors=FALSE)) 
    global_stdev_ppb  range tok global_freq_ppb 
1   24267673 0.03114799 hello  211592.6 
2   11561448 0.08870838 world  1002043.0 
> str(do.call(rbind, lapply(my.list, data.frame, stringsAsFactors=FALSE))) 
'data.frame': 2 obs. of 4 variables: 
$ global_stdev_ppb: num 24267673 11561448 
$ range   : num 0.0311 0.0887 
$ tok    : chr "hello" "world" 
$ global_freq_ppb : num 211593 1002043 
+10

`plyr :: rbind.fill`往往是一點點比`rbind.fill`快,整個操作等同於`plyr :: ldply(my.list,data.frame)` – hadley 2010-12-23 00:54:02

17

我不能告訴你這是在內存或速度的「最有效」,但它是相當有效的術語編碼:

my.df <- do.call("rbind", lapply(my.list, data.frame)) 
與data.frame()

的lapply()步驟變爲每個列表項到單個行數據幀然後用作與rbind()很好

31

另一種選擇是:

data.frame(t(sapply(mylist, `[`))) 

但這簡單操作的結果列表的數據幀:

> str(data.frame(t(sapply(mylist, `[`)))) 
'data.frame': 2 obs. of 3 variables: 
$ a:List of 2 
    ..$ : num 1 
    ..$ : num 2 
$ b:List of 2 
    ..$ : num 2 
    ..$ : num 3 
$ c:List of 2 
    ..$ : chr "a" 
    ..$ : chr "b" 

對此的替換方案,沿着相同的路線,但現在的結果相同的其他解決方案,是:

data.frame(lapply(data.frame(t(sapply(mylist, `[`))), unlist)) 

[編輯:包括@Martin Morgan的兩個解決方案的時序,它們比另一個解決方案具有優勢,可以返回矢量數據幀。]一些非常有代表性的時序簡單的問題:

mylist <- list(list(a = 1, b = 2, c = "a"), list(a = 2, b = 3, c = "b")) 

> ## @Joshua Ulrich's solution: 
> system.time(replicate(1000, do.call(rbind, lapply(mylist, data.frame, 
+          stringsAsFactors=FALSE)))) 
    user system elapsed 
    1.740 0.001 1.750 

> ## @JD Long's solution: 
> system.time(replicate(1000, do.call(rbind, lapply(mylist, data.frame)))) 
    user system elapsed 
    2.308 0.002 2.339 

> ## my sapply solution No.1: 
> system.time(replicate(1000, data.frame(t(sapply(mylist, `[`))))) 
    user system elapsed 
    0.296 0.000 0.301 

> ## my sapply solution No.2: 
> system.time(replicate(1000, data.frame(lapply(data.frame(t(sapply(mylist, `[`))), 
+            unlist)))) 
    user system elapsed 
    1.067 0.001 1.091 

> ## @Martin Morgan's Map() sapply() solution: 
> f = function(x) function(i) sapply(x, `[[`, i) 
> system.time(replicate(1000, as.data.frame(Map(f(mylist), names(mylist[[1]]))))) 
    user system elapsed 
    0.775 0.000 0.778 

> ## @Martin Morgan's Map() lapply() unlist() solution: 
> f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE) 
> system.time(replicate(1000, as.data.frame(Map(f(mylist), names(mylist[[1]]))))) 
    user system elapsed 
    0.653 0.000 0.658 
+0

Hrm ..在這個答案中`replicate()`的用法有點奇怪。您正在測試將小列表轉換爲數據幀的效率。這似乎很少有用。測試一個* large * list列表的轉換效率是否更有意義? – naught101 2014-07-08 02:22:21

13

f = function(x) function(i) sapply(x, `[[`, i) 

是返回提取x的第i個元素的功能的功能。所以

Map(f(mylist), names(mylist[[1]])) 

得到一個命名(感謝地圖!矢量)的列表,可以製作成數據幀

as.data.frame(Map(f(mylist), names(mylist[[1]]))) 

對於速度通常更快的使用unlist(lapply(...), use.names=FALSE)作爲

f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE) 

更一般的變體是

f = function(X, FUN) function(...) sapply(X, FUN, ...) 

當執行列表結構出現了嗎?也許有一個更早的步驟,迭代可以被更多矢量化的東西所取代?

14

儘管這個問題早已有了答案,這是值得指出的data.table包有rbindlist其完成這項任務非常迅速:

library(microbenchmark) 
library(data.table) 
l <- replicate(1E4, list(a=runif(1), b=runif(1), c=runif(1)), simplify=FALSE) 

microbenchmark(times=5, 
    R=as.data.frame(Map(f(l), names(l[[1]]))), 
    dt=data.frame(rbindlist(l)) 
) 

給我

Unit: milliseconds 
expr  min  lq median  uq  max neval 
    R 31.060119 31.403943 32.278537 32.370004 33.932700  5 
    dt 2.271059 2.273157 2.600976 2.635001 2.729421  5 
2

的dplyr包的bind_rows是高效的。

one <- mtcars[1:4, ] 
two <- mtcars[11:14, ] 
system.time(dplyr::bind_rows(one, two)) 
    user system elapsed 
    0.001 0.000 0.001 
相關問題