这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
使用机器学习时,会存在不同的特征类型:连续型特征和离散型特征。
针对连续性特征,我们通常将其线性缩放到[-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]]