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

Posted

技术标签:

【中文标题】如何在 R 中对多个分类变量进行一次热编码【英文标题】:How to one hot encode several categorical variables in R 【发布时间】:2018-07-16 21:29:27 【问题描述】:

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

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

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

【问题讨论】:

编码是指创建虚拟变量吗? 用同样的方法。有什么不同? 是的,我的意思是创建虚拟变量:对于每个分类变量,我需要创建尽可能多的虚拟变量,因为变量中有不同的类别。 @Gregor 不同的是,某些类别可能存在于测试集中而不是训练中,而且假人的顺序也很重要,而且这两组不同跨度> @xeco 我建议你在 R 中寻找 vtreat 包 【参考方案1】:

我推荐使用 caret 包中的 dummyVars 函数:

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

示例source

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

【讨论】:

我发现插入符号方法(使用 dummyVars)比 mltools 包中的 one_hot() 函数快约 73%。使用microbenchmark 包和iris 数据集,插入符号方法在0.025 毫秒内完成,而one_hot() 方法在0.095 毫秒内完成。 @DaleKube 您是否将data.frame(predict(dmy, newdata = customers)) 包含在您的基准测试中?显然dummyVars 本身不会给你真正的假人 如果你有一个包含不同变量的数据帧,并且你想只对其中的一些进行一次热编码,你需要使用类似dummyVars(" ~ VARIABLE1 + VARIABLE2", data = customers) @raffamaiden 是的,我包含了 predict() 调用和对 data.frame 的转换。 这是一个使用 recipes (tidymodels) 包的替代方案:blog.datascienceheroes.com/…【参考方案2】:

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

解决方案

model.matrix(~0+category)

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

示例

这是一个使用 iris 数据集的示例。

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

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

【讨论】:

这适用于一个变量,但如果有多个分类变量,则不会命中所有类别。 真,见***.com/questions/62116652/…【参考方案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))

【讨论】:

【参考方案4】:

您好,这是我的版本,此函数对所有属于 'factors' 的分类变量进行编码,并删除其中一个虚拟变量以避免 虚拟变量陷阱并返回一个带有编码的新数据帧:-

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)

【讨论】:

【参考方案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"))

【讨论】:

以上是关于如何在 R 中对多个分类变量进行一次热编码的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中对数据框列进行一次热编码?

使用 sklearn 或 pandas 进行一次热编码后,如何在混合数据集(数值 + 分类)上应用 KNN

一次对多列进行一次热编码并附加到主数据集?

如何在使用 sklearn 进行一次热编码后给出列名?

如何在使用 sklearn 进行一次热编码后给出列名?

Julia DataFrames - 如何进行一次热编码?