类别数据
与数值特征不同,类别数据往往更难被计算机理解,主要分为序数和标称。
序数具有顺序,比如衣服尺码中有XL>L>M等
标称不含任何顺序,特征之间相互独立。
处理序数特征
为了让算法正确解读序数特征,我们需要用整数来表示。我们可以定义映射关系,训练后再反向映射,比如:
import pandas as pd
df = pd.DataFrame([['green', 'M', 10.1, 'class2'],
['red', 'L', 13.5, 'class1'],
['blue', 'XL', 15.3, 'class2']])
df.columns = ['color', 'size', 'price', 'classlabel']
size_mapping = {
'XL': 3,
'L': 2,
'M': 1}
df['size'] = df['size'].map(size_mapping)
查看映射关系:
inv_size_mapping = {
v: k for k, v in size_mapping.items()}
df['size'].map(inv_size_mapping)
处理标称数据
与上述不同的是,标称数据没有顺序,所以可以更方便地自动获取映射:
# 获取这列数据,用unique去除重复,再为每种数据映射到数字
class_mapping = {
label: idx for idx, label in enumerate(np.unique(df['classlabel']))}
df['classlabel'] = df['classlabel'].map(class_mapping)
# 反向映射
inv_class_mapping = {
v: k for k, v in class_mapping.items()}
df['classlabel'] = df['classlabel'].map(inv_class_mapping)
在ski-learn中也可以直接调用LabelEncoder类来实现:
from sklearn.preprocessing import LabelEncoder
class_le = LabelEncoder()
y = class_le.fit_transform(df['classlabel'].values)
# y = array([1, 0, 1])
# 反向映射
class_le.inverse_transform(y)
# >> array(['class2', 'class1', 'class2'], dtype=object)
独热编码
虽然我们将标称数据映射成了整数,并声称其没有顺序,但机器学习算法会认为1大于0,2大于1,从而影响算法的偏好。
解决这个问题的常用方案是独热编码。我们将每一个特征映射成同样大小的向量。比如:
array = [1, 0, 1, 2]
将会变成:
array2 = [ [0, 1, 0],
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]]
注意
使用独热编码要小心多重共线性,即所有的特征呈现出了一个相关性(如果n种取值中前n-1个都预测为0,那第n个一定是1)。如果发生了特征高度相关,会导致矩阵求逆是很难的,且会降低数据稳定性。
所以,我们可以简单粗暴地删除一个特征列,不用担心丢失信息。
代码实现
最方便的是pandas:
pd.get_dummies(df[['price', 'color', 'size']], drop_first=True)
或者用sklearn中的OneHotEncoder:
from sklearn.preprocessing import OneHotEncoder
X = df[['color', 'size', 'price']].values
color_ohe = OneHotEncoder()
# 此处只转换单列
color_ohe.fit_transform(X[:, 0].reshape(-1, 1)).toarray()
如果想转化多列,ColumnTransformer支持同时对不同列使用不同的转化策略。
# 导入ColumnTransformer类,它允许对数据集的不同列应用不同的转换器
from sklearn.compose import ColumnTransformer
# 从数据帧df中选择'color', 'size', 'price'三列,并将它们转换为NumPy数组赋值给X
X = df[['color', 'size', 'price']].values
# 定义ColumnTransformer实例c_transf
c_transf = ColumnTransformer([
# 定义第一个转换器:对第0列('color'列)进行独热编码
('onehot', OneHotEncoder(), [0]),
# 定义第二个转换器:对于第1列和第2列('size'和'price'列)不进行任何转换,直接保留原数据
# 'nothing'是转换器的名称,'passthrough'是一个特殊的转换器,表示这些列的数据将被直接传递,不做任何修改
('nothing', 'passthrough', [1, 2])
])
# 使用X数据拟合c_transf,并对其进行转换
# fit_transform()方法首先会根据X数据拟合转换器,然后应用这些转换器对X数据进行转换
c_transf.fit_transform(X).astype(float)
降维
数据的维度直接决定了训练模型的难度和时间成本,维度过高也有过拟合的风险。主要有两种降维技术:特征选择和特征提取。
序列特征选择算法
经典的序列特征选择算法是序列后向选择(SBS),使用贪心的思想,每次选择一个性能损失最小的特征并删除,直到新的特征子空间包含了需要的特征数量。
(1)假设 d d d为特征空间 X d X_{d} Xd的维数
(2)找到影响最小的特征 x − ∈ X d x^{-}\in{X_{d}} x−∈Xd, x − = a r g m a x J ( X d − x ) x^{-}=argmaxJ(X_{d}-x) x−=argmaxJ(Xd−x) ------max指找到最大值,arg指找到对应使J最大的 x x x。
(3)从特征集中去除特征 x − x^{-} x−: X d − 1 = X d − x − X_{d-1}=X_{d}-x^{-} Xd−1