文章目录
一. 常见的特征类型
一般特征可以分为两大类特征,连续型和离散型特征。而离散型特征既有是数值型的,也有类别型特征。例如性别(男、女)、成绩等级(A、B、C)等等。连续型特征的原始形态就可以作为模型的输入,无论是LR、神经网络,还是SVM、GBDT、xgboost等等。但是除了决策树等少数模型能直接处理字符串形式的类别型特征输入,逻辑回归、支持向量机等模型的输入必须是数值型特征才能正确工作。因此就需要将离散型特征中的类别型特征转换成数值型的。
二. 编码
2.1 序号编码(Ordinal Encoding)
序号编码通常用于处理类别间具有内在大小顺序关系的数据,对于一个具有m个类别的特征,我们将其对应地映射到 [0,m-1] 的整数。例如对于”学历”这样的类别,”学士”、”硕士”、”博士” 可以很自然地编码成 [0,2],因为它们内在就含有这样的逻辑顺序。但如果对于“颜色”这样的类别,“蓝色”、“绿色”、“红色”分别编码成[0,2]是不合理的,因为我们并没有理由认为“蓝色”和“绿色”的差距比“蓝色”和“红色”的差距对于特征的影响是不同的。
sklearn.preprocessing中的OrdinalEncoder进行处理,可以通过两种方式进行编码:
- 方法1:先将数据categorical_df载入encoder中,再将载入的数据进行OrdinalEncoder编码
- 方法2: 直接载入encoder并编码
import pandas as pd
from sklearn.preprocessing import OrdinalEncoder
categorical_df = pd.DataFrame({'my_id': ['101', '102', '103', '104'],
'name': ['allen', 'bob', 'chartten', 'dory'],
'place': ['third', 'second', 'first', 'second']})
print(categorical_df)
print('--'*20)
encoder = OrdinalEncoder() #创建OrdinalEncoder对象
# 方法1
encoder.fit(categorical_df) #将数据categorical_df载入encoder中
categorical_df = encoder.transform(categorical_df) #将载入的数据进行OrdinalEncoder
# 方法2
categorical_df = encoder.fit_transform(categorical_df) #代替上面的fit()和transform(),一步到位
print(categorical_df)
输出:
我们发现将三列特征全部按照升序的方式编码,比如name
这一列,按照首字母前后顺序排序:allen为0,bob为1,chartten为2,dory为3
,其余两行也是如此。如果我们想自定义排列顺序呢?比如name列,想按照这样的方式编码:dory为0, bob为1, chartten为2, allen为3
。
方法很简单,就是先自定义标签序列name_categories
和place_categories
,然后在创建OrdinalEncoder对象时,categories
变量中传入自定义标签列即可。
import pandas as pd
from sklearn.preprocessing import OrdinalEncoder
categorical_df = pd.DataFrame({'my_id': ['101', '102', '103', '104'],
'name': ['allen', 'bob', 'chartten', 'dory'],
'place': ['third', 'second', 'first', 'second']})
# In the 'name' column, I want 'dory' to be 0, 'bob' to be 1, 'chartten' to be 2, and 'allen' to be 3
# In the 'place' column, I want 'first' to be 0, 'second' to be 1, and 'third' to be 2
name_categories = ['dory', 'bob', 'chartten', 'allen']
place_categories = ['first', 'second', 'third']
encoder = OrdinalEncoder(categories=[name_categories, place_categories])
categorical_df[['name', 'place']]= encoder.fit_transform(categorical_df[['name', 'place']])
categorical_df
输出:
如图中所示,dory现在排在编码后name列的0,以此类推。
2.2独热编码(One-hot Encoding)
独热编码:通常用于处理类别间不具有大小关系的特征。
可以通过导入sklearn.preprocessing中的OneHotEncoder,创建哑变量进行处理。
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
categorical_df = pd.DataFrame({'my_id': ['101', '102', '103', '104'],
'name': ['allen', 'bob', 'chartten', 'dory'],
'place': ['third', 'second', 'first', 'second']})
print(categorical_df)
print('--'*20)
encoder = OneHotEncoder()
encoder.fit_transform(categorical_df[['name']]).toarray()
输出:
我们对name进行onehot编码后,第一列表示allen: [1,0,0,0]
,第二列表示bob: [0,1,0,0]
,以此类推。
同理也可以自定义编码顺序:
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
categorical_df = pd.DataFrame({'my_id': ['101', '102', '103', '104'],
'name': ['allen', 'bob', 'chartten', 'dory'],
'place': ['third', 'second', 'first', 'second']})
print(categorical_df)
print('--'*20)
encoder = OneHotEncoder(categories=[['dory', 'bob', 'chartten', 'allen']])
encoder.fit_transform(categorical_df[['name']]).toarray()
输出:
我们自定义的编码顺序是['dory', 'bob', 'chartten', 'allen']
,dory=[1,0,0,0],bob=[0,1,0,0],chartten=[0,0,1,0],allen=[0,0,0,1], 在输出结果的第一列中表示allen,即为[0,0,0,1],第二列表示bob,即为[0,1,0,0],以此类推。
2.3 标签编码 (Label Encoding)
Label Encoding是给某一列数据编码,而Ordinal Encoding是给所有的特征编码。因此Label Encoding常用于给标签(label)编码,而Ordinal Encoding常用于给数据集中的特征编码。
代码举例:
import pandas as pd
from sklearn.preprocessing import LabelEncoder
categorical_df = pd.DataFrame({'my_id': ['101', '102', '103', '104'],
'name': ['allen', 'bob', 'chartten', 'dory'],
'place': ['third', 'second', 'first', 'second'],
})
print(categorical_df)
print('--'*20)
encoder = LabelEncoder()
categorical_df['place']=encoder.fit_transform(categorical_df['place'])
categorical_df
输出:
我们可以从上面的例子中看到place列已被编码,与上文Ordinal Encoding中的例子做对比可以发现,Label Encoding是给某一列数据编码,而Ordinal Encoding是给所有的特征编码。
我们知道Ordinal Encoding用于有序特征的数据编码,OneHot Encoding用于无序特征的数据编码,而LabelEncoder与OneHotEncoder又存在什么样的区别呢?
表面区别:
用途区别:
有些模型的损失函数对数值大小是敏感的,即变量间的数值大小本身是有比较意义的,如逻辑回归,SVM等,我们暂将其称为A类模型;有些模型本身对数值变化不敏感,数值存在的意义更多的是为了排序,即0.1,0.2,0.3与10,20,30是没有区别的,这部分模型绝大部分是树模型,暂将其称为B类模型。比如用label encoding将某列特征 [dog,cat,dog,mouse,cat],转换为[1,2,1,3,2]。这里就产生了一个奇怪的现象:dog和mouse的平均值是cat,所以label encoding最直观的缺点就是赋值难以解释,适用场景更窄。
-
特征数据类型
对于定类类型的数据,建议使用one-hot encoding。定类类型就是纯分类,不排序,没有逻辑关系。比如性别分男和女,男女不存在任何逻辑关系,我们不能说男就比女好,或者相反。再者,中国各省市分类也可以用独热编码,同样各省不存在逻辑关系,这时候使用one-hot encoding会合适些。但注意,一般会舍去一个变量,比如男的对立面肯定是女,那么女就是重复信息,所以保留其中一个变量即可。
对于定序类型的数据,建议使用label encoding。定序类型也是分类,但有排序逻辑关系,等级上高于定类。比如,学历分小学,初中,高中,本科,研究生,各个类别之间存在一定的逻辑,显然研究生学历是最高的,小学最低。这时候使用Label encoding会显得更合适,因为自定义的数字顺序可以不破坏原有逻辑,并与这个逻辑相对应。 -
所使用的模型
对数值大小敏感的模型必须使用one-hot encoding。典型的例子就是LR和SVM。二者的损失函数对数值大小是敏感的,并且变量间的数值大小是有比较意义的。而Label encoding的数字编码没有数值大小的含义,只是一种排序,因此对于这些模型都使用one-hot encoding。
对数值大小不敏感的模型(如树模型)不建议使用one-hot encoding。一般这类模型为树模型。如果分类类别特别多,那么one-hot encoding会分裂出很多特征变量。这时候,如果我们限制了树模型的深度而不能向下分裂的话,一些特征变量可能就因为模型无法继续分裂而被舍弃损失掉了。因此,此种情况下可以考虑使用Label encoding。
2.4 频数编码(Frequency Encoding/Count Encoding)
将类别特征替换为训练集中的计数(一般是根据训练集来进行计数,属于统计编码的一种,统计编码,就是用类别的统计特征来代替原始类别,比如类别A在训练集中出现了100次则编码为100)。这个方法对离群值很敏感,所以结果可以归一化或者转换一下(例如使用对数变换)。未知类别可以替换为1。
频数编码使用频次替换类别。有些变量的频次可能是一样的,这将导致碰撞。尽管可能性不是非常大,没法说这是否会导致模型退化,不过原则上我们不希望出现这种情况。
比如某个分类中’Peking’出现了10次,那么’Peking’就会被替换为10. 我们可以用categorical-encodings
包中的CountEncoder
实现。
import category_encoders as ce
features = ['Peking', 'Peking', 'Shanghai', 'Peking', 'Guangzhou', 'Shanghai']
count_enc = ce.CountEncoder()
count_enc.fit_transform(features)
2.5 目标编码(Target Encoding)
等待更新。。。