我有一个JSON对象的以下表单的命名列表列表:
my_list = list(list(a = 10, b = "blah"),
list(a = 15, b = "stuff"))
外部列表的每个元素都是一个命名列表,我想将它转换为以下形式的data.frame,列名完好无损:
a b
10 "blah"
15 "stuff"
从表面上看,我可以通过to_df = data.frame(do.call(rbind, my_list))
来实现这一点。
但是,如果我尝试使用to_df$a
或to_df[,1]
提取单个列,我会得到一个列表而不是像data.frame通常所期望的那样的向量:
> to_df[,1]
[[1]]
[1] 10
[[2]]
[1] 15
而不是:
> to_df[,1]
[1] 10 15
R邮件列表上的旧帖子提出了以下解决方案:to_df = as.data.frame(t(sapply(my_list, rbind)))
。但是,这不仅不会转移到列名称,在使用to_df[,1]
查看单个列时,它仍然存在返回列表而不是向量的相同问题。
实现这一目标的最佳方法是什么?有dplyr
方式吗?
编辑:感谢所有解决方案,看来诀窍是lapply
并将列表的每个元素转换为data.frame
,然后使用dplyr或do.call
将它们绑定在一起。或者,data.table
只需拨打rbindlist
即可完成大部分工作。
答案 0 :(得分:13)
我更喜欢 data.table 包中的rbindlist
。它简单,快速,并返回数据框/表。
data.table::rbindlist(my_list)
# a b
# 1: 10 blah
# 2: 15 stuff
rbindlist()
的另一个优点是,它会自动使用NA
填写缺失值。
要删除data.table
类,您只需打包as.data.frame()
as.data.frame(data.table::rbindlist(my_list))
答案 1 :(得分:6)
在基地R你可以做
df<-do.call(rbind,lapply(my_list,data.frame))
答案 2 :(得分:6)
从两天前的 dplyr ,dplyr_0.4.2.9002的开发版本看,您可以使用bind_rows
执行此操作。
library(dplyr)
bind_rows(my_list)
Source: local data frame [2 x 2]
a b
1 10 blah
2 15 stuff
答案 3 :(得分:1)
快速纯base
R方式,如果列的类型不同并且您想要保留类型
# sample data
set.seed(46823239)
list_of_lists <-
replicate(
100, list(a = rnorm(100), b = sample.int(100, 100, replace = TRUE),
c = factor(sample(letters, 100, replace = TRUE))),
simplify = FALSE)
str( # show first two lists
list_of_lists[1:2])
#R> List of 2
#R> $ :List of 3
#R> ..$ a: num [1:100] -0.0439 -0.4487 -0.5682 -0.8062 1.5074 ...
#R> ..$ b: int [1:100] 59 91 63 87 61 72 92 77 62 41 ...
#R> ..$ c: Factor w/ 26 levels "a","b","c","d",..: 4 16 5 14 25 17 25 4 4 20 ...
#R> $ :List of 3
#R> ..$ a: num [1:100] 0.356 1.239 -0.926 -0.673 -1.168 ...
#R> ..$ b: int [1:100] 62 21 90 20 41 99 57 6 83 22 ...
#R> ..$ c: Factor w/ 26 levels "a","b","c","d",..: 15 16 17 6 3 13 21 16 3 11 ...
# define functions to stack
f1 <- function(x){
. <- function(...){
args <- list(...)
if(is.factor(args[[1]]))
# see https://stackoverflow.com/a/3449403/5861244
return(factor(do.call(c, lapply(args, as.character))))
do.call(c, args)
}
out <- NULL
for(i in 1:length(x[[1]]))
out <- c(out, list(do.call(., lapply(x, "[[", i))))
out <- data.frame(out)
names(out) <- names(x[[1]])
out
}
f2 <- function(x)
# simple alternative from http://r.789695.n4.nabble.com/Convert-list-of-lists-lt-gt-data-frame-td860048.html
do.call(rbind, lapply(x, data.frame))
# show output
all.equal( # yields the same
f1(list_of_lists), f2(list_of_lists))
#R> [1] TRUE
all.equal(
f1(list_of_lists), data.table::rbindlist(list_of_lists),
check.attributes = FALSE)
#R> [1] TRUE
out <- f1(list_of_lists)
head(out, 5)
#R> a b c
#R> 1 -0.04391595 59 d
#R> 2 -0.44866652 91 p
#R> 3 -0.56815817 63 e
#R> 4 -0.80622044 87 n
#R> 5 1.50736514 61 y
sapply(out, class)
#R> a b c
#R> "numeric" "integer" "factor"
# benchmark
microbenchmark::microbenchmark(
f1(list_of_lists), f2(list_of_lists), data.table::rbindlist(list_of_lists))
#R> Unit: microseconds
#R> expr min lq mean median uq max neval
#R> f1(list_of_lists) 1259.850 1426.3685 1633.127 1531.0590 1643.257 7086.211 100
#R> f2(list_of_lists) 31348.099 34293.8720 61224.476 37003.7930 92775.162 153318.869 100
#R> data.table::rbindlist(list_of_lists) 652.246 786.7645 1040.994 872.6905 1022.221 4063.994 100