如何“展开”列表列?

11

考虑这个简单的例子

mydf <- data_frame(regular_col = c(1,2),
                   normal_col = c('a','b'),
                   weird_col = list(list('hakuna', 'matata'),
                                 list('squash', 'banana')))

> mydf
# A tibble: 2 x 3
  regular_col normal_col weird_col 
        <dbl> <chr>      <list>    
1           1 a          <list [2]>
2           2 b          <list [2]>
我想提取weird_col的元素(程序化地,元素数量可能会改变),以便每个元素都放置在不同的列中。也就是说,我期望以下输出结果。
> data_frame(regular_col = c(1,2),
+           normal_col = c('a','b'),
+           weirdo_one = c('hakuna', 'squash'),
+           weirdo_two = c('matata', 'banana'))
# A tibble: 2 x 4
  regular_col normal_col weirdo_one weirdo_two
        <dbl> <chr>      <chr>      <chr>     
1           1 a          hakuna     matata
2           2 b          squash     banana    

然而,我无法用简单的语言来解释。例如,使用经典的unnest会失败,因为它会展开数据框而不是将列表中的每个元素放置在不同的列中。

> mydf %>% unnest(weird_col)
# A tibble: 4 x 3
  regular_col normal_col weird_col
        <dbl> <chr>      <list>   
1           1 a          <chr [1]>
2           1 a          <chr [1]>
3           2 b          <chr [1]>
4           2 b          <chr [1]>

tidyverse中有没有解决方案?


你会斯瓦希里语吗? - Onyambu
1
我知道狮子王哈哈 :) - ℕʘʘḆḽḘ
3
mydf%>%group_by(regular_col)%>%mutate(weird_col = invoke(paste,weird_col,collapse=","))%>%separate(weird_col,c("col1","col2"))将数据框按照regular_col字段进行分组,然后将weird_col中的多个值拼接成一个字符串,并以逗号为分隔符。接着,将拼接后的字符串按照逗号分隔成两列,分别命名为col1col2 - Onyambu
1
调用(invoke)与 do.call 完全相同。 - Onyambu
invoke is similar to do.call (and it's a simple wrapper round it, if you look at the code), the main difference is that it has an additional ... argument, that @Onyambu uses here to specify collapse="," - moodymudskipper
显示剩余3条评论
2个回答

11
你可以从unnest的输出中提取值,稍微处理以生成列名,然后再次使用spread。请注意,由于您的深度为一的列表列,我使用flatten_chr,但是如果它是嵌套的,则可以使用flatten,并且spread在列表列上同样有效。
library(tidyverse)
#> Warning: package 'dplyr' was built under R version 3.5.1
mydf <- data_frame(
  regular_col = c(1, 2),
  normal_col = c("a", "b"),
  weird_col = list(
    list("hakuna", "matata"),
    list("squash", "banana")
  )
)
mydf %>%
  unnest(weird_col) %>%
  group_by(regular_col, normal_col) %>%
  mutate(
    weird_col = flatten_chr(weird_col),
    weird_colname = str_c("weirdo_", row_number())
    ) %>% # or just as.character
  spread(weird_colname, weird_col)
#> # A tibble: 2 x 4
#> # Groups:   regular_col, normal_col [2]
#>   regular_col normal_col weirdo_1 weirdo_2
#>         <dbl> <chr>      <chr>    <chr>   
#> 1           1 a          hakuna   matata  
#> 2           2 b          squash   banana

reprex软件包(v0.2.0)于2018年08月12日创建。


5
< p > unnest 将列表和向量展开为垂直方向,并将单行数据框水平展开。因此,我们可以将您的列表更改为数据框(具有适当的列名),然后进行unnest操作。

mydf %>% mutate(weird_col = map(weird_col,~ as_data_frame(
  setNames(.,paste0("weirdo_",1:length(.)))
  ))) %>% 
  unnest

# # A tibble: 2 x 4
#   regular_col normal_col weirdo_1 weirdo_2
#         <dbl>      <chr>    <chr>    <chr>
# 1           1          a   hakuna   matata
# 2           2          b   squash   banana

1
相当不错和简洁! - ℕʘʘḆḽḘ
与 @Onyambu 在评论中建议的类似,只不过他首先将列表转换为逗号分隔的字符串,然后使用 separate 横向展开。 - moodymudskipper
如果列表中的元素数量在不同行中可能会有所变化,那么这个解决方案是否可行? - ℕʘʘḆḽḘ
1
是的,如果从第一个列表中删除“matata”,则会在“weirdo_2”列中得到NA。 - moodymudskipper

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