机器学习模型中除了决策树等少数模型能直接处理字符串形式的类别型特征输入外,逻辑回归、支持向量机等模型的输入必须是数值型特征才能在矩阵上执行线性代数计算,所以参加计算的特征必须是数值型的,对于非数值型的特征需要进行编码处理。对于离散属性(也称为分类变量或类别特征)的编码,我们可以使用5种方式来实现,分别是标签编码、序列编码、独热编码、频数编码和目标编码。
标签编码(Label Encoding)将每个类别映射到从0开始的整数值。需满足的条件是,特征值间没有明显的顺序关系,而且转化后的数值没有具体含义,只做类别区分。标签编码是给某一列数据编码,所以常用于给标签(label)编码。
举例来说,性别[‘男’,‘女’]可以编码为[0,1],血型[‘O’,‘A’,‘B’,‘AB’]可以编码为[0,1,2,3]。
sklearn.preprocessing.LabelEncoder实现标签编码
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
city_list = ["paris", "paris", "tokyo", "amsterdam"]
le.fit(city_list)
city_list_encode = le.transform(city_list) # 进行Encode
city_list_decode = le.inverse_transform(city_list_le) # 进行decode
print(f'原始数据为:{city_list}')
print(f'list中含有的类别有:{le.classes_}')
print(f'编码后:{city_list_encode}')
print(f'解码后:{city_list_decode}')
原始数据为:[‘paris’, ‘paris’, ‘tokyo’, ‘amsterdam’]
list中含有的类别有:[‘amsterdam’ ‘paris’ ‘tokyo’]
编码后:[1 1 2 0]
解码后:[‘paris’ ‘paris’ ‘tokyo’ ‘amsterdam’]
Pandas.factorize()实现标签编码
import pandas as pd
df = pd.DataFrame(['green','bule','red','bule','green'],columns=['color'])
print(pd.factorize(df['color']))
print("编码后:",pd.factorize(df['color'])[0])
print("类别索引:",pd.factorize(df['color'])[1])
(array([0, 1, 2, 1, 0], dtype=int64), Index([‘green’, ‘bule’, ‘red’], dtype=‘object’))
编码后: [0 1 2 1 0]
类别索引: Index([‘green’, ‘bule’, ‘red’], dtype=‘object’)
序列编码(Ordinal Encoding)是一种将离散特征的各个类别映射为整数序列的方法。序列编码适用于有序特征,其中类别之间存在一定的顺序关系,但没有明确的意义。
比如,学历[‘本科’,‘硕士’,‘博士’]存在内在逻辑顺序关系,‘本科’ < ‘硕士’ < ‘博士’,可以编码为[1,2,3],转换后保留了逻辑关系。而对于“颜色”这样的类别,'蓝色’与’绿色’显然是没有逻辑顺序的。
sklearn.preprocessing.OrdinalEncoder实现序列编码
import pandas as pd
from sklearn.preprocessing import OrdinalEncoder
degree_df = pd.DataFrame({'degree':['硕士', '博士', '本科', '硕士']})
degree_categories=['本科','硕士','博士'] # 自定义标签序列,按该顺序从0开始编码
oe = OrdinalEncoder(categories=[degree_categories]) #创建OrdinalEncoder对象
degree_df_encode = oe.fit_transform(degree_df[['degree']]) # 进行Encode
degree_df_decode = oe.inverse_transform(degree_df_encode) # 进行decode
print('原始数据为:',degree_df['degree'].values)
print('自定义有序类别为:',oe.categories_)
print('编码后:',degree_df_encode.T)
print('解码后:',degree_df_decode.T)
原始数据为: [‘硕士’ ‘博士’ ‘本科’ ‘硕士’]
自定义有序类别为: [array([‘本科’, ‘硕士’, ‘博士’], dtype=object)]
编码后: [[1. 2. 0. 1.]]
解码后: [[‘硕士’ ‘博士’ ‘本科’ ‘硕士’]]
DataFrame.map()实现序列编码
import pandas as pd
degree_list = ["硕士", "博士", "本科", "硕士"]
df = pd.DataFrame(degree_list,columns=['学历'])
ord_map = {'本科':1,'硕士':2,'博士':3}
encode_df = df['学历'].map(ord_map)
print('原始数据为:',df['学历'].values)
print('编码后:',encode_df.values)
原始数据为: [‘硕士’ ‘博士’ ‘本科’ ‘硕士’]
编码后: [2 3 1 2]
独热编码(One-Hot Encoding)将每个离散属性的每个类别创建一个新的二进制特征。对于每个样本,只有一个二进制特征为1,表示它属于对应的类别,其他特征为0。这种方法适用于具有有限数量的类别。适合于分类属性中的各个特征值彼此间无序、无关联,且特征值的取值数量较少的情况。
比如颜色特征有3种:红色、绿色和黄色,转换成独热编码分别表示为:001, 010, 100。
优缺点:
优点:独热编码解决了分类器不好处理属性数据的问题,在一定程度上也起到了扩充特征的作用。它的值只有0和1,不同的类型存储在垂直的空间。使回归的距离计算更合理。
缺点:当类别的数量很多时,特征空间会变得非常大。在这种情况下,一般可以用PCA来减少维度。而且one hot encoding+PCA这种组合在实际中也非常有用。
sklearn.preprocessing.OneHotEncoder实现独热编码
from sklearn.preprocessing import OneHotEncoder
import numpy as np
city_arr = np.array(["苏州", "无锡", "苏州", "上海"])
city_df = city_arr.reshape(-1,1)
ohe = OneHotEncoder()
city_enc = ohe.fit_transform(city_df) # fit来学习编码,返回稀疏矩阵
print('原始数据为:',city_arr)
print('编码后:',city_enc.toarray())
print('含有的类别有:',ohe.categories_)
print('OneHot的类别有:',ohe.get_feature_names())
原始数据为: [‘苏州’ ‘无锡’ ‘苏州’ ‘上海’]
编码后: [
[0. 0. 1.]
[0. 1. 0.]
[0. 0. 1.]
[1. 0. 0.]]
含有的类别有: [array([‘上海’, ‘无锡’, ‘苏州’], dtype=‘<U2’)]
OneHot的类别有: [‘x0_上海’ ‘x0_无锡’ ‘x0_苏州’]
Pandas.get_dummies()实现独热编码
import pandas as pd
df = pd.DataFrame([
['green', 'BMW', 2017],
['blue', 'BMW', 2015],
['yellow', 'Lexus', 2018],
])
df.columns = ['color', 'make', 'year']
df_processed = pd.get_dummies(df, prefix_sep="_", columns=df.columns[:-1])
print('编码后:\n',df_processed)
频数编码(Count Encoding)将每个类别替换为该类别在数据集中的频数或出现次数。这种编码方法可以提供关于类别的频率信息,但可能引入一定的信息泄漏。若类别的频数可能与目标变量之间存在相关性,则适合使用频数编码。如不同城市的人爱吃辣的程度:四川地区选择爱吃辣的频数>江苏地区选择爱吃辣的频数。
优点:
缺点:
category_encoders.CountEncoder()实现频数编码
import category_encoders as ce
citys = ['Peking', 'Peking', 'Shanghai', 'Peking', 'Guangzhou', 'Shanghai']
count_enc = ce.CountEncoder()
df_count = count_enc.fit_transform(citys)
print('编码后:',df_count.values.T)
编码后: [[3 3 2 3 1 2]]
DataFrame.groupby()实现频数编码
import pandas as pd
data = pd.DataFrame([
['green', 'Chevrolet', 2017],
['blue', 'BMW', 2015],
['yellow', 'Lexus', 2018],
['yellow', 'audi', 2023]
])
data.columns = ['color', 'make', 'year']
data_count = data.groupby(by='color',as_index=False)['color'].agg(['count'])
print(data_count)
color ? count
blue ? ? 1
green ? 1
yellow ? 2
目标编码(Target Encoding)将离散属性的每个类别编码为其在目标变量上的平均值或其他统计信息。目标编码能够捕获类别与目标变量之间的关联性,但需要注意信息泄漏和过拟合的问题。
若分类编码与目标遍历间具有一定关联性,则适合使用目标编码,如一个城市的房价与其所处的区域有很大关系,使用目标编码计算同一个区域的平均房价来代替区域属性上的离散值:上海黄埔区的目标编码>上海嘉定区的目标编码。
优点:
缺点:
category_encoders TargetEncoder()实现目标编码
from category_encoders import TargetEncoder
import pandas as pd
from sklearn.datasets import load_boston
# prepare some data
bunch = load_boston()
y_train = bunch.target[0:250]
y_test = bunch.target[250:506]
X_train = pd.DataFrame(bunch.data[0:250], columns=bunch.feature_names)
X_test = pd.DataFrame(bunch.data[250:506], columns=bunch.feature_names)
# use target encoding to encode two categorical features
enc = TargetEncoder(cols=['CHAS', 'RAD'])
# transform the datasets
training_numeric_dataset = enc.fit_transform(X_train, y_train)
testing_numeric_dataset = enc.transform(X_test)
print(training_numeric_dataset)
print(testing_numeric_dataset)