将列表列直接展开为多个列

41
我可以直接将列表列展开成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)

1
我不知道为什么,但这段代码能够正常工作?library(splitstackshape); cSplit(df1, "values", sep = ":") - Ronak Shah
@RonakShah 我认为 splitstackshape 在底层执行的是 Victorp 的答案所做的操作。 - zx8754
6个回答

43

使用 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包(v0.3.0)于2019年9月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()


冗长度在未来的版本中可能会发生变化,如果我忘记在发生变化时编辑答案,请有人在这里提醒我! - moodymudskipper
如果我有多列需要展开怎么办? - Laura
我遇到了类似的问题。我的列表列的每个元素都是由3个数字组成的列表,使用unnest()展开该列只会提供第一个值。为什么需要使用unnest_wider? - JASC
Laura和@JASC查看了Paweł Kozielski-Romaneczko的答案 - rubengavidia0x

13

使用 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

4
当列表元素长度不相等时的替代方法是:df1[, rn := .I][, transpose(values), by = .(gr, rn)][]。其中,.I表示数据表中的行数索引,rn := .I为将该索引赋值给新的一列rntranspose(values)将列表转置为数据表;by = .(gr, rn)按照组gr和新列rn进行分组聚合;最后的[]用于输出结果。 - Jaap

12
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

8

也许是这样:

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

1
另一个例子:
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) 不方便,那么你可以将其与 lazyevalf_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

1

我曾多次遇到类似的问题。我的解决方案与其他答案相比确实有些笨重,但为了完整性而报告。

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 

谢谢,但我们绝对不想转换为“矩阵”并将所有内容强制转换为字符! - Axeman

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