在实际中,采集的数据往往不完整、不一致,并可能包含许多错误。数据预处理(Data Preprocessing)是一种数据挖掘技术,对原始数据进行处理以便进一步分析。
本文介绍分类数据(Categorical Data)的处理。
在处理分类数据时,需要区分定类(nominal)特征和定序(ordinal)特征。
- 定类特征:不同类别,相互间比较没有意义。如姓名,性别,水果等。
- 定序特征:不同类别,相互间可以比较排序。如非常满意/一般满意/不满意,小型/中型/大型等。和数字特征不同,两者之差一般没有意义。
以下的 df
变量代表了 T 恤的一些特征:
1 | import pandas as pd |
其中包括定类特征 color(颜色)
、定序特征 size(尺码)
和数字特征 price(价格)
。最后一列为分类类别 label
。
定序特征的映射
为了确保学习算法能够识别定序特征,需要手动将分类字符串映射(Mapping)为整型。
如上例的 T 恤尺码,假设已知排序 $XL > L > M$,可以进行如下转换:
1 | size_mapping = { |
对于反向转换,创建反向词典然后进行 map
即可:
1 | for k, v in size_mapping.items()} inv_size_mapping = {v: k |
类标签的编码
许多机器学习库要求类标签编码(Encoding)为整数值;虽然 scikit-learn 已默认集成了此处理机制,但是建议养成手动转换的习惯。
类标签的数字大小没有任何意义,因此可以直接使用枚举进行标签转换:
1 | import numpy as np |
将类标签编码为整数:
1 | 'classlabel'] = df['classlabel'].map(class_mapping) df[ |
反向转换:
1 | for k, v in class_mapping.items()} inv_class_mapping = {v: k |
通过 sklearn.preprocessing.LabelEncoder 可以更简便地将类标签编码为整数:
1 | from sklearn.preprocessing import LabelEncoder |
反向转换:
1 | class_le.inverse_transform(y) |
定类特征的独热编码
独热编码的原理
在介绍独热编码(One-Hot Encoding)之前,先说明一下为什么不用之前章节的编码方式。
如果按照之前的方式进行编码:
1 | 'color', 'size', 'price']].values X = df[[ |
编码结果为:
- blue = 0
- green = 1
- red = 2
如果把上述数据提供给分类器,则会发生处理分类数据的最常见错误之一:虽然我们知道 0、1、2 这些数字不代表大小,但是算法并不知道。因此算法在学习过程中,会默认将其关联起来,即假定 red > green > blue
。这样处理后,算法仍然能产生一定的结果,但其性能会受影响。
独热编码的思想为为每一个值创建一个新的特征。对于上述的例子,可以把颜色特征转换为三个新的特征:blue
、green
和 red
,然后使用二进制值标记。对于 blue
样本而言,编码为 blue=1, green=0, red=0
。
独热编码的实现
使用 sklearn.preprocessing.OneHotEncoder 对特征 color
进行编码,返回一个稀疏矩阵:
1 | from sklearn.preprocessing import OneHotEncoder |
另一个更方便的独热编码方法是 pandas 中的 get_dummies
方法,转换 DataFrame 的指定字符串列,其他列保持不变:
1 | 'price', 'color', 'size']]) pd.get_dummies(df[[ |
独热编码的相关性
当使用热门的编码数据集时,必须记住它引入了多重共线性,即某个变量可以由其他变量线性预测得到(如上面的矩阵,若已知 blue
、green
、red
中的任意两个,可以得到最后一个)。这会对某些操作(如矩阵求逆)造成影响。
为了减少变量之间的相关性,我们可以简单地从独热编码数组中删除一个特征列。
sklearn.preprocessing.OneHotEncoder
不提供特征列删除方法,需要转换为 numpy 数组后进行切片:
1 | 0]) ohe = OneHotEncoder(categorical_features=[ |
pandas 中的 get_dummies
提供参数 drop_first
,可以很方便地删除首个特征列:
1 | 'price', 'color', 'size']], pd.get_dummies(df[[ |