OneHotEncoder#
独热编码是一种用于表示分类数据的方法,其中每个类别由一个二进制变量表示。如果该类别存在,则二进制变量取值为1,否则为0。这些二进制变量也称为虚拟变量。
为了表示分类特征“is-smoker”,其类别为“Smoker”和“Non-smoker”,我们可以生成虚拟变量“Smoker”,如果该人吸烟则取1,否则取0。我们还可以生成变量“Non-smoker”,如果该人不吸烟则取1,否则取0。
下表展示了一个可能的变量“是否吸烟者”的独热编码表示:
是吸烟者 |
吸烟者 |
非吸烟者 |
---|---|---|
吸烟者 |
|
0 |
非吸烟者 |
0 |
|
非吸烟者 |
0 |
|
吸烟者 |
|
0 |
非吸烟者 |
0 |
|
对于分类变量 Country,其值为 England、Argentina 和 Germany,我们可以创建三个变量,分别命名为 England
、Argentina
和 Germany
。这些变量将在观测值分别为 England、Argentina 或 Germany 时取值为 1,否则取值为 0。
编码为 k 与 k-1 变量#
具有 k 个唯一类别的分类特征可以使用 k-1 个二进制变量进行编码。对于 Smoker
,k 为 2,因为它包含两个标签(吸烟者和非吸烟者),所以我们只需要一个二进制变量(k - 1 = 1)来捕获所有信息。
在下表中,我们看到虚拟变量 Smoker
完全代表了原始的分类值:
是吸烟者 |
吸烟者 |
---|---|
吸烟者 |
|
非吸烟者 |
0 |
非吸烟者 |
0 |
吸烟者 |
|
非吸烟者 |
0 |
对于 Country 变量,它有三个类别(k=3;英格兰、阿根廷和德国),我们需要两个(k - 1 = 2)二元变量来捕捉所有信息。该变量将完全表示如下:
国家 |
英格兰 |
阿根廷 |
---|---|---|
英格兰 |
|
0 |
阿根廷 |
0 |
|
德国 |
0 |
0 |
正如我们在前表中看到的,如果观察对象是英格兰,它将在 England
变量中显示值 1;如果观察对象是阿根廷,它将在 Argentina
变量中显示值 1;而如果观察对象是德国,它将在两个虚拟变量中都显示零。
像这样,通过查看 k-1 个虚拟变量的值,我们可以推断出每个观测值的原始分类值。
将编码为 k-1 个二进制变量非常适合线性回归模型。线性模型在拟合过程中评估所有特征,因此,使用 k-1 个变量,它们就拥有了关于原始分类变量的所有信息。
在某些情况下,我们可能更倾向于用 k 个二进制变量来编码分类变量。
如果在训练基于决策树的模型或进行特征选择时,编码为k个虚拟变量。基于决策树的模型和许多特征选择算法分别评估变量或变量组。因此,如果编码为k-1,最后一个类别将不会被检查。换句话说,我们将失去该类别中包含的信息。
二进制变量#
当一个分类变量只有两个类别时,比如我们之前的例子中的“吸烟者”,那么编码为 k-1 适用于所有目的,因为由独热编码创建的第二个虚拟变量是完全多余的。
编码流行类别#
独热编码可以显著增加特征空间,特别是当我们有许多分类特征,或者特征具有高基数时。为了控制特征空间,通常的做法是仅对每个分类变量中最频繁的类别进行编码。
当我们对最频繁的类别进行编码时,我们将为这些频繁的类别中的每一个创建二进制变量,当观察结果属于不同的、不太流行的类别时,它将在所有二进制变量中为0。请参见以下示例:
变量 |
流行1 |
受欢迎的2 |
---|---|---|
流行1 |
|
0 |
受欢迎的2 |
0 |
|
流行1 |
|
0 |
非流行的 |
0 |
0 |
受欢迎的2 |
0 |
|
不太流行 |
0 |
0 |
不受欢迎的 |
0 |
0 |
孤独的 |
0 |
0 |
如前表所示,不太受欢迎的类别通过在所有二进制变量中显示零来作为一个组表示。
OneHotEncoder#
Feature-engine 的 OneHotEncoder()
将分类数据编码为一个独热数值数据框。
OneHotEncoder()
可以编码为 k 或 k-1 个虚拟变量。行为通过 drop_last
参数指定,可以设置为 False
以生成 k 个虚拟变量,或设置为 True
以生成 k-1 个虚拟变量。
OneHotEncoder()
可以专门将二元变量编码为 k-1 个变量(即 1 个虚拟变量),同时将高基数的分类特征编码为 k 个虚拟变量。这种行为通过设置参数 drop_last_binary=True
来指定。这将确保对于数据集中的每个二元变量,即对于只有 2 个类别的每个分类变量,只创建 1 个虚拟变量。除非你怀疑该变量原则上可能取超过 2 个值,否则这是推荐的。
OneHotEncoder()
也可以为 n 个最流行的类别创建二进制变量,其中 n 由用户决定。例如,如果我们只对 6 个最流行的类别进行编码,通过设置参数 top_categories=6
,转换器将只为 6 个最频繁的类别添加二进制变量。最频繁的类别是那些观察次数最多的类别。其余类别在每个派生的虚拟变量中都将显示为零。当分类变量具有高度基数以控制特征空间的扩展时,这种行为非常有用。
注意
在编码最受欢迎的类别时,参数 drop_last
将被忽略。
Python 实现#
让我们来看一个使用 Feature-engine 的 OneHotEncoder()
和泰坦尼克号数据集进行独热编码的例子。
我们将首先导入库、函数和类,并将数据加载到 pandas 数据框中,然后将其划分为训练集和测试集:
from sklearn.model_selection import train_test_split
from feature_engine.datasets import load_titanic
from feature_engine.encoding import OneHotEncoder
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())
我们看到了训练数据的前5行如下:
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
让我们探讨一下4个分类特征的基数:
X_train[['sex', 'pclass', 'cabin', 'embarked']].nunique()
sex 2
pclass 3
cabin 9
embarked 4
dtype: int64
我们看到变量 sex 有 2 个类别,pclass 有 3 个类别,变量 cabin 有 9 个类别,变量 embarked 有 4 个类别。
现在让我们设置 OneHotEncoder 来将 2 个分类变量编码为 k-1 个虚拟变量:
encoder = OneHotEncoder(
variables=['cabin', 'embarked'],
drop_last=True,
)
encoder.fit(X_train)
通过 fit()
,编码器学习变量的类别,这些类别存储在属性 encoder_dict_
中。
encoder.encoder_dict_
{'cabin': ['M', 'E', 'C', 'D', 'B', 'A', 'F', 'T'],
'embarked': ['S', 'C', 'Q']}
encoder_dict_
包含了每个分类变量将被虚拟变量表示的类别。
通过转换,我们继续对变量进行编码。注意,默认情况下,OneHotEncoder()
会丢弃原始的分类变量,这些变量现在由独热编码数组表示。
train_t = encoder.transform(X_train)
test_t = encoder.transform(X_test)
print(train_t.head())
下面我们看到添加到数据集中的独热虚拟变量,原始变量不再存在于数据框中:
pclass sex age sibsp parch fare cabin_M cabin_E \
501 2 female 13.000000 0 1 19.5000 1 0
588 2 female 4.000000 1 1 23.0000 1 0
402 2 female 30.000000 1 0 13.8583 1 0
1193 3 male 29.881135 0 0 7.7250 1 0
686 3 female 22.000000 0 0 7.7250 1 0
cabin_C cabin_D cabin_B cabin_A cabin_F cabin_T embarked_S \
501 0 0 0 0 0 0 1
588 0 0 0 0 0 0 1
402 0 0 0 0 0 0 0
1193 0 0 0 0 0 0 0
686 0 0 0 0 0 0 0
embarked_C embarked_Q
501 0 0
588 0 0
402 1 0
1193 0 1
686 0 1
自动查找分类变量#
Feature-engine 的 OneHotEncoder()
可以自动查找并编码 pandas 数据框中的所有分类特征。让我们通过一个例子来展示这一点。
让我们设置 OneHotEncoder 以查找并编码所有分类特征:
encoder = OneHotEncoder(
variables=None,
drop_last=True,
)
encoder.fit(X_train)
通过fit,编码器找到分类特征并识别其唯一类别。我们可以这样找到分类变量:
encoder.variables_
['sex', 'cabin', 'embarked']
我们可以这样识别每个变量的唯一类别:
encoder.encoder_dict_
{'sex': ['female'],
'cabin': ['M', 'E', 'C', 'D', 'B', 'A', 'F', 'T'],
'embarked': ['S', 'C', 'Q']}
我们现在可以对分类变量进行编码:
train_t = encoder.transform(X_train)
test_t = encoder.transform(X_test)
print(train_t.head())
在这里我们看到生成的数据框:
pclass age sibsp parch fare sex_female cabin_M cabin_E \
501 2 13.000000 0 1 19.5000 1 1 0
588 2 4.000000 1 1 23.0000 1 1 0
402 2 30.000000 1 0 13.8583 1 1 0
1193 3 29.881135 0 0 7.7250 0 1 0
686 3 22.000000 0 0 7.7250 1 1 0
cabin_C cabin_D cabin_B cabin_A cabin_F cabin_T embarked_S \
501 0 0 0 0 0 0 1
588 0 0 0 0 0 0 1
402 0 0 0 0 0 0 0
1193 0 0 0 0 0 0 0
686 0 0 0 0 0 0 0
embarked_C embarked_Q
501 0 0
588 0 0
402 1 0
1193 0 1
686 0 1
编码数值类型的变量#
默认情况下,Feature-engine 的 OneHotEncoder()
只会编码分类特征。如果你尝试编码数值类型的变量,它会引发错误。为了避免这个错误,你可以指示编码器忽略数据类型格式,如下所示:
enc = OneHotEncoder(
variables=['pclass'],
drop_last=True,
ignore_format=True,
)
enc.fit(X_train)
train_t = enc.transform(X_train)
test_t = enc.transform(X_test)
print(train_t.head())
注意,pclass 原本是数值而非字符串,并且通过转换器被独热编码为两个虚拟变量:
sex age sibsp parch fare cabin embarked pclass_2 \
501 female 13.000000 0 1 19.5000 M S 1
588 female 4.000000 1 1 23.0000 M S 1
402 female 30.000000 1 0 13.8583 M C 1
1193 male 29.881135 0 0 7.7250 M Q 0
686 female 22.000000 0 0 7.7250 M Q 0
pclass_3
501 0
588 0
402 0
1193 1
686 1
将二进制变量编码为1个虚拟变量#
使用 Feature-engine 的 OneHotEncoder()
,我们可以通过如下设置编码器,将所有分类变量编码为 k 个虚拟变量,将二进制变量编码为 k-1 个虚拟变量:
ohe = OneHotEncoder(
variables=['sex', 'cabin','embarked'],
drop_last=False,
drop_last_binary=True,
)
train_t = ohe.fit_transform(X_train)
test_t = ohe.transform(X_test)
print(train_t.head())
正如我们在以下输入中看到的,对于变量性别,我们只有一个虚拟变量,而对于所有其他变量,我们有 k 个虚拟变量:
pclass age sibsp parch fare sex_female cabin_M cabin_E \
501 2 13.000000 0 1 19.5000 1 1 0
588 2 4.000000 1 1 23.0000 1 1 0
402 2 30.000000 1 0 13.8583 1 1 0
1193 3 29.881135 0 0 7.7250 0 1 0
686 3 22.000000 0 0 7.7250 1 1 0
cabin_C cabin_D cabin_B cabin_A cabin_F cabin_T cabin_G \
501 0 0 0 0 0 0 0
588 0 0 0 0 0 0 0
402 0 0 0 0 0 0 0
1193 0 0 0 0 0 0 0
686 0 0 0 0 0 0 0
embarked_S embarked_C embarked_Q embarked_Missing
501 1 0 0 0
588 1 0 0 0
402 0 1 0 0
1193 0 0 1 0
编码频繁类别#
如果分类变量具有高基数,我们可能在独热编码后得到非常大的数据集。此外,如果这些变量中的一些相当恒定或非常相似,我们可能会得到高度相关(如果不是完全相同)的独热编码特征。为了避免这种行为,我们可以只编码最频繁的类别。
为了编码每个分类列中出现频率最高的两个类别,我们将转换器设置如下:
ohe = OneHotEncoder(
top_categories=2,
variables=['pclass', 'cabin', 'embarked'],
ignore_format=True,
)
train_t = ohe.fit_transform(X_train)
test_t = ohe.transform(X_test)
print(train_t.head())
正如我们在结果数据框中看到的,我们为每个变量只创建了2个虚拟变量:
sex age sibsp parch fare pclass_3 pclass_1 cabin_M \
501 female 13.000000 0 1 19.5000 0 0 1
588 female 4.000000 1 1 23.0000 0 0 1
402 female 30.000000 1 0 13.8583 0 0 1
1193 male 29.881135 0 0 7.7250 1 0 1
686 female 22.000000 0 0 7.7250 1 0 1
cabin_C embarked_S embarked_C
501 0 1 0
588 0 1 0
402 0 0 1
1193 0 0 0
686 0 0 0
最后,如果我们想获取结果数据框中的列名,可以执行以下操作:
encoder.get_feature_names_out()
我们看到了下面的列名:
['sex',
'age',
'sibsp',
'parch',
'fare',
'pclass_3',
'pclass_1',
'cabin_M',
'cabin_C',
'embarked_S',
'embarked_C']
考虑事项#
将分类变量编码为 k 个虚拟变量,将自动处理未知类别。在训练期间未见过的特征将在所有虚拟变量中显示为零。
将分类特征编码为 k-1 个虚拟变量,会导致未见过的数据被视为被丢弃的类别。
编码顶级类别将使未见类别成为不太受欢迎类别组的一部分。
如果你在数据中添加了大量虚拟变量,许多变量可能会相同或高度相关。考虑使用 选择模块 中的转换器来删除相同和相关的特征。
对于数据科学中使用的替代编码方法,请查看 OrdinalEncoder()
以及其他包含在 编码模块 中的编码器。
教程、书籍和课程#
有关 OneHotEncoder()
功能的更多详细信息,请访问:
关于此方法及其他数据预处理方法的教程,请查看我们的在线课程:
或者阅读我们的书:
我们的书籍和课程都适合初学者和更高级的数据科学家。通过购买它们,您正在支持 Feature-engine 的主要开发者 Sole。