2017-01-16 52 views
7

我有一个分类变量保持列表字符串,具有可变长度的数据帧的虚拟变量(这是很重要的,否则这个问题将是this重复或this),例如:R:创建一个基于列表的分类变量*

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看中什么?


更新:我有3种不同的方法。我在我的(Windows 7,32GB RAM)笔记本上使用system.timereal数据集上测试了这些数据集,其中包含1M行,每行包含长度为1到4个字符串的列表(大约350个唯一字符串值中的一个),总共200MB在磁盘上。因此,预期的结果是尺寸为1M x 350的数据帧。tidyverse(@Sotos)和base(@ joel.wilson)方法需要很长时间才能重新启动R.但qdapTools(@akrun)方法的效果却很棒:

> system.time(res1 <- mtabulate(varsLists)) 
    user system elapsed 
    47.05 10.27 116.82 

所以这是我接受的方法。

+0

或'data.frame(X = DF $ X,T(sapply(DF $ Y,功能(l){表(因子(1,级别= LETTERS [1:5] ))})))' – alistaire

+0

@alistaire maybe'levels = unique(unlist(df $ y))''而不是'LETTERS [1:5]'? – Sotos

+0

@Sotos我有,但认为这是更少的计算。最好的办法是把它作为一个单独的变量存储,但这需要第二行... – alistaire

回答

6

我们可以使用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 
+0

这是令人印象深刻和超级快速(几百秒〜1M的行,在我的PC上约350个独特的值) 。你有没有需要一个全新的包装的答案?谢谢。 –

+0

@GioraSimchoni看起来像别人没有包裹回答它 – akrun

+2

@GioraSimchoni太;我猜一个基本的选择是'table(rep(df $ x,lengths(df $ y)),unlist(df $ y))'? –

6

另一个想法,

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 

而且您在评论中提到的情况下,我们可以使用dcastreshape2因为它比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 
+0

谢谢,看看当df $ x = rep(1,5)时会发生什么。 “错误:行(1,2),(3,5),(4,7)的重复标识符” –

+0

在这种情况下,您的预期结果是什么?类似于'df%>%unnest(y)%>%group_by(x)%>%mutate(new = 1:n())%>%spread(y,x,fill = 0)'' – Sotos

+0

保留原始x列的结果相同。这在最初的'df'上给出了“错误:行(1,2)的重复标识符”。 –

1

this invo LVES没有外部包,

# thanks to Sotos for suggesting to use `unique(unlist(df$y))` instead of `LETTERS[1!:5]` 
sapply(unique(unlist(df$y)), function(j) as.numeric(grepl(j, df$y))) 
#  A B C D E 
#[1,] 1 0 0 0 0 
#[2,] 1 1 0 0 0 
#[3,] 0 0 1 0 0 
#[4,] 0 1 1 1 0 
#[5,] 0 0 0 0 1 
+2

'LETTERS'部分不好。你可以使用'unique(unlist(df $ y))'代替 – Sotos

+0

不能与'df $ x = rep(1,5)'或'df $ x = c(1,1,2,2,3) )'。它应该不会影响'df $ x'是什么。 –

+1

@ joel.wilson很好,我会做一些基准来看看它与其他“更好的”解决方案的比较,谢谢。 –

相关问题