如何将列表列表转换为可用的data.frame(用于打印表格)?

时间:2018-01-14 18:55:13

标签: r

我有一个未命名的列表列表,我需要将其转换为可用的data.frame。在大多数情况下,列表中的每个列表都具有相同的元素名称,但有些列表中有一些元素不会。因此,每个列表应该是我的data.frame中的一行,每个变量名称应该是一列,如果列表没有特定的变量,data.frame应该包含一个NA元素。

在我的例子中,this_list是我正在使用的,而this_df就是我想拥有的。我已经尝试了各种方法来取消列表并转换为data.frame,但我的列名只是重复,我只得到1次观察。谢谢。

b20.Text = "Text1";
b21.Text = "Text2";

4 个答案:

答案 0 :(得分:4)

您可以使用rbindlist中的data.table

library(data.table)
that_df <- as.data.frame(rbindlist(this_list, fill = TRUE))

# the result
   Name A B C  D
1:  One 2 3 4  5
2:  Two 5 2 1 NA

答案 1 :(得分:4)

这是人们经常联系dplyr::bind_rowsdata.table::rbindlist的任务。但是,在基数R中,如果列表元素一致,则快速基础R解决方案为do.call(rbind, ...)

do.call(rbind, list(this_list[[1]][1:4], this_list[[2]]))
#>      Name  A B C
#> [1,] "One" 2 3 4
#> [2,] "Two" 5 2 1

它返回一个矩阵,但可以很容易地清理。

但是,如果列表元素不一致,它会以烦人的方式回收(谢天谢地):

do.call(rbind, this_list)
#> Warning in (function (..., deparse.level = 1) : number of columns of result
#> is not a multiple of vector length (arg 2)
#>      Name  A B C D    
#> [1,] "One" 2 3 4 5    
#> [2,] "Two" 5 2 1 "Two"

因此需要更强大的解决方案,例如

rbind_list <- function(list, ...){
    # generate a vector of all variable names
    vars <- Reduce(function(x, y){union(x, names(y))}, list, init = c()); 

    filled_list <- lapply(list, function(x){
        x <- x[vars]    # add missing elements, reordering if necessary
        names(x) <- vars    # fix missing names
        x <- lapply(x, function(y){
            if (is.null(y)) {    # replace NULL with NA
                NA
            } else if (is.list(y)) {
                if (length(y) != 1) y <- list(y)    # handle non-length-1 list columns
                I(y)    # add as-is class to list columns so they don't fail
            } else {
                y
            }
        }) 
        as.data.frame(x, ...)    # coerce to data frame
    })

    do.call(rbind, filled_list)    # rbind resulting list of data frames
}

它明显优于do.call(rbind, ...)

rbind_list(this_list, stringsAsFactors = FALSE)
#>   Name A B C  D
#> 1  One 2 3 4  5
#> 2  Two 5 2 1 NA

rbind_list(c(this_list, this_list))
#>   Name A B C  D
#> 1  One 2 3 4  5
#> 2  Two 5 2 1 NA
#> 3  One 2 3 4  5
#> 4  Two 5 2 1 NA

rbind_list(list(list(a = 1), list(b = 2)))
#>    a  b
#> 1  1 NA
#> 2 NA  2

rbind_list(list(list(a = 1), list(a = 1, b = 2)))
#>   a  b
#> 1 1 NA
#> 2 1  2

rbind_list(list(list(a = 1, b = 2), list(b = 2, a = 1)))
#>   a b
#> 1 1 2
#> 2 1 2

...尽管列表处理仍然不一致:

# correct; is a list column
rbind_list(list(list(a = 1, c = list('foo')), list(a = 1, c = list('baz'))))
#>   a   c
#> 1 1 foo
#> 2 1 baz

# also correct
rbind_list(list(list(a = 1, c = list(c('foo', 'bar'))), list(a = 1, c = list('baz'))))
#>   a        c
#> 1 1 foo, bar
#> 2 1      baz

# can handle non-encapsulated nested lists
rbind_list(list(list(a = 1, c = list('foo', 'bar')), list(a = 1, c = list('baz'))))
#>   a        c
#> 1 1 foo, bar
#> 2 1      baz

# ...which confuses dplyr
dplyr::bind_rows(list(list(a = 1, c = list('foo', 'bar')), list(a = 1, c = list('baz'))))
#> Error in bind_rows_(x, .id): Argument 2 must be length 1, not 2

# ...but fills missing list elements with NA because it doesn't track classes across observations
rbind_list(list(list(a = 1), list(c = list('baz'))))
#>    a   c
#> 1  1  NA
#> 2 NA baz

# ...which dplyr handles better
dplyr::bind_rows(list(list(a = 1), list(c = list('baz'))))
#> # A tibble: 2 x 2
#>       a c        
#>   <dbl> <list>   
#> 1  1.00 <NULL>   
#> 2 NA    <chr [1]>

虽然肯定比do.call(rbind, ...)更强大,但这种方法可能比用C或C ++编写的包实现慢得多。

答案 2 :(得分:3)

仅使用基础R的解决方案。按顺序对每个列表元素执行完全连接。 (根据@RichScriven的评论编辑)

 this_df <- Reduce(function(x, y) merge(x, y, all = TRUE), this_list)

答案 3 :(得分:3)

使用dplyr包的另一种选择:

bind_rows(this_list)
# A tibble: 2 x 5
   Name     A     B     C     D
  <chr> <dbl> <dbl> <dbl> <dbl>
1   One     2     3     4     5
2   Two     5     2     1    NA

编辑:

我们在这里。这是rlist的另一个快速替代方案:

list.stack(this_list, fill = TRUE)
  Name A B C  D
1  One 2 3 4  5
2  Two 5 2 1 NA