我有一个带有分类变量的数据框,其中包含可变长度的字符串列表(这很重要,因为否则此问题将与此或此重复),例如:
df <- data.frame(x = 1:5) df$y <- list("A", c("A", "B"), "C", c("B", "D", "C"), "E") df
x y 1 1 A 2 2 A, B 3 3 C 4 4 B, D, C 5 5 E
并且所需的形式是在任何地方看到的每个唯一字符串的虚拟变量df$y
,即:
data.frame(x = 1:5, A = c(1,1,0,0,0), B = c(0,1,0,1,0), C = c(0,0,1,1,0), D = c(0,0,0,1,0), E = c(0,0,0,0,1))
x A B C D E 1 1 1 0 0 0 0 2 2 1 1 0 0 0 3 3 0 0 1 0 0 4 4 0 1 1 1 0 5 5 0 0 0 0 1
这种天真的方法有效:
> uniqueStrings <- unique(unlist(df$y)) > n <- ncol(df) > for (i in 1:length(uniqueStrings)) { + df[, n + i] <- sapply(df$y, function(x) ifelse(uniqueStrings[i] %in% x, 1, 0)) + colnames(df)[n + i] <- uniqueStrings[i] + }
然而,对于大数据帧,它非常难看,懒惰和缓慢.
有什么建议?有什么好看的tidyverse
吗?
更新:我有三种不同的方法.我system.time
在我的(Windows 7,32GB RAM)笔记本电脑上使用真实数据集测试它们,包括1M行,每行包含长度为1到4个字符串的列表(大约350个唯一字符串值),磁盘上总共200MB.所以预期的结果是一个尺寸为1M x 350的数据框.(@Sotos)tidyverse
和base
(@ joel.wilson)方法花了这么长时间我不得不重新启动R.但是qdapTools
(@akrun)方法却很棒:
> system.time(res1 <- mtabulate(varsLists)) user system elapsed 47.05 10.27 116.82
所以这是我接受的方法.
我们可以用 mtabulate
library(qdapTools) cbind(df[1], mtabulate(df$y)) # x A B C D E #1 1 1 0 0 0 0 #2 2 1 1 0 0 0 #3 3 0 0 1 0 0 #4 4 0 1 1 1 0 #5 5 0 0 0 0 1
另一个想法,
library(dplyr) library(tidyr) df %>% unnest(y) %>% mutate(new = 1) %>% spread(y, new, fill = 0) # x A B C D E #1 1 1 0 0 0 0 #2 2 1 1 0 0 0 #3 3 0 0 1 0 0 #4 4 0 1 1 1 0 #5 5 0 0 0 0 1
另外您在评论中提到的情况下,我们可以使用dcast
从reshape2
因为它比更灵活spread
,
df2 <- df %>% unnest(y) %>% group_by(x) %>% filter(!duplicated(y)) %>% ungroup() reshape2::dcast(df2, x ~ y, value.var = 'y', length) # x A B C D E #1 1 1 0 0 0 0 #2 2 1 1 0 0 0 #3 3 0 0 1 0 0 #4 4 0 1 1 1 0 #5 5 0 0 0 0 1 #or with df$x <- c(1, 1, 2, 2, 3) # x A B C D E #1 1 1 1 0 0 0 #2 2 0 1 1 1 0 #3 3 0 0 0 0 1 #or with df$x <- rep(1,5) # x A B C D E #1 1 1 1 1 1 1