使用机器学习时,会存在不同的特征类型:连续型特征和离散型特征。
针对连续性特征,我们通常将其线性缩放到[-1, 1]区间或者缩放到均值为0,方差为1的范围。
但是,特征并不总是连续值,而有可能是分类值、离散值。因此,我们也需要对离散值进行特征编码数据预处理。
离散特征的编码分为两种情况:
- 如果离散特征的取值之间没有大小的意义,比如,颜色:[红色, 蓝色, 黄色],那么就使用独热编码(one-hot)编码,即,红色:
1 0 0
,黄色:0 1 0
,蓝色:0 0 1
。 - 如果离散特征的取值有大小的意义,比如,衣服的型号:[X, XL, XXL],那么就使用标签编码,对数值进行映射:
{X:1, XL:2, XXL:3}
。
独热编码
什么是独热编码
独热编码(one-hot encoding), 直观来说就是有多少个状态就有多少比特,而且只有一个比特为1,其他全为0的一种码制。
针对独热编码,该离散特征有多少取值,就用多少维来表示该特征。
独热编码优缺点
优点:
独热编码解决了分类器不好处理属性数据的问题,在一定程度上也起到了扩充特征的作用。它的值只有0和1,不同的类型存储在垂直的空间。
缺点:
当类别的数量很多时,特征空间会变得非常大。在这种情况下,一般可以用PCA来减少维度。而且独热编码+PCA这种组合在实际中也非常有用。
如何判断用不用独热编码
用:
独热编码用来解决类别型数据的离散值问题。
不用:
将离散型特征进行独热编码的作用,是为了让距离计算更合理,但如果特征是离散的,并且不用独热编码就可以很合理的计算出距离,那么就没必要进行独热编码。
有些基于树的算法在处理变量时,并不是基于向量空间度量,数值只是个类别符号,所以不用进行独热编码。
树模型不太需要独热编码,对于决策树来说,独热编码的本质是增加树的深度。
总的来说,要是独热编码的类别数目不太多,建议优先考虑。
如何判断需不需要归一化
- 需要: 基于参数的模型或基于距离的模型,都是要进行特征的归一化。
- 不需要:基于树的方法是不需要进行特征的归一化,例如随机森林,bagging 和 boosting等。
示例代码
pandas实现
import pandas as pd data = pd.Series(list('abca')) print(data) print("----------") # 独热编码 # 参数说明 # data : array-like, Series, or DataFrame ,输入的数据 # prefix : string, list of strings, or dict of strings, default None ,get_dummies转换后,列名的前缀 # columns : list-like, default None,指定需要实现类别转换的列名 # dummy_na : bool, default False,增加一列表示空缺值,如果False就忽略空缺值 # drop_first : bool, default False,获得k中的k-1个类别值,去除第一个 result = pd.get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, columns=None, sparse=False, drop_first=False) print(result) print("----------") # 哑编码 result2 = pd.get_dummies(data,drop_first=True) print(result2) 复制代码
运行结果:
0 a 1 b 2 c 3 a dtype: object ---------- a b c 0 1 0 0 1 0 1 0 2 0 0 1 3 1 0 0 ---------- b c 0 0 0 1 1 0 2 0 1 3 0 0 复制代码
关于独特编码与哑编码的区别与联系
哑变量编码直观的解释就是任意的将一个状态位去除。 拿上面的例子来说,我们用2个状态位就足够反应上述3个类别的信息。
独特编码与哑编码的的“思想路线”是相同的,只是哑变量编码觉得独特编码太罗嗦了,所以它就很那么很明显的东西省去了。这种简化不能说到底好不好,这要看使用的场景。
sklearn实现
from sklearn.preprocessing import OneHotEncoder import numpy as np target = np.array([[1],[2],[3],[4]]) ohe = OneHotEncoder() ohe.fit(target) print(ohe.get_feature_names()) ohe_r = ohe.transform([[4],[3],[1],[4]]).toarray() print(ohe_r) 复制代码
运行结果:
['x0_1' 'x0_2' 'x0_3' 'x0_4'] [[0. 0. 0. 1.] [0. 0. 1. 0.] [1. 0. 0. 0.] [0. 0. 0. 1.]] 复制代码
针对多个特征进行特征编码:
# 多个特征独热编码 from sklearn import preprocessing import numpy as np data = np.array([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]) enc = preprocessing.OneHotEncoder() enc.fit(data) # fit来学习编码 enc.transform([[0, 1, 3]]).toarray() # 进行编码 复制代码
运行结果如下:
array([[1., 0., 0., 1., 0., 0., 0., 0., 1.]]) 复制代码
上面的代码说明如下:
原始数据矩阵为4*3,即4个样本数据,3个特征维度。
- 观察数据矩阵,第一列为第一个特征维度,有两种取值0\1. 所以对应编码方式为10 、01。
- 同理,第二列为第二个特征维度,有三种取值0\1\2,所以对应编码方式为100、010、001。
- 同理,第三列为第三个特征维度,有四中取值0\1\2\3,所以对应编码方式为1000、0100、0010、0001。
再来看要进行编码的参数[0 , 1, 3]
, 0作为第一个特征编码为10, 1作为第二个特征编码为010, 3作为第三个特征编码为0001。
故此编码结果为 1 0 0 1 0 0 0 0 1
。
标签编码
什么是标签编码
标签编码,就是根据字符串形式的特征值在特征序列中的位置,为其指定一个数字标签,用于提供给基于数值算法的学习模型。
利用LabelEncoder
对不连续的数字或者文本进行编号,转换成连续的数值型变量。
示例代码
对文本标签进行编号的示例代码如下:
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() le.fit(["Japan", "china", "Japan", "Korea","china"]) print('标签个数:%s' % le.classes_) print('标签值标准化:%s' % le.transform(["Japan", "china", "Japan", "Korea","china"])) print('标准化标签值反转:%s' % le.inverse_transform([0, 2 ,0 ,1 ,2])) 复制代码
运行结果:
标签个数:['Japan' 'Korea' 'china'] 标签值标准化:[0 2 0 1 2] 标准化标签值反转:['Japan' 'china' 'Japan' 'Korea' 'china'] 复制代码
对不连续的数字进行编号的示例代码如下:
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() # 对不连续的数字进行编号 le.fit([1,5,67,100]) result = le.transform([1,1,100,67,5]) print(f"编号:{result}") print("逆过程:%s"%le.inverse_transform([0,0,3,2,1])) 复制代码
运行结果:
编号:[0 0 3 2 1] 逆过程:[ 1 1 100 67 5] 复制代码
二值化
什么是二值化
设置一个条件,把离散(或连续)数据分类两类。比如Age,大于30和小于30。大于30为1,小于等于30为0。
示例代码
from sklearn.preprocessing import Binarizer binerize = Binarizer(threshold = 30) x = np.array([30, 20, 45, 99, 87, 25, 31]) # 提取数据 trans = binerize.fit_transform(x.reshape(-1,1)) trans.tolist() 复制代码
运行结果:
[[0], [0], [1], [1], [1], [0], [1]] 复制代码
参考文档
- OneHotEncoder独热编码和LabelEncoder标签编码
- 数据归一化处理和常用标签编码,独热编码
- 数据预处理:独热编码(One-Hot Encoding)和 LabelEncoder标签编码
- 离散型特征编码方式:one-hot与哑变量*
- 连续数据特征离散化的方法