从分类变量中创建新的虚拟变量列

40

我有几个包含75000个观测值的数据集和一个可以取值为0-4的变量type。 我想为每个类型添加五个新的虚拟变量到每个数据集中。 我能想到的最好方法如下:

# For the 'binom' data set create dummy variables for all types in all data sets
binom.dummy.list<-list()
for(i in 0:4){
    binom.dummy.list[[i+1]]<-sapply(binom$type,function(t) ifelse(t==i,1,0))
}

# Add and merge data
binom.dummy.df<-as.data.frame(do.call("cbind",binom.dummy.list))
binom.dummy.df<-transform(binom.dummy.df,id=1:nrow(binom))
binom<-merge(binom,binom.dummy.df,by="id")

虽然这种方法可行,但速度非常慢(合并函数甚至有时会崩溃)。是否有更有效的方法来实现这个功能?也许这个功能是我不熟悉的包的一部分?


ifelse是向量化的,所以如果我正确理解你的代码,你不需要那个sapply。而且我不会使用merge - 我会使用SQLite或PostgreSQL。一些样本数据也会有所帮助 :-) - Vince
8个回答

49

R有一个“子语言”可以将公式转化为设计矩阵,在这种语言的精神下,您可以利用它。它快速而简洁。例如:您有一个基数预测变量x,一个分类预测变量catVar和一个响应y。

> binom <- data.frame(y=runif(1e5), x=runif(1e5), catVar=as.factor(sample(0:4,1e5,TRUE)))
> head(binom)
          y          x catVar
1 0.5051653 0.34888390      2
2 0.4868774 0.85005067      2
3 0.3324482 0.58467798      2
4 0.2966733 0.05510749      3
5 0.5695851 0.96237936      1
6 0.8358417 0.06367418      2

你只需要这样做

> A <- model.matrix(y ~ x + catVar,binom) 
> head(A)
  (Intercept)          x catVar1 catVar2 catVar3 catVar4
1           1 0.34888390       0       1       0       0
2           1 0.85005067       0       1       0       0
3           1 0.58467798       0       1       0       0
4           1 0.05510749       0       0       1       0
5           1 0.96237936       1       0       0       0
6           1 0.06367418       0       1       0       0

完成。


6
有没有一种简单的方法可以实现相反的操作——即您有虚拟变量,但想将它们合并为一个变量? - Misha
1
请注意,如果更改使用的对比类型,则会得到不同的结果。此外,有序和无序因子会得到不同的答案。R中的默认对比设置为options(contrasts = c("contr.treatment", "contr.poly"))。请参阅?contrasts以增加您的困惑。 - geneorama
请注意,此处的示例有5个类别,因为索引从0开始 sample(0:4, 1e5 , TRUE)。我认为在基本R中不可能自动生成所有虚拟变量的级别。这个特定的例子碰巧省略了任何0的样本,在模型矩阵中会出现一行零。 - geneorama
1
这个方法会删除带有NAs的行,所以我更喜欢Joshua Ullrich的答案。为了澄清geneorama的观点,在一个变量的n个级别中,你只需要n-1个虚拟变量来表示信息。(如果出于某种原因你想要修改model.matrix()来显式地表示所有列,你可以添加一个没有成员的参考级别,如levels(binom$catVar) <- c("dummy", levels(binom$catVar)); A <- model.matrix(y ~ x + catVar,binom, contrasts = "contr.treatment")但这种冗余似乎是有风险的,如果你正在进行建模的话。) - MattBagg
如果您不想进行拦截,那么请使用 A <- model.matrix(y ~ x + catVar -1, binom) - Manoj Kumar

24

德鲁,这个更快,不会导致任何崩溃。

> binom <- data.frame(data=runif(1e5),type=sample(0:4,1e5,TRUE))
> for(t in unique(binom$type)) {
+   binom[paste("type",t,sep="")] <- ifelse(binom$type==t,1,0)
+ }
> head(binom)
        data type type2 type4 type1 type3 type0
1 0.11787309    2     1     0     0     0     0
2 0.11884046    4     0     1     0     0     0
3 0.92234950    4     0     1     0     0     0
4 0.44759259    1     0     0     1     0     0
5 0.01669651    2     1     0     0     0     0
6 0.33966184    3     0     0     0     1     0

2
不错的解决方案。我可以建议,在“粘贴”之前加入一点“make.names”,以防级别名称包含某些有争议的字符。 - agenis

16

尝试使用model.matrix()函数?

