如何对R中的几个分类变量进行热编码

时间:2018-02-06 18:16:11

标签: r one-hot-encoding

我正在研究预测问题,而且我在R中构建了一个决策树,我有几个分类变量,我希望在我的培训和测试中对它们进行一次热编码组。 我设法通过以下方式对我的训练数据进行了处理:

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

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

6 个答案:

答案 0 :(得分:17)

我建议在插入符包中使用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

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

答案 1 :(得分:7)

代码

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))

答案 2 :(得分:5)

这是一种简单的解决方案,无需包装即可对您的类别进行一次编码。

解决方案

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

P.S。 通常最好在培训和测试数据中包括所有类别。但这不关我的事。

答案 3 :(得分:1)

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

<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/eLibrary-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

答案 4 :(得分:0)

我有一个整洁的解决方案,可以在整个过程中为用户提供更多控制权。我的解决方案具有一个JavaScript组件,该组件拆分每个单元格并将列名称存储为JSON。然后,我使用tidyjson :: spread_all函数将JSON传播到不同的列名中。

您需要另存为encoder.js的JavaScript组件:

function oneHotSplitEncoder(inputStrArray, prefix, spliterRegExStr, spliterRegExStrOptions){
  if (Array.isArray(inputStrArray)) {
    return inputStrArray.map(function(str) {
      try{
        if(typeof(str) === 'string' && typeof(spliterRegExStr)==='string' && typeof(spliterRegExStrOptions)==='string' && typeof(prefix) === 'string'){
          return JSON.stringify(
            str.split(RegExp(spliterRegExStr, spliterRegExStrOptions))
               .reduce(function(p, component){
                 p[prefix + component] = 1;
                   return p;
               }, {})
          )
        } else {
          return NaN;
        }
      } catch (e) {
        console.warn("\n"+e+"\n"+str+"\n"+spliterRegExStr+' string expected')
        return NaN;
      }
    });
  } else {    
    console.warn("Error: oneHotSplitEncoder function needs array type inputs");
    return NaN;
  }
};

R个组件:

library('dplyr')
js <<- V8::v8(); 
js$source("encoder.js");
oneHotSplitEncoder <- function(inputStrArray, prefix, spliterRegExStr, spliterRegExStrOptions)
  js$call("oneHotSplitEncoder", inputStrArray, prefix, spliterRegExStr, spliterRegExStrOptions)

df_one_hot <- df %>%
  mutate(
    fooColumn = oneHotSplitEncoder(fooColumn, 'prefix.', ' *[,;] *', 'g')
  ) %>%
  bind_cols(tidyjson::spread_all(.$fooColumn) %>% select(-document.id) %>% replace(is.na(.), 0))

答案 5 :(得分:0)

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

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"))