将具有不同名称的嵌套列表转换为 data.frame 填充 NA 并添加列

eng*_*uze 2 r list nested-lists dataframe rbind

我需要一个基本的 R 解决方案来将具有不同名称的嵌套列表转换为 data.frame

mylist <- list(list(a=1,b=2), list(a=3), list(b=5), list(a=9, z=list('k'))

convert(mylist)
## returns a data.frame:
##
##     a     b    z           
##     1     2    <NULL>   
##     3    NA    <NULL>   
##    NA     5    <NULL>   
##     9    NA    <chr [1]>
Run Code Online (Sandbox Code Playgroud)

我知道这可以很容易地用dplyr::bind_rowsdata.table::rbindlistwith完成fill = TRUE(虽然并不理想,因为它用NULL,而不是填充字符列NA),但我确实需要基本 R 中的解决方案。为了简化问题,使用 2 级嵌套也可以没有第三级列表的列表,例如

mylist <- list(list(a=1,b=2), list(a=3), list(b=5), list(a=9, z='k'))

convert(mylist)
## returns a data.frame:
##
##     a     b    z           
##     1     2    NA   
##     3    NA    NA   
##    NA     5    NA   
##     9    NA    k  
Run Code Online (Sandbox Code Playgroud)

我试过类似的东西

convert <- function(L) as.data.frame(do.call(rbind, L))
Run Code Online (Sandbox Code Playgroud)

这不会填充NA并添加附加列z

更新

mylist这只是一个简化的例子。实际上,我无法假设子列表元素的名称(a示例中的b和),也无法假设子列表的长度(示例中的 2, 1, 1, 2 )。z

data.frame以下是预期和输入的假设mylist

  1. 预期的列数data.frame由子列表的最大长度决定,子列表的最大长度可能从 1 到数百不等。没有关于每个子列表长度的明确信息源(哪些名称将在哪个子列表中出现或消失未知) max(sapply(mylist, length)) <= 1000 ## ==> TRUE
  2. 预期的行数data.frame 由长度决定,mylist可能从 1 到几千不等 dplyr::between(length(mylist), 0, 10000) ## ==> TRUE
  3. 没有关于子列表元素的名称及其顺序的明确信息,因此预期的列名称和顺序data.frame只能从本质上确定mylist
  4. numeric每个子列表包含,character或类型的元素list。为了简化问题,仅考虑 numericcharacter

All*_*ron 5

基础 R 的一个较短的解决方案是

make_df <- function(a = NA, b = NA, z = NA) {
  data.frame(a = unlist(a), b = unlist(b), z = unlist(z))
}

do.call(rbind, lapply(mylist, function(x) do.call(make_df, x)))
#>    a  b    z
#> 1  1  2 <NA>
#> 2  3 NA <NA>
#> 3 NA  5 <NA>
#> 4  9 NA    k
Run Code Online (Sandbox Code Playgroud)

更新

使用相同方法但不需要特定名称的更通用解决方案是:

build_data_frame <- function(obj) {
  nms     <- unique(unlist(lapply(obj, names)))
  frmls   <- as.list(setNames(rep(NA, length(nms)), nms))
  dflst   <- setNames(lapply(nms, function(x) call("unlist", as.symbol(x))), nms)
  make_df <- as.function(c(frmls, call("do.call", "data.frame", dflst)))
  
  do.call(rbind, lapply(mylist, function(x) do.call(make_df, x)))
}
Run Code Online (Sandbox Code Playgroud)

这允许

build_data_frame(mylist)
#>    a  b    z
#> 1  1  2 <NA>
#> 2  3 NA <NA>
#> 3 NA  5 <NA>
#> 4  9 NA    k
Run Code Online (Sandbox Code Playgroud)