> binom <- data.frame(data=runif(1e5),type=sample(0:4,1e5,TRUE))
> head(binom)
       data type
1 0.1412164    2
2 0.8764588    2
3 0.5559061    4
4 0.3890109    3
5 0.8725753    3
6 0.8358100    1
> inds <- model.matrix(~ factor(binom$type) - 1)
> head(inds)
  factor(binom$type)0 factor(binom$type)1 factor(binom$type)2 factor(binom$type)3 factor(binom$type)4
1                   0                   0                   1                   0                   0
2                   0                   0                   1                   0                   0
3                   0                   0                   0                   0                   1
4                   0                   0                   0                   1                   0
5                   0                   0                   0                   1                   0
6                   0                   1                   0                   0                   0

1
如果类型变量也有NA值怎么办?我们如何将NA值保留为单独的变量,除了1和0之外? - Scott Davis
将NA值编码为除NA之外的其他值。 - user3720516

3
如果您愿意使用 data.table 包,mltools 提供了一个 one_hot() 方法。
library(data.table)
library(mltools)

binom <- data.table(y=runif(1e5), x=runif(1e5), catVar=as.factor(sample(0:4,1e5,TRUE)))
one_hot(binom)

                 y          x catVar_0 catVar_1 catVar_2 catVar_3 catVar_4
     1: 0.90511891 0.83045050        0        0        1        0        0
     2: 0.91375984 0.73273830        0        0        0        1        0
     3: 0.01926608 0.10301409        0        0        1        0        0
     4: 0.48691138 0.24428157        0        1        0        0        0
     5: 0.60660396 0.09132816        0        0        1        0        0
    ---                                                                   
 99996: 0.12908356 0.26157731        0        1        0        0        0
 99997: 0.96397273 0.98959000        0        1        0        0        0
 99998: 0.16818414 0.37460941        1        0        0        0        0
 99999: 0.72610508 0.72055867        1        0        0        0        0
100000: 0.89710998 0.24155507        0        0        0        0        1

使用方法

one_hot(dt, cols = "auto", sparsifyNAs = FALSE, 
        naCols = FALSE, dropCols = TRUE,
        dropUnusedLevels = FALSE)

哪些列应该进行独热编码?cols="auto"会对所有无序因子列进行编码。因此,下面的命令是等效的。只有在数据表中包含不应编码的因子时,这才很重要。
one_hot(binom, cols="catVar")

2

recipes包也可以用来完成这项任务,下面的例子非常冗长,但只要添加更多的预处理步骤,它就可以变得非常简洁。

library(recipes)

binom <- data.frame(y = runif(1e5), 
                    x = runif(1e5),
                    catVar = as.factor(sample(0:4, 1e5, TRUE))) # use the example from gappy
head(binom)

new_data <- recipe(y ~ ., data = binom) %>% 
  step_dummy(catVar) %>% # add dummy variable
  prep(training = binom) %>% # apply the preprocessing steps (could be more than just adding dummy variables)
  bake(newdata = binom) # apply the recipe to new data
head(new_data)

其他步骤示例包括 step_scale、step_center、step_pca 等。

1

我使用model.matrix()函数时遇到了一些问题,因为它会无缘由地省略一些因子水平。但是,我发现library(fastDummies)中的这个简单函数很好用:

被转化为二进制虚拟变量的列必须是分类变量。

fastDummies::dummy_cols(fastDummies_example, select_columns = "numbers", remove_selected_columns = "numbers")


0

nnet包用于单层神经网络(不理解因素),其中有一个转换命令:class.ind。


0
你可以使用名为 dummies 的包。
binom <- data.frame(y=runif(1e5), x=runif(1e5), catVar=as.factor(sample(0:4,1e5,TRUE)))
head(binom)

          y          x catVar
1 0.4143348 0.09721401      1
2 0.3140782 0.54340539      3
3 0.1262037 0.51820499      2
4 0.7159850 0.13167720      3
5 0.8203528 0.94116026      3
6 0.2169781 0.82020216      1

解决方案:

library(dummies)
binom<-dummy.data.frame(binom)
head(binom)

          y          x catVar0 catVar1 catVar2 catVar3 catVar4
1 0.4143348 0.09721401       0       1       0       0       0
2 0.3140782 0.54340539       0       0       0       1       0
3 0.1262037 0.51820499       0       0       1       0       0
4 0.7159850 0.13167720       0       0       0       1       0
5 0.8203528 0.94116026       0       0       0       1       0
6 0.2169781 0.82020216       0       1       0       0       0

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