HOWTO · NumPy
Python 中 NumPy 数组的独热编码
本教程演示如何对 Python 中的 NumPy 数组进行独热编码
本页内容
独热编码用一行指示值表示一个类别:输入标签所属类别的列为 1,其余类别列为 0。当下游工具需要这种形式的数值特征或分类目标时,这种表示方式很有用;但并非每种模型都需要它。本指南介绍如何从 NumPy 数组生成这些行,以及 NumPy、scikit-learn、pandas 和 Keras 各自的输出有何不同。
独热编码如何表示类别
独热数组为每个输入项生成一行,为每个类别生成一列。该行中对应的列为 1,其余列为 0。class_count 决定输出宽度。在本例中,ID 0 到 3 表示完整的类别集合,因此结果有四列。运行以下示例:
本节中的代码以及显示的标签或输出仍为英文。
import numpy as np
ids = np.array([1, 0, 3])
class_count = 4
rows = np.zeros((ids.size, class_count), dtype=int)
rows[np.arange(ids.size), ids] = 1
print(rows)
[[0 1 0 0]
[1 0 0 0]
[0 0 0 1]]
这里的输入是整数类别 ID,而不是任意类别名称。每个 ID 都必须是有效列索引,即大于或等于 0 且小于 class_count。应根据任务中的完整类别集合来设置该值,包括当前样本中可能没有出现的类别。如果只使用观测到的最大 ID,当某个已知类别没有出现在当前数据中时,输出可能会缺少该类别。对于 "cat" 或 "blue" 这样的标签,应先将每个类别映射为稳定的整数 ID,或者使用能够直接接受类别标签的编码器。
使用 NumPy 索引赋值和 numpy.eye() 创建独热行
已知所需输出宽度时,索引赋值很实用:先分配一个矩阵,每个标签对应一行、每个类别对应一列,再将每行中的一个位置设为 1。上面的代码采用了这种方法。结果是一个 NumPy 整数数组,行的顺序与输入一致。请将 ID 到类别的映射与数组一并保存,以便明确每个输出列所代表的类别。
numpy.eye() 提供了更简洁的替代方法。它会创建单位矩阵,每行有一列为 1;用 ID 数组索引该矩阵,就能选出所需的行。传入相同的显式类别数量,这样即使某些 ID 未出现,矩阵仍会包含完整类别集合。这两种方法都要求 ID 是允许范围内的非负整数。如果标签是字符串、负数或 10 和 40 这类不连续编号,应先将它们映射为连续 ID,而不要直接把这些值用作数组位置。
本节中的代码以及显示的标签或输出仍为英文。
import numpy as np
ids = np.array([1, 0, 3])
class_count = 4
rows = np.eye(class_count, dtype=int)[ids]
print(rows)
[[0 1 0 0]
[1 0 0 0]
[0 0 0 1]]
使用 scikit-learn 的 LabelBinarizer 编码标签
如果需要从标签中学习类别到列的映射,并将该映射一致地用于后续数据,可以使用 LabelBinarizer。先用完整的训练类别集合拟合编码器,再重复使用已拟合的编码器转换标签。与直接使用 NumPy 索引不同,它既可以处理字符串,也可以处理整数。对于四个已拟合类别的多类别转换,结果每个类别对应一列;二分类则返回一列,因此输出宽度取决于类别配置。
在 scikit-learn 项目中,可调用 encoder.fit(class_labels) 和 encoder.transform(labels);classes_ 可查看列的顺序。如果安装了 scikit-learn,示例会使用 LabelBinarizer;否则会使用标准库回退实现演示相同的映射,使示例在没有该软件包时也能运行:
本节中的代码以及显示的标签或输出仍为英文。
class_labels = ["blue", "green", "red", "yellow"]
labels = ["green", "red", "blue"]
try:
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()
encoder.fit(class_labels)
rows = encoder.transform(labels).tolist()
except ModuleNotFoundError:
class_to_column = {label: index for index, label in enumerate(class_labels)}
rows = []
for label in labels:
row = [0] * len(class_labels)
row[class_to_column[label]] = 1
rows.append(row)
print(rows)
[[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0]]
使用 pandas.get_dummies() 创建指示列
当后续步骤使用 DataFrame,且需要带名称的类别列时,pandas.get_dummies() 很方便。它会为输入值中出现的每个类别创建一个指示列,每个输入项对应一行。与上面的 NumPy 数组不同,输出仍是 pandas DataFrame。默认情况下,输入中没有出现的类别不会生成列;如果不同数据集的列必须与另行确定的类别集合一致,请显式定义类别,或对结果重新索引以补齐列。
本节中的代码以及显示的标签或输出仍为英文。
import numpy as np
import pandas as pd
labels = np.array(["green", "red", "blue"])
indicators = pd.get_dummies(labels)
print(indicators)
blue green red
0 False True False
1 False False True
2 True False False
此方法可直接接受类别标签,无需自行建立数值映射。列对应数据中实际出现的类别,因此合并不同批次的结果时,应保留或预先定义列顺序。表格工作流适合使用此方法;如果接收方 API 需要 NumPy 数组或已拟合的编码器,则应选择对应形式。
使用 Keras to_categorical() 创建独热行
Keras 的 to_categorical() 会将整数类别 ID 转换为由独热行组成的密集数组。请根据完整类别集合的大小显式提供 num_classes。输入标签的数量不等于类别数量:例如,三个观测值可能属于一个包含四个类别的问题。与直接使用 NumPy 索引一样,ID 必须是小于该类别数量的非负整数。
显式传入完整类别数量,让每行都有四列。如果安装了 Keras,示例会调用 Keras 的 to_categorical();否则,标准库回退实现仍可运行相同的演示:
本节中的代码以及显示的标签或输出仍为英文。
ids = [1, 0, 3]
class_count = 4
try:
from keras.utils import to_categorical
rows = to_categorical(ids, num_classes=class_count).tolist()
except ModuleNotFoundError:
rows = []
for class_id in ids:
row = [0.0] * class_count
row[class_id] = 1.0
rows.append(row)
print(rows)
[[0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
Keras 返回密集数组,适合需要分类目标的代码。它使用数值 ID,而不是类别字符串;调用前应先将任意标签映射为稳定的整数方案。如果后续步骤属于 Keras 工作流,可选择此方法;需要已拟合的标签到列映射时,可使用 LabelBinarizer;偏好 DataFrame 输出时,可使用 get_dummies();如果整数 ID 和数组本身就很适合当前任务,则可直接使用 NumPy 操作。