我经常想要将每个索引具有相同元素类型的列表转换为数据帧.例如,我可能有一个列表:
> 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
Run Code Online (Sandbox Code Playgroud)
我想将此列表转换为数据框,其中每个索引元素都是一列.自然(对我来说)要做的就是使用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
Run Code Online (Sandbox Code Playgroud)
很简单,但是当我尝试将此矩阵转换为数据框时,列仍然是列表元素,而不是向量:
> my.df<-as.data.frame(my.matrix, stringsAsFactors=FALSE)
> my.df[,1]
[[1]]
[1] 24267673
[[2]]
[1] 11561448
Run Code Online (Sandbox Code Playgroud)
目前,为了正确地投射数据帧,我使用unlist和迭代每个列as.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)
Run Code Online (Sandbox Code Playgroud)
然而,这似乎效率很低.有没有更好的方法来做到这一点?
Jos*_*ich 49
我想你想要:
> 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
Run Code Online (Sandbox Code Playgroud)
Rei*_*son 31
另一种选择是:
data.frame(t(sapply(mylist, `[`)))
Run Code Online (Sandbox Code Playgroud)
但是这个简单的操作会导致列表的数据框:
> 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"
Run Code Online (Sandbox Code Playgroud)
对此的替代方案,沿着相同的路线,但现在结果与其他解决方案相同,是:
data.frame(lapply(data.frame(t(sapply(mylist, `[`))), unlist))
Run Code Online (Sandbox Code Playgroud)
[ 编辑:包含@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
Run Code Online (Sandbox Code Playgroud)
JD *_*ong 18
我不能告诉你这在内存或速度方面是"最有效的",但它在编码方面非常有效:
my.df <- do.call("rbind", lapply(my.list, data.frame))
Run Code Online (Sandbox Code Playgroud)
data.frame()的lapply()步骤将每个列表项转换为单行数据框,然后使用rbind()运行良好
Kev*_*hey 16
尽管这个问题早已有了答案,这是值得指出的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))
)
Run Code Online (Sandbox Code Playgroud)
给我
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
Run Code Online (Sandbox Code Playgroud)
Mar*_*gan 13
这个
f = function(x) function(i) sapply(x, `[[`, i)
Run Code Online (Sandbox Code Playgroud)
是一个函数,它返回一个提取x的第i个元素的函数.所以
Map(f(mylist), names(mylist[[1]]))
Run Code Online (Sandbox Code Playgroud)
得到一个命名(谢谢Map!)矢量列表,可以将其作为数据框
as.data.frame(Map(f(mylist), names(mylist[[1]])))
Run Code Online (Sandbox Code Playgroud)
对于速度它通常是更快地使用unlist(lapply(...), use.names=FALSE)作为
f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE)
Run Code Online (Sandbox Code Playgroud)
更一般的变体是
f = function(X, FUN) function(...) sapply(X, FUN, ...)
Run Code Online (Sandbox Code Playgroud)
什么时候列表列表结构出现了?也许有一个更早的步骤,迭代可以被更多矢量化的东西取代?