本文主要对OneHot独热编码进行了简单介绍,同时介绍了在sklearn与pandas中进行数字编码以及One-hot独热编码的几种方式。
在建模过程中,我们通常会碰到各种类型的属性,如果是标称型属性(非数值类型的属性),也就是不具备序列性、不能比较大小的属性,通常我们不能用简单的数值来粗暴替换。因为属性的数值大小会影响到权重矩阵的计算,不存在大小关系的属性,其权重也不应该发生相应的变化,那么我们就需要用到One-hot编码(也有人称独热编码)这种特殊的编码方式了。
data = pd.DataFrame([
[ 'M', 10.1],
[ 'L', 13.5],
[ 'XL', 15.3],
[ 'XL', 16.3]])
One-hot编码后变为:
data = array([[0, 1, 0, 10.1],
[1, 0, 0, 13.5],
[0, 0, 1, 15.3],
[0, 0, 1, 16.3]])
one-hot通常用在GBDT、XGBoost这些模型里面都挺好的,但是用在逻辑回归里不行。因为逻辑回归要求变量间相互独立,如果你只有一个属性需要做one-hot编码还好,如果你有多个属性需要做one-ont编码,那么当某个样本的多个one-hot属性同时为1时,这两个属性就完全相关了,必然会导致singular error,也就是非奇异矩阵不能求解唯一解,得不出唯一的模型,但是你又不可能把同一个属性的某一个one-hot延伸变量删除。
如果在逻辑回归中入模标称属性,可以直接替换成数值,然后做woe变换,用每个类别的woe值来代替原来的数值,这样既能够避免生成相关性强的变量,又能避开类别间大小无法比较的问题。
import pandas as pd
import numpy as np
data = pd.DataFrame([
['yellow', 'M', 10.1, 'class1'],
['red', 'L', 13.5, 'class2'],
['blue', 'XL', 15.3, 'class1'],
['green', 'XL', 16.3, 'class3']])
data.columns = ['颜色', '大小', '价格', '类别标签']
data
颜色 | 大小 | 价格 | 类别标签 | |
---|---|---|---|---|
0 | yellow | M | 10.1 | class1 |
1 | red | L | 13.5 | class2 |
2 | blue | XL | 15.3 | class1 |
3 | green | XL | 16.3 | class3 |
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
category = encoder.fit_transform(data['颜色'])
print("颜色编码:",category)
print("编码对应的颜色:",encoder.classes_)
颜色编码: [3 2 0 1]
编码对应的颜色: ['blue' 'green' 'red' 'yellow']
但是LabelEncoder一次只能处理一个属性,因此需要使用循环分别对属性进行处理
columns = ['颜色','大小','类别标签']
data2 = data.copy()
for column in columns:
data2[column] = encoder.fit_transform(data[column])
print("{}编码顺序:{}".format(column, encoder.classes_))
data2
颜色编码顺序:['blue' 'green' 'red' 'yellow']
大小编码顺序:['L' 'M' 'XL']
类别标签编码顺序:['class1' 'class2' 'class3']
颜色 | 大小 | 价格 | 类别标签 | |
---|---|---|---|---|
0 | 3 | 1 | 10.1 | 0 |
1 | 2 | 0 | 13.5 | 1 |
2 | 0 | 2 | 15.3 | 0 |
3 | 1 | 2 | 16.3 | 2 |
from sklearn.preprocessing import OneHotEncoder
columns = ['颜色','大小','类别标签']
enc = OneHotEncoder()
# 将['颜色','大小','类别标签']这3列进行独热编码
enc.fit_transform(data2[columns]).toarray()
array([[0., 0., 0., 1., 0., 1., 0., 1., 0., 0.],
[0., 0., 1., 0., 1., 0., 0., 0., 1., 0.],
[1., 0., 0., 0., 0., 0., 1., 1., 0., 0.],
[0., 1., 0., 0., 0., 0., 1., 0., 0., 1.]])
LabelBinarizer 每次也只能对一个特征进行独热编码,需要使用循环对每个特征进行编码
from sklearn.preprocessing import LabelBinarizer
enc = LabelBinarizer()
transform_data = enc.fit_transform(data['颜色'])
transform_data
array([[0, 0, 0, 1],
[0, 0, 1, 0],
[1, 0, 0, 0],
[0, 1, 0, 0]])
transform_data = enc.fit_transform(data['大小'])
transform_data
array([[0, 1, 0],
[1, 0, 0],
[0, 0, 1],
[0, 0, 1]])
from sklearn.feature_extraction import DictVectorizer
dict = DictVectorizer(sparse=False)
dict_data= dict.fit_transform(data.to_dict(orient="records"))
dict_data
array([[10.1, 0. , 1. , 0. , 1. , 0. , 0. , 0. , 0. , 0. , 1. ],
[13.5, 1. , 0. , 0. , 0. , 1. , 0. , 0. , 0. , 1. , 0. ],
[15.3, 0. , 0. , 1. , 1. , 0. , 0. , 1. , 0. , 0. , 0. ],
[16.3, 0. , 0. , 1. , 0. , 0. , 1. , 0. , 1. , 0. , 0. ]])
# 查看各列标签名称
dict.get_feature_names()
['价格',
'大小=L',
'大小=M',
'大小=XL',
'类别标签=class1',
'类别标签=class2',
'类别标签=class3',
'颜色=blue',
'颜色=green',
'颜色=red',
'颜色=yellow']
data_category, data_class = pd.factorize(data['大小'])
print(data_category)
print(data_class)
[0 1 2 2]
Index(['M', 'L', 'XL'], dtype='object')
data_oneHot = pd.get_dummies(data)
data_oneHot
价格 | 颜色_blue | 颜色_green | 颜色_red | 颜色_yellow | 大小_L | 大小_M | 大小_XL | 类别标签_class1 | 类别标签_class2 | 类别标签_class3 | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 10.1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
1 | 13.5 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
2 | 15.3 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |
3 | 16.3 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
综合上述方法:
个人认为下面两种方式转化为One_hot类型特征最方便: