数据预处理作为机器学习中的关键一环,这里介绍其中的Encoding Categorical Variables 类别变量编码

楔子
类别变量用于表示某种类别、属性,其值是一个名称或标签。对于类别变量而言,大体可分为两类:标称型、序数型
- 标称型:类别之间没有内在的顺序或等级关系,无法比较大小或排序。类别之间是平等的,仅仅表示不同的标签。例如:血型、性别、颜色、城市。对于颜色(红色、蓝色、黄色)而言,总不能说红色比黄色大吧?
- 序数型:类别之间存在清晰的、有意义的内在顺序或等级关系。类别之间可以进行比较、排序。例如:衣服尺码(S、M、L)。显然,L号的衣服比S号的衣服大,M号的衣服比L号的衣服小
但是对于类别变量而言,模型通常无法直接处理(字符串无法直接进行数学运算)。为此就需要将类别变量转化为数值变量
Label Encoding 标签编码
其编码方式很简单:将一维数据中的每个类别分别映射为一个整数。其适用特性:
目标变量:其非常适合分类任务中样本集的目标变量。因为,对于目标变量而言,数值仅仅是作为一个标识符,不用于任何地比较、排序用途。例如,识别照片中动物的样本集,其目标变量为动物类别——狗(映射为0)、猫(映射为1)、虎(映射为2)、鸟(映射为3)、鱼(映射为4)
序数型特征:对于序数型的类别特征,也可以使用其进行编码。因为可通过数值大小体现出类别间的排序规则。例如,衣服尺码(S、M、L)可以分别为映射为0、1、2
标称型特征:对于标称型的类别特征,则绝对不可以使用该方式进行编码。这样会人为的给该类别引入一个虚假的的大小、排序关系。例如,对颜色特征进行编码(红 -> 0, 绿 -> 1, 蓝 -> 2),则模型可能会错误的认为蓝色比红色大。显然这并不合理
可以通过Sklearn中的LabelEncoder来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import numpy as np import pandas as pd from sklearn.preprocessing import LabelEncoder
people_name = ['亚洲人', '非洲人', '亚洲人', '非洲人', '欧洲人']
le = LabelEncoder()
le.fit(people_name)
print("映射关系: 类别名称 <<----->> 类别编号 ") for i in range(le.classes_.size): print(f"{le.classes_[i]} <<----->> {i}")
people_index = le.transform(people_name)
print(f"基于类别名称的数据: {people_name}") print(f"基于类别编号的数据: {people_index}")
people_name2 = le.inverse_transform(people_index) print("基于类别编号的数据 还原为 基于类别名称 :", people_name2)
|
输出结果如下
1 2 3 4 5 6 7
| 映射关系: 类别名称 <<----->> 类别编号 亚洲人 <<----->> 0 欧洲人 <<----->> 1 非洲人 <<----->> 2 基于类别名称的数据: ['亚洲人', '非洲人', '亚洲人', '非洲人', '欧洲人'] 基于类别编号的数据: [0 2 0 2 1] 基于类别编号的数据 还原为 基于类别名称 : ['亚洲人' '非洲人' '亚洲人' '非洲人' '欧洲人']
|
One-Hot Encoding 独热编码
对于一个包含n种不同类别的一维数据,其使用一个n维的新数据来编码。其中,新数据中每个维度分别代表一种类别。这样当一个原始数据属于某个类别时,则其在新数据中的编码结果为:该类别对应维度的值为1,其余维度均为0。由于编码结果中只有一个1、其余皆为0,故得名 One-Hot
例如,对于颜色(红色、绿色、黑色)类别而言,这里有3种.我们可以规定:第1维表示绿色、第2维表示红色、第3维表示黑色。则:红色对应的编码即为 [0,1,0],绿色对应的编码即为 [1,0,0],黑色对应的编码即为 [0,0,1]
前面提到对于标称型的类别特征来说,如果使用Label Encoding标签编码会人为引入大小关系。因为彼此间的数值是可以比较大小的。而使用One-Hot Encoding独热编码就可以很好的解决这个问题。但该编码
会将该特征由原来的1个维度变为n个维度,显著增大数据维度,甚至出现维度灾难。同时,由于编码方式的特性,其编码结果中存在大量的0,会存在数据稀疏的问题
可以通过Sklearn中的OneHotEncoder来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| from sklearn.preprocessing import OneHotEncoder import numpy as np
data = np.array([ ['Red', 2.3], ['Green', 6.2], ['Blue', 7.34], ['Red', 13.83], ['Green', 5.5], ['Red', 7.2] ])
color_feature = data[:,0] print(f"color_feature #1: {color_feature}") print("-" * 100)
color_feature = color_feature.reshape(-1,1) print(f"color_feature #2:\n {color_feature}") print("-" * 100)
oneHotEncoder = OneHotEncoder(handle_unknown='ignore', sparse_output=False)
oneHotEncoder.fit(color_feature)
color_categories = oneHotEncoder.categories_ print(f"特征Color 独热编码的位置顺序及对应的类别: {color_categories[0]}") print("-" * 100)
encoded_color = oneHotEncoder.transform(color_feature)
new_color_feature = oneHotEncoder.inverse_transform(encoded_color)
feature_names = oneHotEncoder.get_feature_names_out(['颜色'])
for i in range(feature_names.size): print(f"{feature_names[i]} \t", end="" ) print("原始类别 \t 独热编码还原为类别")
m,_ = encoded_color.shape for i in range(m): print(f"{encoded_color[i,0]} \t\t {encoded_color[i,1]} \t\t {encoded_color[i,2]} \t\t {data[i,0]} \t\t {new_color_feature[i]}" )
|
输出结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| color_feature #1: ['Red' 'Green' 'Blue' 'Red' 'Green' 'Red'] ---------------------------------------------------------------------------------------------------- color_feature #2: [['Red'] ['Green'] ['Blue'] ['Red'] ['Green'] ['Red']] ---------------------------------------------------------------------------------------------------- 特征Color 独热编码的位置顺序及对应的类别: ['Blue' 'Green' 'Red'] ---------------------------------------------------------------------------------------------------- 颜色_Blue 颜色_Green 颜色_Red 原始类别 独热编码还原为类别 0.0 0.0 1.0 Red ['Red'] 0.0 1.0 0.0 Green ['Green'] 1.0 0.0 0.0 Blue ['Blue'] 0.0 0.0 1.0 Red ['Red'] 0.0 1.0 0.0 Green ['Green'] 0.0 0.0 1.0 Red ['Red']
|
参考文献
- 机器学习 周志华著
- 机器学习公式详解 谢文睿、秦州著
- 图解机器学习和深度学习入门 山口达辉、松田洋之著