MeanEncoder#
均值编码是将分类特征中的类别替换为目标变量在每个类别中显示的均值的过程。例如,如果我们试图预测违约率(这是目标变量),并且我们的数据集具有分类变量 城市,其类别为 伦敦、曼彻斯特 和 布里斯托尔,且各城市的违约率分别为 0.1、0.5 和 0.3,通过均值编码,我们将伦敦替换为 0.1,曼彻斯特替换为 0.5,布里斯托尔替换为 0.3。
均值编码,与独热编码和序数编码一起,属于数据科学中最常用的分类编码技术。
据说均值编码很容易导致过拟合。这是因为我们在编码过程中将目标的一些信息捕获到了预测特征中。更重要的是,过拟合可能是由于使用不可靠的均值目标值对低频类别进行编码所引起的。简而言之,训练集中这些类别的均值目标值在测试数据或新观测中并不适用。
过拟合#
当分类特征中的类别有良好的表示时,换句话说,当我们的数据集中有足够的观测值显示我们想要编码的类别时,那么按类别取目标变量的简单平均值是一个很好的近似。我们可以相信,一个新的数据点,比如说来自测试数据,显示该类别也将具有与我们在训练期间为该类别计算的目标均值相似的目标值。
然而,如果只有少数观测值显示某些类别,那么这些类别的平均目标值将不可靠。换句话说,我们对于新观测值显示该类别时,其平均目标值接近我们估计值的确定性会降低。
为了考虑罕见类别编码值的不确定性,我们通常的做法是将每个类别的目标变量的平均值与整个训练数据集上计算的目标的总体平均值进行 “混合”。这种混合是根据该类别内目标的可变性和类别频率成比例的。
平滑#
为了避免过拟合,我们可以将目标值估计的均值确定为两个值的混合:每个类别的目标值均值(称为后验)和整个数据集中的目标值均值(称为先验)。
以下公式展示了使用平滑估计的平均目标值:
先验值和后验值通过一个权重因子(wi
)进行“混合”。这个权重因子是类别组大小(n_i
)以及数据中目标的方差(t
)和类别内的方差(s
)的函数:
当类别组较大时,权重因子接近1,因此更多权重被赋予后验(每个类别的目标均值)。当类别组规模较小时,权重接近0,更多权重被赋予先验(整个数据集中目标的均值)。
此外,如果目标在该类别内的变异性较大,我们也会给予先验更多的权重,而如果变异性较小,则我们会给予后验更多的权重。
简而言之,在这些情况下添加平滑处理可以帮助防止过拟合,即分类数据具有许多不频繁的类别或显示出高基数。
高基数#
高基数指的是分类特征中存在大量唯一类别的情况。均值编码是专门设计用来处理高基数变量的,它利用这种平滑函数,通过将不常见的类别替换为非常接近训练数据上计算的总体目标均值的值,从而将这些不常见的类别混合在一起。
另一种开箱即用的处理基数问题的编码方法是计数编码。例如,参见 CountFrequencyEncoder
。
为了在替代编码方法中处理高基数的变量,您可以通过使用 RareLabelEncoder
将稀有类别分组。
均值编码的替代Python实现#
在 Feature-engine 中,我们考虑目标变异性和类别频率来混合概率。在原始论文中,有其他公式来确定混合。如果你想查看这些内容,可以使用 Python 库 Category encoders 中的转换器:
均值编码器#
Feature-engine 的 MeanEncoder()
用每个类别的目标均值替换类别。默认情况下,它不实现平滑处理。这意味着它将用训练数据集(仅后验)训练期间确定的目标均值替换类别。
要应用我们之前描述的平滑公式,请将参数 smoothing
设置为 "auto"
。这是我们推荐的解决方案。或者,您可以将参数 smoothing
设置为您想要的任何值,在这种情况下,权重因子 wi
将按如下方式计算:
其中 s 是您传递给 smoothing
的值。
未见类别#
未见类别是指在训练过程中未见过的标签。换言之,即训练数据中未出现的类别。
使用 MeanEncoder()
,我们可以通过以下三种方式之一处理未见过的类别:
我们可以设置均值编码器忽略未见过的类别,在这种情况下,这些类别将被替换为 nan。
我们可以设置均值编码器在遇到未见过的类别时抛出错误。当不期望这些分类变量出现新类别时,这非常有用。
我们可以指示均值编码器用训练数据中显示的目标均值来替换未见或新的类别,即先验。
均值编码与机器学习#
Feature-engine 的 MeanEncoder()
可以对回归和二分类数据集执行均值编码。目前,我们不支持多类目标。
Python 示例#
在以下章节中,我们将使用泰坦尼克号数据集展示 MeanEncoder()
的功能。
首先,让我们加载库、函数和类:
from sklearn.model_selection import train_test_split
from feature_engine.datasets import load_titanic
from feature_engine.encoding import MeanEncoder
为了避免数据泄露,将数据分为训练集和测试集是非常重要的。无论是否进行平滑处理,目标值的平均值都将仅使用训练数据来确定。
让我们加载并分割数据:
X, y = load_titanic(
return_X_y_frame=True,
handle_missing=True,
predictors_only=True,
cabin="letter_only",
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0,
)
print(X_train.head())
我们看到生成的数据框包含3个分类列:性别、客舱和登船地点:
pclass sex age sibsp parch fare cabin embarked
501 2 female 13.000000 0 1 19.5000 M S
588 2 female 4.000000 1 1 23.0000 M S
402 2 female 30.000000 1 0 13.8583 M C
1193 3 male 29.881135 0 0 7.7250 M Q
686 3 female 22.000000 0 0 7.7250 M Q
简单均值编码#
让我们设置 MeanEncoder()
来用目标均值替换分类特征中的类别,不进行平滑处理:
encoder = MeanEncoder(
variables=['cabin', 'sex', 'embarked'],
)
encoder.fit(X_train, y_train)
通过 fit()
方法,编码器学习每个类别的目标平均值,并将这些值存储在 encoder_dict_
属性中:
encoder.encoder_dict_
encoder_dict_
包含了每个变量、每个类别的目标均值。我们可以使用这个字典将编码特征中的数字映射回原始的类别值。
{'cabin': {'A': 0.5294117647058824,
'B': 0.7619047619047619,
'C': 0.5633802816901409,
'D': 0.71875,
'E': 0.71875,
'F': 0.6666666666666666,
'G': 0.5,
'M': 0.30484330484330485,
'T': 0.0},
'sex': {'female': 0.7283582089552239, 'male': 0.18760757314974183},
'embarked': {'C': 0.553072625698324,
'Missing': 1.0,
'Q': 0.37349397590361444,
'S': 0.3389570552147239}}
我们现在可以继续用数值替换分类值:
train_t = encoder.transform(X_train)
test_t = encoder.transform(X_test)
print(train_t.head())
下面我们看到结果数据框,其中分类值已被目标均值替换:
pclass sex age sibsp parch fare cabin embarked
501 2 0.728358 13.000000 0 1 19.5000 0.304843 0.338957
588 2 0.728358 4.000000 1 1 23.0000 0.304843 0.338957
402 2 0.728358 30.000000 1 0 13.8583 0.304843 0.553073
1193 3 0.187608 29.881135 0 0 7.7250 0.304843 0.373494
686 3 0.728358 22.000000 0 0 7.7250 0.304843 0.373494
带有平滑的均值编码#
默认情况下,MeanEncoder()
计算目标值的均值而不进行混合。如果我们想要应用平滑来控制变量的基数并避免过拟合,我们可以如下设置转换器:
encoder = MeanEncoder(
variables=None,
smoothing="auto"
)
encoder.fit(X_train, y_train)
在这个例子中,我们没有指定要编码的变量。MeanEncoder()
可以自动找到分类变量,这些变量存储在其一个属性中:
encoder.variables_
下面我们看到由 MeanEncoder()
发现的分类特征:
['sex', 'cabin', 'embarked']
我们可以找到由均值编码器计算的类别映射:
encoder.encoder_dict_
请注意,这些值与未平滑处理时确定的值不同:
{'sex': {'female': 0.7275051072923914, 'male': 0.18782635616273297},
'cabin': {'A': 0.5210189753697639,
'B': 0.755161569137655,
'C': 0.5608140829162441,
'D': 0.7100896537503179,
'E': 0.7100896537503179,
'F': 0.6501082490288561,
'G': 0.47606795923242295,
'M': 0.3049458046855866,
'T': 0.0},
'embarked': {'C': 0.552100581239763,
'Missing': 1.0,
'Q': 0.3736336816011083,
'S': 0.3390242994568531}}
我们现在可以继续用数值替换分类值:
train_t = encoder.transform(X_train)
test_t = encoder.transform(X_test)
print(train_t.head())
下面我们看到包含编码特征的结果数据框:
pclass sex age sibsp parch fare cabin embarked
501 2 0.727505 13.000000 0 1 19.5000 0.304946 0.339024
588 2 0.727505 4.000000 1 1 23.0000 0.304946 0.339024
402 2 0.727505 30.000000 1 0 13.8583 0.304946 0.552101
1193 3 0.187826 29.881135 0 0 7.7250 0.304946 0.373634
686 3 0.727505 22.000000 0 0 7.7250 0.304946 0.373634
我们现在可以使用这些数据框来训练回归或分类的机器学习模型。
使用数值编码变量#
MeanEncoder()
, 以及所有 Feature-engine 编码器,默认情况下都是设计用于处理对象类型或分类类型的变量。如果你想编码数值类型的变量,你需要指示转换器忽略数据类型:
encoder = MeanEncoder(
variables=['cabin', 'pclass'],
ignore_format=True,
)
t_train = encoder.fit_transform(X_train, y_train)
t_test = encoder.transform(X_test)
在编码特征之后,我们可以使用数据集来训练机器学习算法。
在结束之前最后要注意的是,均值编码不会增加结果数据框的维度:从1个分类特征,我们得到1个编码变量。因此,这种编码方法适用于使用对特征空间大小敏感的模型的预测建模。
附加资源#
在下面的笔记本中,你可以找到更多关于 MeanEncoder()
功能的详细信息以及带有编码变量的示例图:
关于此功能及其他特征工程方法的教程,请查看以下资源:
或者阅读我们的书:
我们的书籍和课程都适合初学者和更高级的数据科学家。通过购买它们,您正在支持 Feature-engine 的主要开发者 Sole。