如何在 R 中对多个分类变量进行热编码

How to one hot encode several categorical variables in R

提问人:xeco 提问时间:2/7/2018 最后编辑:Esteban PSxeco 更新时间:11/21/2023 访问量:89350

问:

我正在研究一个预测问题,我正在 R 中构建一个决策树,我有几个分类变量,我想在我的训练和测试集中一致地对它们进行一热编码。 我设法在我的训练数据上做到了:

temps <- X_train
tt <- subset(temps, select = -output)
oh <- data.frame(model.matrix(~ . -1, tt), CLASS = temps$output)

但是我找不到在我的测试集上应用相同编码的方法,我该怎么做?

r one-hot-encoding

评论

1赞 Esteban PS 2/7/2018
通过编码,您的意思是创建虚拟变量吗?
0赞 Gregor Thomas 2/7/2018
以同样的方式做。有什么不同?
0赞 xeco 2/7/2018
是的,我的意思是创建假人:对于每个分类变量,我需要创建尽可能多的虚拟变量,因为变量中有不同的类别。
1赞 xeco 2/7/2018
@Gregor不同的是,某些类别可能存在于测试集中,而不是出现在训练中,而且假人的顺序也很重要,两组也不会相同
1赞 Rushabh Patel 2/7/2018
@xeco 我建议你在 R 中寻找 vtreat 包

答:

48赞 Esteban PS 2/7/2018 #1

我建议在插入符号包中使用 dummyVars 函数:

library(caret)

customers <- data.frame(
  id=c(10, 20, 30, 40, 50),
  gender=c('male', 'female', 'female', 'male', 'female'),
  mood=c('happy', 'sad', 'happy', 'sad','happy'),
  outcome=c(1, 1, 0, 0, 0))
customers
id gender  mood outcome
1 10   male happy       1
2 20 female   sad       1
3 30 female happy       0
4 40   male   sad       0
5 50 female happy       0


# dummify the data
dmy <- dummyVars(" ~ .", data = customers)
trsf <- data.frame(predict(dmy, newdata = customers))
trsf
id gender.female gender.male mood.happy mood.sad outcome
1 10             0           1          1        0       1
2 20             1           0          0        1       1
3 30             1           0          1        0       0
4 40             0           1          0        1       0
5 50             1           0          1        0       0

示例

将相同的过程应用于训练集和验证集。

评论

11赞 Dale Kube 12/19/2018
我发现插入符号方法(使用 dummyVars)比包中的函数快约 73%。使用包和数据集,插入符号方法在 0.025 毫秒内完成,而该方法在 0.095 毫秒内完成。one_hot()mltoolsmicrobenchmarkirisone_hot()
1赞 robertspierre 4/22/2019
@DaleKube,您在基准测试中包括了这些内容?显然,仅凭这一点不会给你真正的假人data.frame(predict(dmy, newdata = customers))dummyVars
3赞 robertspierre 4/22/2019
如果你有一个具有不同变量的数据帧,并且你只想对其中的一些变量进行单热编码,则需要使用类似dummyVars(" ~ VARIABLE1 + VARIABLE2", data = customers)
1赞 Dale Kube 4/23/2019
@raffamaiden是的,我包括了predict()调用和对data.frame的转换。
0赞 Pablo Casas 7/24/2019
这是使用 recipes (tidymodels) 包的替代方案:blog.datascienceheroes.com/...
3赞 Shubham Joshi 9/18/2018 #2

嗨,这是我的相同版本,此函数对所有作为“因子”的分类变量进行编码,并删除其中一个虚拟变量以避免虚拟变量陷阱,并返回一个编码为:-

onehotencoder <- function(df_orig) {
  df<-cbind(df_orig)
  df_clmtyp<-data.frame(clmtyp=sapply(df,class))
  df_col_typ<-data.frame(clmnm=colnames(df),clmtyp=df_clmtyp$clmtyp)
  for (rownm in 1:nrow(df_col_typ)) {
    if (df_col_typ[rownm,"clmtyp"]=="factor") {
      clmn_obj<-df[toString(df_col_typ[rownm,"clmnm"])] 
      dummy_matx<-data.frame(model.matrix( ~.-1, data = clmn_obj))
      dummy_matx<-dummy_matx[,c(1,3:ncol(dummy_matx))]
      df[toString(df_col_typ[rownm,"clmnm"])]<-NULL
      df<-cbind(df,dummy_matx)
      df[toString(df_col_typ[rownm,"clmnm"])]<-NULL
    }  }
  return(df)
}

