本文是将kaggle Courses中 Categorical Variables | Kaggle进行了翻译并且加入自己的理解,如有地方不清楚,可以查阅原文

介绍

一个分类变量只能取到有限变量的值

  • 考虑一项调查,询问您吃早餐的频率并提供四个选项:“从不”、“很少”、“大多数天”或“每天”。在这种情况下,数据是分类的,因为答案属于一组固定的类别。
  • 如果人们回答关于他们拥有什么品牌的汽车的调查,则回答将分为“本田”、“丰田”和“福特”等类别。在这种情况下,数据也是分类的。

如果您尝试将这些变量插入 Python 中的大多数机器学习模型而不首先对其进行预处理,则会出现错误。在本教程中,我们将比较可用于准备分类数据的三种方法。

三种方法

1)删除分类变量

处理分类变量的最简单方法是简单地将它们从数据集中删除。这种方法只有在列不包含有用信息的情况下才会有效。

2)Ordinal Encoding (序数编码)

序数编码将每个唯一值分配给不同的整数。

image-20220206164702559

这种方法假定类别的排序:“从不”(0)<“很少”(1)<“大多数天”(2)<“每天”(3)

这个假设在这个例子中是有意义的,因为这些类别有一个无可争辩的排名。并非所有分类变量的值都具有明确的顺序,但我们将这些变量称为 ordinal variables (有序变量)。对于基于树的模型(如决策树和随机森林),你可以期望序数编码能够很好地处理序数变量。

3)One-Hot Encoding

One-Hot 编码会创建新列,指示原始数据中每个可能值的存在(或不存在)。为了理解这一点,我们将通过一个示例进行操作。

image-20220206164823292

在原始数据集中,“颜色”是一个分类变量,具有“红色”、“黄色”和“绿色”三个类别。对应的 one-hot 编码包含每个可能值的一列,以及原始数据集中每一行的一行。如果原始值为“红色”,我们在“红色”列中输入 1;如果原始值为“Yellow”,我们在“Yellow”列中输入 1,依此类推。

与序数编码相比,one-hot 编码不假设类别的排序。因此,如果分类数据中没有明确的顺序(例如,“红色”既不超过也不少于“黄色”),你可以期望这种方法特别有效。

我们将没有内在排名的分类变量称为nominal variables (名义变量)

如果分类变量具有大量值,则 One-hot 编码通常不会很好地执行。(通常不会将其用于具有超过 15 个不同值的变量)

这是因为如果数据量较大,例如有10,000行的数据,其中有一个分类变量,该变量有100个类,那么如果采用这种分类方法的话,则会多产生 99 * 10,000 的数据量,而如果使用Ordinal Encoding 则可以很好的避免产生较多数据量的问题。

代码实现

这部分的代码是根据原文中给出的示例代码结合配套的练习题中的代码得到的,如有错误,欢迎指出。

1)Drop columns with categorical data

X_train是从数据集中得到的pd.DataFrame对象,下文相同

直接通过select_dtypes方法即可获取到所有属性为object的列

drop_X_train = X_train.select_dtypes(include=['object'])

2)Ordinal encoding

Scikit-Learn的序列编码器是将序号编码器拟合到训练数据中的列中,为训练数据中出现的每个唯一值创建相应的整数标签。然后将给训练集分配的值一一分配给验证集中的对应数据。

前9行是为了处理在训练及测试时出现的一个问题:有可能验证集中包含训练集中没有出现的值,在这种情况下,编码器将抛出错误,因为没有值可以分配给它们。

例如,对于如下分类变量进行该操作就会报错:

Unique values in 'Condition2' column in training data: ['Norm' 'PosA' 'Feedr' 'PosN' 'Artery' 'RRAe']
Unique values in 'Condition2' column in validation data: ['Norm' 'RRAn' 'RRNn' 'Artery' 'Feedr' 'PosN']

验证数据中的“Condition2”列包含值“RRAN”和“RRNN”,但这些不显示在培训数据中,因此,如果我们尝试使用,代码将抛出错误。

对于无法安全编码的列,我们可以采用最简单的方法----直接删掉(当然,这并不是一种好方法,你可以使用其他方法处理这些数据)

完成这些工作后,我们就可以使用sklearn.preprocessing中的OrdinalEncoder类对我们的数据进行处理:

# 训练数据中的分类属性列
object_cols = [col for col in X_train.columns if X_train[col].dtype == "object"]

# 可以被安全编码的列
good_label_cols = [col for col in object_cols if 
                   set(X_valid[col]).issubset(set(X_train[col]))]
        
# 无法被安全编码的列
bad_label_cols = list(set(object_cols)-set(good_label_cols))

from sklearn.preprocessing import OrdinalEncoder

# Drop categorical columns that will not be encoded
label_X_train = X_train.drop(bad_label_cols, axis=1)
label_X_valid = X_valid.drop(bad_label_cols, axis=1)

# Apply ordinal encoder 
my_ordinalEncoder = OrdinalEncoder()
label_X_train[good_label_cols] = my_ordinalEncoder.fit_transform(label_X_train[good_label_cols]) 
label_X_valid[good_label_cols] = my_ordinalEncoder.transform(label_X_valid[good_label_cols])

3)One-hot encoding

这里我们需要注意的问题是,这种编码方式不适合分类数较多的变量,下面的代码中处理了分类数小于10的列,对于分类较多的列也是直接删除掉了(只是为了方便,当然你也可以使用序数编码来处理这些列)

我们使用Scikit-learning.preprocessingOneHotEncoder类进行One-hot encoding。有许多参数可用于自定义其行为

  • 当验证数据包含在训练数据中没有表示的类时,我们设置handle_unknown='ignore'以避免错误
  • 设置sparse = false可确保编码列作为numpy数组返回(而不是稀疏矩阵)。
from sklearn.preprocessing import OneHotEncoder

# Columns that will be one-hot encoded
low_cardinality_cols = [col for col in object_cols if X_train[col].nunique() < 10]

# Columns that will be dropped from the dataset
high_cardinality_cols = list(set(object_cols)-set(low_cardinality_cols))

# Apply one-hot encoder to each column with categorical data
oh_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
low_OH_X_train = pd.DataFrame(oh_encoder.fit_transform(X_train[low_cardinality_cols]))
low_OH_X_valid = pd.DataFrame(oh_encoder.transform(X_valid[low_cardinality_cols]))

# One-hot encoding removed index; put it back
low_OH_X_train.index = low_X_train.index
low_OH_X_valid.index = low_X_valid.index

# Remove categorical columns which will be replaced by one-hot encoding
left_X_train_cols = X_train.drop(object_cols, axis=1)
left_X_valid_cols = X_valid.drop(object_cols, axis=1)

# Add one-hot encoded columns to numerical features
OH_X_train = pd.concat([low_OH_X_train,left_X_train_cols], axis=1)
OH_X_valid = pd.concat([low_OH_X_valid,left_X_valid_cols], axis=1)
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