如何使用 dplyr 管道一次性取消嵌套 DataFrame 的多个列表列

How to unnest multiple list columns of a dataframe in one go with dplyr pipe

提问人:littleworth 提问时间:5/30/2019 最后编辑:Z.Linlittleworth 更新时间:6/4/2022 访问量:5778

问:

我有以下提示,它有两个嵌套列:

library(tidyverse)
df <- structure(list(a = list(c("a", "b"), "c"), b = list(c("1", "2", 
"3"), "3"), c = c(11, 22)), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -2L))

这会产生:

# A tibble: 2 x 3
  a         b             c
  <list>    <list>    <dbl>
1 <chr [2]> <chr [3]>    11
2 <chr [1]> <chr [1]>    22

我怎样才能一次解开它们的巢穴,产生一个滴答声?

我试过这个但失败了:

> df %>% unnest(a, b)
Error: All nested columns must have the same number of elements.
r dplyr tidyr

评论


答:

8赞 Jared Wilber 5/30/2019 #1

可能有一种更简洁的方法可以做到这一点,但如果你想要列的笛卡尔积,你可以按顺序取消嵌套它们,如果不出意外的话:

> df %>% 
    unnest(a, .drop = FALSE) %>% 
    unnest(b, .drop = FALSE)

# # A tibble: 7 x 3
#       c a     b    
#   <dbl> <chr> <chr>
# 1    11 a     1    
# 2    11 a     2    
# 3    11 a     3    
# 4    11 b     1    
# 5    11 b     2    
# 6    11 b     3    
# 7    22 c     3

评论

3赞 drastega 10/3/2020
如何为许多列编写通用函数(无管道)?
5赞 Allen Baron 6/4/2022 #2

tl;博士

使用(如果 list-cols 缺少数据,请小心 --> ):unnest_cross()keep_empty = TRUE

unnest_cross <- function(data, cols, ...) {
    .df_out <- data
    .cols <- tidyselect::eval_select(rlang::enquo(cols), data)
    purrr::walk(
        .cols,
        function(col) {
            .df_out <<- unnest(.df_out, {{ col }}, ...)
        }
    )
    .df_out
}

背景:多个列表列unnest()

unnest自 2015 年以来处理了多个列。它目前使用参数,该参数接受典型的 tidyverse 选择方法。v0.3.0cols

请注意,它是专门为反转 data.frames 而设计的,并要求列表列是“并行条目......兼容尺寸”。这意味着:nest()

  1. 它不适用于 OP 的 data.frame。
df <- structure(list(
    a = list(c("a", "b"), "c"),
    b = list(c("1", "2", "3"), "3"),
    c = c(11, 22)),
    class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -2L))

tidyr::unnest(df, cols = tidyselect::everything())
#> Error in `fn()`:
#> ! In row 1, can't recycle input of size 2 to size 3.
  1. 它不会产生与顺序列表列相同的输出(例如笛卡尔乘积)。unnest()
# "parallel"/"compatible" data.frame
df_parallel <- structure(list(
    a = list(c("a", "b", "c"), "c"),
    b = list(c("1", "2", "3"), "3"),
    c = c(11, 22)),
    class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -2L))

tidyr::unnest(df_parallel, cols = tidyselect::everything())
#> # A tibble: 4 × 3
#>   a     b         c
#>   <chr> <chr> <dbl>
#> 1 a     1        11
#> 2 b     2        11
#> 3 c     3        11
#> 4 c     3        22

unnest_cross()

unnest_cross()用于循环显示指定的列及其列,每次通过 superassignment (with ) 保存结果。它的名字来源于相似性,因为它总是在 data.frame 中生成列表列的笛卡尔乘积,即使它们是“并行条目”和/或“大小兼容”purrr::walk()unnest()<<-purrr::cross()

  1. 它按原始 data.frame 的需要工作(列表列长度不等):
# For original data.frame
unnest_cross(df, cols = tidyselect::everything())
#> # A tibble: 7 × 3
#>   a     b         c
#>   <chr> <chr> <dbl>
#> 1 a     1        11
#> 2 a     2        11
#> 3 a     3        11
#> 4 b     1        11
#> 5 b     2        11
#> 6 b     3        11
#> 7 c     3        22
  1. 它创造了 的笛卡尔积,这与 有很大不同。df_parallelunnest()
# For df with list-columns of "compatible size"
unnest_cross(df_parallel, cols = tidyselect::everything())
#> # A tibble: 10 × 3
#>    a     b         c
#>    <chr> <chr> <dbl>
#>  1 a     1        11
#>  2 a     2        11
#>  3 a     3        11
#>  4 b     1        11
#>  5 b     2        11
#>  6 b     3        11
#>  7 c     1        11
#>  8 c     2        11
#>  9 c     3        11
#> 10 c     3        22

创建于 2022-06-03 由 reprex 软件包 (v2.0.1)