首页 > 解决方案 > 将具有部分空值的嵌套列表转换为 R data.frame

问题描述

对于单个 data.frames 对象列表,我通常可以轻松转换:

my_df <- do.call("rbind", lapply(my_list, data.frame))

但是,我目前拥有的列表对象是嵌套的。它是 data.frames 列表的列表。需要注意的几点:

  1. 父列表中的某些子列表的元素为空。
  2. 在包含信息的子列表中,一些列表具有多个 data.frame 对象。
  3. data.frame 对象的数量可能因子列表而异。

这是我正在处理的简化示例:

List of 3
 $ 1 :List of 2
  ..$ : NULL
  ..$ : NULL
 $ 2 :List of 2
  ..$ :'data.frame':    3 obs. of  2 variables:
  .. ..$ name                   : chr [1:3] "jack" "jim" "joe" "jon"
  .. ..$ value                  : chr [1:3] "10" "12" "13" "14"
  ..$ :'data.frame':    4 obs. of  2 variables:
  .. ..$ name                   : chr [1:4] "jacky" "jane" "juanita" "julia"
  .. ..$ value                  : chr [1:4] "11" "9" "10" "14"
 $ 3 :List of 1
  ..$ :'data.frame':    5 obs. of  2 variables:
  .. ..$ name                   : chr [1:5] "adam" "ashley" "arnold" "avery" "arthur"
  .. ..$ value                  : chr [1:5] "6" "7" "11" "12" "11"

上面的do.call方法报告了一个错误arguments imply differing number of rows,所以看起来我的带有不同行号的 data.frames 的列表导致了这个问题?

我尝试了这篇文章中描述的一些策略,但每次尝试都有其独特的错误。

data.tablerbindlist”方法和dplyr“bind_rows”方法都报告了:

fill=TRUE, but names of input list at position 1 is NULL

感谢您提供有关如何处理这种情况的任何提示。

标签: r

解决方案


首先,NULL值无关紧要,因为rbind(NULL, NULL, ..., A, B, C, ...)与 相同rbind(A, B, C, ...)

其次,列表的结构很重要。如果您的嵌套列表与您的示例一样简单,那么答案也很简单。下面的代码可以解决这个问题:

# This list is the same as your example
test <- list(
  list(NULL, NULL),
  list(data.frame(name = c("jack", "jim", "joe", "jon"), 
                  value = c("10", "12", "13", "14")),
       data.frame(name = c("jacky", "jane", "juanita", "julia"), 
                  value = c("11", "9", "10", "14"))),

  list(data.frame(name = c("adam", "ashley", "arnold", "avery", "arthur"),
                  value = c("6", "7", "11", "12", "11")))
)

# This function rbinds the dataframes inside a list
ls_rbind <- function(ls_of_df) do.call(rbind, ls_of_df)
# Applying "ls_rbind" to each of your child lists creates a list, each of whose elements is either a NULL or a dataframe
# Applying "ls_rbind" again to the result list gives you a dataframe you want
result_df <- ls_rbind(lapply(test, ls_rbind))

但是,如果您的嵌套列表实际上更复杂,那么您可能需要更通用的方法来处理它。例如,每个子列表可以是以下项之一:

  • 非列表项,即数据框或NULL
  • 也可能包含列表、数据框或NULLs 的列表

在这种情况下,递归可能会有所帮助。考虑以下代码:

# These two lines complicate the list structure 
test[[4]] <- test
test[[1]][[3]] <- test

recr_ls_rbind <- function(ls_of_ls) {
  are_lists <- lapply(ls_of_ls, class) == "list"
  # Any child lists will be recursively evaluated. "part1" is always a list
  part1 <- lapply(ls_of_ls[are_lists], recr_ls_rbind)
  # Applying the above function "ls_rbind" to all non-list child items and then coerce the result into a list. 
  part2 <- list(ls_rbind(ls_of_ls[!are_lists]))
  # Put part1 and part2 into the same list and apply "ls_rbind" to it.
  ls_rbind(c(part1, part2))
}
result_df <- recr_ls_rbind(test)

推荐阅读