如何使用dplyr管道一次性展开数据框中的多个列表列

9

我有以下tibble,其中包含两个嵌套列:

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

我该如何一次性将它们解嵌套并生成一个单一的tibble?

我尝试了这个方法但失败了:

> df %>% unnest(a, b)
Error: All nested columns must have the same number of elements.
2个回答

8

也许有更简便的方法,但如果你想要得到列的笛卡尔积,你可以按顺序将它们展开:

> 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

5

简洁版

使用 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()v0.3.0(2015年)开始支持多个列。它当前使用cols参数,该参数接受典型的tidyverse选择方法。

注意:它专门设计用于反转nest()数据框,并要求列表列为“兼容尺寸的并行条目”。这意味着:

  1. 它不适用于原始数据框。
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() 使用 purrr::walk() 循环遍历指定的列,并通过超级赋值(使用 <<-)每次将结果保存下来。它的名称源于与 purrr::cross() 的相似性,因为它总是在数据框中生成列表列的笛卡尔积,即使它们是“平行条目”和/或“兼容大小”。

  1. 对于原始数据框(具有不同长度的列表列),它可以按预期工作:
# 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_parallel的笛卡尔积,这与unnest()非常不同。
# 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年6月3日,使用reprex软件包(v2.0.1)

这是一段关于IT技术的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接