R:基于分类变量*列表*创建虚拟变量[重复]

Posted

技术标签:

【中文标题】R:基于分类变量*列表*创建虚拟变量[重复]【英文标题】:R: create dummy variables based on a categorical variable *of lists* [duplicate] 【发布时间】:2017-05-31 01:36:01 【问题描述】:

我有一个数据框,其中包含一个分类变量,其中包含 lists 的字符串,长度可变(这很重要,否则这个问题将与 this 或 this 重复),例如:

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.time 对它们进行了测试,该数据集包含 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

所以这是我将标记为接受的方法。

【问题讨论】:

data.frame(x = df$x, t(sapply(df$y, function(l)table(factor(l, levels = LETTERS[1:5]))))) @alistaire 也许是levels = unique(unlist(df$y)) 而不是LETTERS[1:5] ? @Sotos 我有,但认为这是更少的计算。最好的方法是将其存储为单独的变量,但这需要第二行... @alistaire True 【参考方案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

除了你在cmets中提到的情况,我们可以使用reshape2中的dcast,因为它比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

【讨论】:

谢谢,看看当 df$x = rep(1, 5) 时会发生什么。 “错误:行 (1, 2)、(3, 5)、(4, 7) 的标识符重复” 在这种情况下您的预期结果是什么?类似df %&gt;% unnest(y) %&gt;% group_by(x) %&gt;% mutate(new = 1:n()) %&gt;% spread(y, x, fill = 0)? 保留原始 x 列的相同结果。这在原始 df 上给出了“错误:行 (1, 2) 的重复标识符”。 它适用于df$x = rep(1, 5) 案例。在原始的 df$x = 1:5 案例中,它给出“错误:行 (1, 2) 的重复标识符”。 group_by()之前尝试mutate(new = 1:n())【参考方案2】:

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

【讨论】:

这令人印象深刻且速度超快(我的 PC 上大约 1M 行具有大约 350 个唯一值只需几秒钟)。您是否有不需要全新包装的答案?谢谢。 @GioraSimchoni 好像有人在没有包裹的情况下回答了它 @GioraSimchoni;我猜一个基本的选择是table(rep(df$x, lengths(df$y)), unlist(df$y))? 不适用于df$x = rep(1,5)df$x = c(1,1,2,2,3)df$x 是什么无关紧要。 对不起@akrun,我指的是alexis_laz评论。【参考方案3】:

这不涉及外部包,

# 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

【讨论】:

LETTERS 部分不好。你可以改为unique(unlist(df$y)) 不适用于df$x = rep(1,5)df$x = c(1,1,2,2,3)df$x 是什么无关紧要。 @joel.wilson 效果很好,我会做一些基准测试,看看它与其他“更高级”的解决方案相比如何,谢谢。 @GioraSimchoni 表现如何?

以上是关于R:基于分类变量*列表*创建虚拟变量[重复]的主要内容,如果未能解决你的问题,请参考以下文章

[R] 中的 One-Hot 编码 |分类到虚拟变量

R语言基于R语言的数据挖掘之决策树

R:根据OR条件创建具有多个级别的新变量[重复]

iOS开发:分类添加属性,不会添加实例变量

R:基于一个列的值存在于另一列中,生成虚拟变量

从互斥虚拟变量创建分类变量