评论

1赞 schotti 7/19/2023
由于删除了其中一个虚拟变量,因此可能不应将函数称为“OneHotEncoder”。“one hot”表示一个虚拟人始终是“hot”,即 1 或 TRUE。
0赞 qwr 10/10/2023
我假设的“虚拟变量陷阱”是独热编码的完美多重共线性。虚拟变量函数应像 dummyVars 一样自动删除一个变量。
22赞 Roman 10/21/2018 #3

法典

library(data.table)
library(mltools)
customers_1h <- one_hot(as.data.table(customers))

结果

> customers_1h
id gender_female gender_male mood_happy mood_sad outcome
1: 10             0           1          1        0       1
2: 20             1           0          0        1       1
3: 30             1           0          1        0       0
4: 40             0           1          0        1       0
5: 50             1           0          1        0       0

数据

customers <- data.frame(
  id=c(10, 20, 30, 40, 50),
  gender=c('male', 'female', 'female', 'male', 'female'),
  mood=c('happy', 'sad', 'happy', 'sad','happy'),
  outcome=c(1, 1, 0, 0, 0))
31赞 D A Wells 8/28/2019 #4

这是一个简单的解决方案,可以不使用包对类别进行单热编码。

溶液

model.matrix(~0+category)

它需要您的分类变量是一个因子。训练数据和检验数据中的因子水平必须相同,请检查 和 。如果某些级别没有出现在您的测试集中,也没关系。levels(train$category)levels(test$category)

下面是使用鸢尾花数据集的示例。

data(iris)
#Split into train and test sets.
train <- sample(1:nrow(iris),100)
test <- -1*train

iris[test,]

    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
34           5.5         4.2          1.4         0.2    setosa
106          7.6         3.0          6.6         2.1 virginica
112          6.4         2.7          5.3         1.9 virginica
127          6.2         2.8          4.8         1.8 virginica
132          7.9         3.8          6.4         2.0 virginica

model.matrix()为因子的每个水平创建一个列,即使数据中不存在该列。零表示它不是那个级别,一个表示它是。添加零指定不需要截距或引用级别,并且等效于 -1。

oh_train <- model.matrix(~0+iris[train,'Species'])
oh_test <- model.matrix(~0+iris[test,'Species'])

#Renaming the columns to be more concise.
attr(oh_test, "dimnames")[[2]] <- levels(iris$Species)


  setosa versicolor virginica
1      1          0         0
2      0          0         1
3      0          0         1
4      0          0         1
5      0          0         1

附言 通常,最好在训练和测试数据中包含所有类别。但这不关我的事。

评论

0赞 Gregor Thomas 3/17/2021
这适用于一个变量,但如果有多个分类变量,则不会命中每个类别。
2赞 Aydin K. 3/30/2021
没错,请参阅 stackoverflow.com/questions/62116652/...
4赞 Angel 3/26/2021 #5

如果您不想使用任何外部包,我有自己的函数:

one_hot_encoding = function(df, columns="season"){
  # create a copy of the original data.frame for not modifying the original
  df = cbind(df)
  # convert the columns to vector in case it is a string
  columns = c(columns)
  # for each variable perform the One hot encoding
  for (column in columns){
    unique_values = sort(unique(df[column])[,column])
    non_reference_values  = unique_values[c(-1)] # the first element is going 
                                                 # to be the reference by default
    for (value in non_reference_values){
      # the new dummy column name
      new_col_name = paste0(column,'.',value)
      # create new dummy column for each value of the non_reference_values
      df[new_col_name] <- with(df, ifelse(df[,column] == value, 1, 0))
    }
    # delete the one hot encoded column
    df[column] = NULL

  }
  return(df)
}

你像这样使用它:

df = one_hot_encoding(df, c("season"))

评论

0赞 pd441 10/13/2022
这是最好的答案!