类别型特征的编码方法

发布时间:2024年01月09日

机器学习模型中除了决策树等少数模型能直接处理字符串形式的类别型特征输入外,逻辑回归、支持向量机等模型的输入必须是数值型特征才能在矩阵上执行线性代数计算,所以参加计算的特征必须是数值型的,对于非数值型的特征需要进行编码处理。对于离散属性(也称为分类变量或类别特征)的编码,我们可以使用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)将每个类别替换为该类别在数据集中的频数或出现次数。这种编码方法可以提供关于类别的频率信息,但可能引入一定的信息泄漏。若类别的频数可能与目标变量之间存在相关性,则适合使用频数编码。如不同城市的人爱吃辣的程度:四川地区选择爱吃辣的频数>江苏地区选择爱吃辣的频数。

优点:

  1. 信息保留:频数编码将分类特征转换为数值特征,可以帮助模型更好地理解分类特征之间的关系,同时保留了部分类别信息。
  2. 提供有用的统计信息:频数编码利用了每个类别在数据集中的频次信息,不仅提供了类别本身的信息,还提供了关于类别分布的统计信息。这有助于模型更好地理解数据。
  3. 处理缺失数据和噪声鲁棒性:相对于独热编码等编码方法,频数编码更具有噪声鲁棒性,因为它将类别信息转换为连续的数值特征,并减少了维度空间的需求。这可能有助于减少过拟合和处理缺失数据的影响。
  4. 离群值敏感:对离群值很敏感,所以结果可以归一化或者转换一下(例如使用对数变换)。未知类别可以替换为1。

缺点:

  1. 引入偏见:频数编码会将分类特征的不同类别替换为它们在数据集中出现的频次,如果某个类别在数据集中的频次很高,可能会导致该类别的编码过大,从而引入偏见。
  2. 处理高基数特征的挑战:当分类特征具有高基数(即类别数目非常多)时,频数编码可能引入过多的维度,从而增加模型的复杂度和计算成本。
  3. 丢失部分信息:频数编码通过将类别信息转换为数值特征来进行编码,可能会丢失一部分类别本身的信息。这可能会影响模型对某些类别的学习能力。

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)将离散属性的每个类别编码为其在目标变量上的平均值或其他统计信息。目标编码能够捕获类别与目标变量之间的关联性,但需要注意信息泄漏和过拟合的问题。
若分类编码与目标遍历间具有一定关联性,则适合使用目标编码,如一个城市的房价与其所处的区域有很大关系,使用目标编码计算同一个区域的平均房价来代替区域属性上的离散值:上海黄埔区的目标编码>上海嘉定区的目标编码。

优点:

  1. 信息丰富性:目标编码能够捕获分类变量与目标变量之间的关联性,将目标变量的统计信息编码为数值型特征。这样,模型可以利用更多的相关信息进行训练,提高模型的性能。
  2. 保留类别信息:相比于使用独热编码或标签编码等方法,目标编码能够保留原始的类别信息。这对于一些具有有序关系的分类变量,如年龄组、评级等,能够提供更具有解释性的特征表示。
  3. 处理高基数分类变量:对于具有大量类别的分类变量(高基数),独热编码会导致高维度的稀疏特征表示,而目标编码可以使用目标变量的统计信息来代替,从而减少特征的维度。

缺点:

  1. 信息泄漏:在目标编码中,使用了目标变量的统计信息作为编码依据,因此可能会导致信息泄漏。即使在交叉验证中也有可能出现该问题。在进行目标编码时,需要小心处理,确保不将测试集中的信息泄漏到训练集中。
  2. 过拟合风险:目标编码过于依赖于目标变量的统计信息,可能导致在训练集上过拟合。特别是当类别数量较少或数据量较少时,过拟合风险更高。为了解决这个问题,可以使用平滑技术(如平均值编码、贝叶斯平滑等)来减轻过拟合风险。
  3. 对异常值敏感:如果分类变量中存在异常值,目标编码可能会受到影响。异常值的存在可能导致目标变量的统计指标产生偏移,从而影响编码结果。因此,在应用目标编码之前,需要先进行异常值处理。

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)
文章来源:https://blog.csdn.net/RainTicking/article/details/135446040
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。