在带有ID列的数据框中展开列表列

18

我的数据框包含了一个选择多个选项类型的调查输出。有些单元格有多个值。

df <- data.frame(a=1:3,b=I(list(1,1:2,1:3)))
df
  a       b
1 1       1
2 2    1, 2
3 3 1, 2, 3

我想将列表展平以获得以下输出:

df
  a       b
1 1       1
2 2       1
3 2       2
4 3       1
5 3       2
6 3       3

应该很简单,但不知道为什么找不到搜索词。谢谢。

5个回答

19

你可以直接使用 "tidyr" 中的 unnest 函数:

library(tidyr)
unnest(df, b)
#   a b
# 1 1 1
# 2 2 1
# 3 2 2
# 4 3 1
# 5 3 2
# 6 3 3

谢谢!完美,我看了tidyr但没有找到这个特殊情况。 - mloudon
楼主说这很容易,而你已经证明它确实是那么容易!干得好! - Alex A.

7

使用基本的R语言,一种选择是在将'b'列的列表元素命名为'a'元素后使用stack函数。我们可以使用setNames函数更改名称。

stack(setNames(df$b, df$a))

另一种选择是使用unstack将'b'的列表元素自动命名为'a'元素,然后进行stack以获取data.frame输出。
stack(unstack(df, b~a))

或者我们可以使用splitstackshape中方便的函数listCol_l来将list转换为data.frame
library(splitstackshape)
listCol_l(df, 'b')

2
干得好。我总是喜欢看到简单、优雅的基本解决方案。 - Alex A.

4

以下是一种使用data.table的方法:

require(data.table)
data.table(df)[,as.integer(unlist(b)),by=a]

如果b已经被一致地存储,那么可以跳过as.integer。您可以使用以下方式进行检查:
unique(sapply(df$b,class))
# [1] "numeric" "integer"

3
如果OP有多个这样的条目,df$b[1] <- list(1L)的方式太过手动和不切实际。我认为可以使用类似于data.table(df)[,as.integer(unlist(b)),by=a]的方法来解决。 - David Arenburg
@DavidArenburg 嗯,或者将数据“正确地”导入R(以整数形式存储)。我会切换到你写的内容。 - Frank

3

这是另一个基本解决方案,比迄今为止发布的任何其他解决方案都不够优雅。出于完整性的考虑而发布,尽管个人建议使用akrun的基本解决方案。

with(df, cbind(a = rep(a, sapply(b, length)), b = do.call(c, b)))

这将第一列构造为a的元素,每个元素都重复以匹配与b的相应列表项的长度。第二列是b使用do.call()c()“展开”的。

正如Ananda Mahto在评论中指出的那样,在最新版本的R(如果我没有弄错的话是3.2),可以使用lengths(b)代替sapply(b, length)


3
在最新版本的 R 中,你可以用 lengths(b) 替换 sapply(b, length) - A5C1D2H2I1M1N2O1R2T1
@AnandaMahto:太棒了,谢谢提醒! - Alex A.
要获取一个 data.frame,你最好使用:with(df, data.frame(a = rep(a, lengths(b)), b = unlist(b))) - Jaap

0
一个基于 R 的方法也可以是为每一行创建一个新的 data.frame,然后将它们用 rbind 连接在一起:
df <- data.frame(a=1:3,b=I(list(1,1:2,1:3)))
df

df <- lapply(seq_along(df$a), function(x){data.frame(a = df$a[[x]], b = df$b[[x]])})
df <- do.call("rbind", df)
df

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