【问题标题】:Unnest a list column directly into several columns将列表列直接取消嵌套到多个列中
【发布时间】:2018-09-16 07:30:36
【问题描述】:

我可以将列表列直接取消嵌套到 n 列吗?

可以假定列表是规则的,所有元素的长度相等。

如果我有一个字符向量而不是一个列表列,我可以tidyr::separate。我可以tidyr::unnest,但我们需要另一个辅助变量才能tidyr::spread。我错过了一个明显的方法吗?

示例数据:

library(tibble)

df1 <- data_frame(
  gr = c('a', 'b', 'c'),
  values = list(1:2, 3:4, 5:6)
)
# A tibble: 3 x 2
  gr    values   
  <chr> <list>   
1 a     <int [2]>
2 b     <int [2]>
3 c     <int [2]>

目标:

df2 <- data_frame(
  gr = c('a', 'b', 'c'),
  V1 = c(1, 3, 5),
  V2 = c(2, 4, 6)
)
# A tibble: 3 x 3
  gr       V1    V2
  <chr> <dbl> <dbl>
1 a        1.    2.
2 b        3.    4.
3 c        5.    6.

当前方法:

unnest(df1) %>% 
  group_by(gr) %>% 
  mutate(r = paste0('V', row_number())) %>% 
  spread(r, values)

【问题讨论】:

  • 我不知道为什么,但这行得通? library(splitstackshape); cSplit(df1, "values", sep = ":") ?
  • @RonakShah 我认为 splitstackshape 在幕后做了 Victorp 的回答。

标签: r tidyr tibble


【解决方案1】:

data.table 非常简单:

library("data.table")
setDT(df1)
df1[, c("V1", "V2") := transpose(values)]
df1
#    gr values V1 V2
# 1:  a    1,2  1  2
# 2:  b    3,4  3  4
# 3:  c    5,6  5  6

【讨论】:

  • 列表元素长度不同时的替代方案:df1[, rn := .I][, transpose(values), by = .(gr, rn)][]
【解决方案2】:

也许是这样的:

cbind(df1[, "gr"], do.call(rbind, df1$values))

【讨论】:

    【解决方案3】:
    library(tibble)
    
    df1 <- data_frame(
      gr = c('a', 'b', 'c'),
      values = list(1:2, 3:4, 5:6)
    )
    
    library(tidyverse)
    
    df1 %>%
      mutate(r = map(values, ~ data.frame(t(.)))) %>%
      unnest(r) %>%
      select(-values)
    
    # # A tibble: 3 x 3
    #   gr       X1    X2
    #   <chr> <int> <int>
    # 1 a         1     2
    # 2 b         3     4
    # 3 c         5     6
    

    【讨论】:

      【解决方案4】:

      另一个:

      library(tibble)
      library(dplyr)
      
      df1 <- data_frame(
        gr = c('a', 'b', 'c'),
        values = list(1:2, 3:4, 5:6)
      )
      
      df %>% mutate(V1 = sapply(values, "[[", 1), V2 = sapply(values, "[[", 2))
      
      # A tibble: 3 x 4
        gr    values       V1    V2
        <chr> <list>    <int> <int>
      1 a     <int [2]>     1     2
      2 b     <int [2]>     3     4
      3 c     <int [2]>     5     6
      

      编辑:

      当列出的向量很长,手写V1 = sapply(values, "[[", index)不方便时,可以将它与lazyeval中的f_interp结合起来:

      library(tibble)
      library(dplyr)
      library(lazyeval)
      df <- data_frame(gr = c('a', 'b', 'c'), values = list(1:11, 3:13, 5:15))
      nums <- c(1:11)
      ll <- lapply(nums, function(nr) f_interp(~sapply(values, "[[", uq(nr))))
      mutate_(df, .dots=setNames(ll, paste("V", nums, sep="")))
      
      # A tibble: 3 x 12
        gr    values        V1    V2    V3    V4    V5    V6    V7    V8    V9   V10
        <chr> <list>     <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
      1 a     <int [11]>     1     2     3     4     5     6     7     8     9    10
      2 b     <int [11]>     3     4     5     6     7     8     9    10    11    12
      3 c     <int [11]>     5     6     7     8     9    10    11    12    13    14
      

      【讨论】:

        【解决方案5】:

        我曾多次遇到类似的问题。与其他答案相比,我的解决方案无疑是笨拙的,但为了完整起见,报告它。

        library(tibble)
        df1 <- data_frame(
          gr = c('a', 'b', 'c'),
          values = list(1:2, 3:4, 5:6)
        )
        
        matrix(unlist(df1[1])) -> grs
        matrix(unlist(df1[2]), byrow=T, ncol=2) -> vals
        

        结果:

        > data.frame(grs, vals)
          grs X1 X2
        1   a  1  2
        2   b  3  4
        3   c  5  6 
        

        【讨论】:

        • 谢谢,但我们绝对不想转换为 matrix 并将所有内容强制转换为字符!
        【解决方案6】:

        使用 tidyr 1.0.0 你可以这样做:

        library(tidyr)
        df1 <- tibble(
          gr = c('a', 'b', 'c'),
          values = list(1:2, 3:4, 5:6)
        )
        
        unnest_wider(df1, values)
        #> New names:
        #> * `` -> ...1
        #> * `` -> ...2
        #> New names:
        #> * `` -> ...1
        #> * `` -> ...2
        #> New names:
        #> * `` -> ...1
        #> * `` -> ...2
        #> # A tibble: 3 x 3
        #>   gr     ...1  ...2
        #>   <chr> <int> <int>
        #> 1 a         1     2
        #> 2 b         3     4
        #> 3 c         5     6
        

        reprex package (v0.3.0) 于 2019-09-14 创建

        这里的输出很冗长,因为没有命名水平未嵌套的元素(向量元素),并且unnest_wider 不想默默猜测。

        我们可以事先命名它们以避免它:

        df1 %>%
          dplyr::mutate(values = purrr::map(values, setNames, c("V1","V2"))) %>%
          unnest_wider(values)
        #> # A tibble: 3 x 3
        #>   gr       V1    V2
        #>   <chr> <int> <int>
        #> 1 a         1     2
        #> 2 b         3     4
        #> 3 c         5     6
        

        或者只使用suppressMessages()purrr::quietly()

        【讨论】:

        • 详细程度可能会在未来的版本中发生变化,如果我忘记编辑答案,请在此处联系我!
        • 如果我有多个列要取消嵌套怎么办?
        • 我遇到了类似的问题。我的列表列的每个元素都是 3 个数字的列表,使用 unnest() 取消嵌套列只会为我提供第一个值。为什么需要 unnest_wider?
        猜你喜欢
        • 2018-01-01
        • 2022-01-10
        • 1970-01-01
        • 2021-10-01
        • 2018-05-18
        • 2021-03-20
        • 2019-10-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多