我有一个列表,其中每个元素都是一个命名列表,但各处的元素并不相同。我已经阅读了有关如何将列表列表转换为数据帧here和here的解决方案,但是当列表不相同时,这些方法都不起作用。
示例 - 注意我也有混合类型,如果解决方案强制将所有内容强加给角色,那就没问题了。
lisnotOK <- list(list(a=1, b=2, c="hi"), list(b=2, c="hello", d="nope"))
结果应该只有NA,其中列不能填充列,就像来自plyr的rbind.fill
或来自dplyr的rbind_all
。
实施例
lisOK <- list(list(a=1, b=2, c="hi"), list(a=3, b=5, c="bye"))
# One of many solutions
do.call(rbind.data.frame, lisOK)
# gives
a b c
2 1 2 hi
21 3 5 bye
使用rbind
或尝试将lisnotOK
转换为矩阵的任何解决方案都会失败,而上面链接的帖子中的任何示例都不起作用,即使我尝试使用{{1 }或rbind_all
。
一个解决方案是一个丑陋的for循环,其中每个连续列表都更改为数据帧,并使用rbind.fill
绑定到数据帧。
有谁知道有效的解决方案?
答案 0 :(得分:12)
在绑定之前对列表的每个元素使用data.frame(.)
的任何函数都会非常低效(更不用说不必要了)。以下是使用data.table
rbindlist
(来自v1.9.3)的另一种方式,您可以here获取。{/ p>
require(data.table) ## 1.9.3
rbindlist(lisnotOK, fill=TRUE)
# a b c d
# 1: 1 2 hi NA
# 2: NA 2 hello nope
它适用于列表列表(如本问题所示),data.frames和data.tables。
如果不是这样,那么我将使用Ananda的list2mat
功能(如果您的类型完全相同)。
Ananda的L2
数据基准:
fun1 <- function(inList) ldply(inList, as.data.frame)
fun2 <- function(inList) list2mat(inList)
fun3 <- function(inList) rbindlist(inList, fill=TRUE)
fun4 <- function(inList) rbind_all(lapply(inList, as.data.frame))
microbenchmark(fun1(L2), fun2(L2), fun3(L2), fun4(L2), times = 10)
# Unit: milliseconds
# expr min lq median uq max neval
# fun1(L2) 1927.857847 2161.432665 2221.999940 2276.241366 2366.649614 10
# fun2(L2) 12.039652 12.167613 12.361629 12.483751 16.040885 10
# fun3(L2) 1.225929 1.374395 1.473621 1.510876 1.858597 10
# fun4(L2) 1435.153576 1457.053482 1492.334965 1548.547706 1630.443430 10
注意:我使用了as.data.frame(.)
代替data.frame(.)
(前者稍快一点)。
答案 1 :(得分:9)
考虑到你可以得到所有相同类型的矩阵(比如character
),你可以尝试编写自己的函数,如下所示:
list2mat <- function(inList) {
UL <- unlist(inList)
Nam <- unique(names(UL))
M <- matrix(NA_character_,
nrow = length(inList), ncol = length(Nam),
dimnames = list(NULL, Nam))
Row <- rep(seq_along(inList), sapply(inList, length))
Col <- match(names(UL), Nam)
M[cbind(Row, Col)] <- UL
M
}
用法是:
list2mat(lisnotOK)
# a b c d
# [1,] "1" "2" "hi" NA
# [2,] NA "2" "hello" "nope"
这个应该非常快,因为所有内容都是预分配的,并且您正在使用矩阵索引。
fun1 <- function(inList) ldply(inList, data.frame)
fun2 <- function(inList) list2mat(inList)
library(microbenchmark)
microbenchmark(fun1(lisnotOK), fun2(lisnotOK))
# Unit: microseconds
# expr min lq median uq max neval
# fun1(lisnotOK) 4193.808 4340.0585 4523.3000 4912.233 7600.341 100
# fun2(lisnotOK) 163.784 182.3865 211.2515 236.910 363.489 100
L2 <- unlist(replicate(1000, lisnotOK, simplify=FALSE), recursive=FALSE)
microbenchmark(fun1(L2), fun2(L2), times = 10)
# Unit: milliseconds
# expr min lq median uq max neval
# fun1(L2) 3032.71572 3106.79006 3196.17178 3306.11756 3609.67445 10
# fun2(L2) 24.16817 24.86991 25.65569 27.44128 29.41908 10
答案 2 :(得分:6)
使用lapply
将您的列表元素转换为data.frame
和rbind_all
:
rbind_all(lapply(lisnotOK,data.frame))
a b c d
1 1 2 hi <NA>
2 NA 2 hello nope
Warning message:
In rbind_all(lapply(lisnotOK, data.frame)) :
Unequal factor levels: coercing to character
来自plyr
,ldply
data.frame
:
ldply(lisnotOK,data.frame)
a b c d
1 1 2 hi <NA>
2 NA 2 hello nope