OneHotEncoder#

独热编码是一种用于表示分类数据的方法,其中每个类别由一个二进制变量表示。如果该类别存在,则二进制变量取值为1,否则为0。这些二进制变量也称为虚拟变量。

为了表示分类特征“is-smoker”,其类别为“Smoker”和“Non-smoker”,我们可以生成虚拟变量“Smoker”,如果该人吸烟则取1,否则取0。我们还可以生成变量“Non-smoker”,如果该人不吸烟则取1,否则取0。

下表展示了一个可能的变量“是否吸烟者”的独热编码表示:

是吸烟者

吸烟者

非吸烟者

吸烟者

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

0

非吸烟者

0

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

非吸烟者

0

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

吸烟者

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

0

非吸烟者

0

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

对于分类变量 Country,其值为 EnglandArgentinaGermany,我们可以创建三个变量,分别命名为 EnglandArgentinaGermany。这些变量将在观测值分别为 England、Argentina 或 Germany 时取值为 1,否则取值为 0。

编码为 k 与 k-1 变量#

具有 k 个唯一类别的分类特征可以使用 k-1 个二进制变量进行编码。对于 Smoker,k 为 2,因为它包含两个标签(吸烟者和非吸烟者),所以我们只需要一个二进制变量(k - 1 = 1)来捕获所有信息。

在下表中,我们看到虚拟变量 Smoker 完全代表了原始的分类值:

是吸烟者

吸烟者

吸烟者

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

非吸烟者

0

非吸烟者

0

吸烟者

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

非吸烟者

0

对于 Country 变量,它有三个类别(k=3;英格兰、阿根廷和德国),我们需要两个(k - 1 = 2)二元变量来捕捉所有信息。该变量将完全表示如下:

国家

英格兰

阿根廷

英格兰

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

0

阿根廷

0

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

德国

0

0

正如我们在前表中看到的,如果观察对象是英格兰,它将在 England 变量中显示值 1;如果观察对象是阿根廷,它将在 Argentina 变量中显示值 1;而如果观察对象是德国,它将在两个虚拟变量中都显示零。

像这样,通过查看 k-1 个虚拟变量的值,我们可以推断出每个观测值的原始分类值。

将编码为 k-1 个二进制变量非常适合线性回归模型。线性模型在拟合过程中评估所有特征,因此,使用 k-1 个变量,它们就拥有了关于原始分类变量的所有信息。

在某些情况下,我们可能更倾向于用 k 个二进制变量来编码分类变量。

如果在训练基于决策树的模型或进行特征选择时,编码为k个虚拟变量。基于决策树的模型和许多特征选择算法分别评估变量或变量组。因此,如果编码为k-1,最后一个类别将不会被检查。换句话说,我们将失去该类别中包含的信息。

二进制变量#

当一个分类变量只有两个类别时,比如我们之前的例子中的“吸烟者”,那么编码为 k-1 适用于所有目的,因为由独热编码创建的第二个虚拟变量是完全多余的。

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() 功能的更多详细信息,请访问:

关于此方法及其他数据预处理方法的教程,请查看我们的在线课程:

../../_images/feml.png

机器学习的特征工程#











或者阅读我们的书:

../../_images/cookbook.png

Python 特征工程手册#














我们的书籍和课程都适合初学者和更高级的数据科学家。通过购买它们,您正在支持 Feature-engine 的主要开发者 Sole。