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



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

在我的例子中,this_list是我正在使用的,this_df是我想要的。我已经尝试了各种方法来取消列出并转换为 data.frame,但我的列名称只是重复,我只得到 1 个观察结果。 谢谢。

this_list <- list(list(
  Name = "One",
  A = 2,
  B = 3,
  C = 4,
  D = 5
),
list(
  Name = "Two",
  A = 5,
  B = 2,
  C = 1
))

this_df <- data.frame(Name=c("One","Two"),
                      A=c(2,5),
                      B=c(3,2),
                      C=c(4,1),
                      D=c(5,NA))

您可以使用data.table中的rbindlist

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
这是

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

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++编写的包实现慢得多。

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

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

只是使用 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

相关内容

最新更新