如何将列表列表转换为数据帧 - 不相同的列表

时间:2014-07-07 11:04:14

标签: r

我有一个列表,其中每个元素都是一个命名列表,但各处的元素并不相同。我已经阅读了有关如何将列表列表转换为数据帧herehere的解决方案,但是当列表不相同时,这些方法都不起作用。

示例 - 注意我也有混合类型,如果解决方案强制将所有内容强加给角色,那就没问题了。

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绑定到数据帧。

有谁知道有效的解决方案?

3 个答案:

答案 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.framerbind_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

来自plyrldply data.frame

ldply(lisnotOK,data.frame)
   a b     c    d
1  1 2    hi <NA>
2 NA 2 hello nope