将嵌套列表元素转换为数据框并将结果绑定到一个数据框中

Jul*_*rre 4 r

我有一个这样的嵌套列表:

x <- list(x = list(a = 1, 
                   b = 2), 
          y = list(a = 3, 
                   b = 4))
Run Code Online (Sandbox Code Playgroud)

我想将嵌套列表转换为data.frames,然后将所有数据帧绑定到一个.

对于这种嵌套级别,我可以用这一行来做:

do.call(rbind.data.frame, lapply(x, as.data.frame, stringsAsFactors = FALSE))
Run Code Online (Sandbox Code Playgroud)

结果是:

  a b
x 1 2
y 3 4
Run Code Online (Sandbox Code Playgroud)

我的问题是,无论嵌套程度如何,我都希望实现这一目标.此列表的另一个示例:

x <- list(X = list(x = list(a = 1, 
                       b = 2), 
              y = list(a = 3, 
                       b = 4)),
     Y = list(x = list(a = 1, 
                       b = 2), 
              y = list(a = 3, 
                       b = 4)))

do.call(rbind.data.frame, lapply(x, function(x) do.call(rbind.data.frame, lapply(x, as.data.frame, stringsAsFactors = FALSE))))

    a b
X.x 1 2
X.y 3 4
Y.x 1 2
Y.y 3 4
Run Code Online (Sandbox Code Playgroud)

有没有人有想法将其归结为任何嵌套级别?谢谢你的帮助

Axe*_*man 8

从Spacedman借贷和flodel 在这里,我们可以定义以下对递归函数:

library(tidyverse)  # I use dplyr and purrr here, plus tidyr further down below

depth <- function(this) ifelse(is.list(this), 1L + max(sapply(this, depth)), 0L)

bind_at_any_depth <- function(l) {
  if (depth(l) == 2) {
    return(bind_rows(l))
  } else {
    l <- at_depth(l, depth(l) - 2, bind_rows)
    bind_at_any_depth(l)
  }
}
Run Code Online (Sandbox Code Playgroud)

我们现在可以将任意深度列表绑定到单个data.frame中:

bind_at_any_depth(x)
Run Code Online (Sandbox Code Playgroud)
# A tibble: 2 × 2
      a     b
  <dbl> <dbl>
1     1     2
2     3     4
Run Code Online (Sandbox Code Playgroud)
bind_at_any_depth(x_ext) # From P Lapointe
Run Code Online (Sandbox Code Playgroud)
# A tibble: 5 × 2
      a     b
  <dbl> <dbl>
1     1     2
2     5     6
3     7     8
4     1     2
5     3     4
Run Code Online (Sandbox Code Playgroud)

如果要跟踪每行的来源,可以使用此版本:

bind_at_any_depth2 <- function(l) {
  if (depth(l) == 2) {
    l <- bind_rows(l, .id = 'source')
    l <- unite(l, 'source', contains('source'))
    return(l)
  } else {
    l <- at_depth(l, depth(l) - 2, bind_rows, .id = paste0('source', depth(l)))
    bind_at_any_depth(l)
  }
}
Run Code Online (Sandbox Code Playgroud)

这将添加一source列:

bind_at_any_depth2(x_ext)
Run Code Online (Sandbox Code Playgroud)
# A tibble: 5 × 3
  source     a     b
*  <chr> <dbl> <dbl>
1  X_x_1     1     2
2  X_y_z     5     6
3 X_y_zz     7     8
4  Y_x_1     1     2
5  Y_y_1     3     4
Run Code Online (Sandbox Code Playgroud)

注意:在某些时候你可以使用purrr::depth,并且需要更改at_depthmodify_depth他们的新版本推出到CRAN时(感谢@ManuelS).