4. 编码分类变量:从标签到数值的四种核心映射

分类变量(Categorical Variable)是数据中最常见的非数值特征,但绝大多数机器学习模型只能处理数值输入。因此,将分类变量转换为数值表示是特征工程中绕不开的基础操作。本章详细拆解四种最常用的编码方法:标签编码、独热编码、目标编码和频率编码,并给出各自适用场景与避坑指南。

核心原则:编码方式的选择取决于分类变量的基数(唯一类别数量)、是否有序、以及下游模型对数值尺度的敏感程度。没有“万能编码”,只有“最合适编码”。

4.1 四种编码方法总览

编码方法 映射方式 输出维度 是否保留顺序信息 典型适用模型 主要风险
标签编码 每个类别 → 一个整数(0,1,2,…) 1 列 是(人为赋予顺序) 树模型(决策树、随机森林、XGBoost) 误导线性模型产生虚假顺序
独热编码 每个类别 → 一个二进制向量 K 列(K 为类别数) 否 线性模型、神经网络、KNN、SVM 高基数时维度爆炸、稀疏性
目标编码 每个类别 → 目标变量均值(+平滑) 1 列 否(基于目标统计) 树模型、线性模型(需正则化) 过拟合、目标泄露、需交叉验证
频率编码 每个类别 → 该类别在样本中的出现频次/频率 1 列 否(基于分布) 树模型、线性模型 不同类别可能映射到相同频率值

4.2 标签编码(Label Encoding)

标签编码将每个类别映射为一个从 0 开始的整数。例如颜色特征:红→0,蓝→1,绿→2。实现简单,输出仅占一列,内存开销极小。

⚠️ 关键陷阱:标签编码引入了人为的数值顺序。线性回归、逻辑回归、SVM 等模型会认为“绿(2)是红(0)的两倍”,这通常毫无意义。因此标签编码仅推荐用于树模型(决策树、随机森林、梯度提升树),因为树模型基于分裂点而非数值距离做决策。
# 标签编码示例(Python + scikit-learn)
from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()
data['color_encoded'] = le.fit_transform(data['color'])
# 输出:红→0, 蓝→1, 绿→2

4.3 独热编码(One-Hot Encoding)

独热编码为每个类别创建一个独立的二进制列(0 或 1)。例如颜色特征有 3 个类别,则生成 3 列:is_red、is_blue、is_green。每行只有一个位置为 1,其余为 0。

优点:完全消除顺序误导,适合线性模型和神经网络。
缺点:当类别基数高(如城市名、用户ID)时,特征维度急剧膨胀,导致稀疏矩阵和计算开销剧增。

# 独热编码示例(Python + pandas)
import pandas as pd

one_hot = pd.get_dummies(data['color'], prefix='color')
data = pd.concat([data, one_hot], axis=1)
# 输出:color_red, color_blue, color_green 三列
降维技巧:对于高基数独热编码,可结合主成分分析(PCA)或截断 SVD 进行降维;或只保留出现频次最高的前 N 个类别,其余归为“其他”类。

4.4 目标编码(Target Encoding)

目标编码用每个类别对应的目标变量均值来替换原始类别。例如在房价预测中,计算每个城市的历史平均房价,然后用该均值编码城市特征。为缓解过拟合,通常加入平滑(Smoothing)或交叉验证。

平滑公式:

encoded_value = (n_i * mean_i + m * global_mean) / (n_i + m)
# n_i:类别 i 的样本数
# mean_i:类别 i 的目标均值
# m:平滑参数(控制全局均值的权重)
⚠️ 目标泄露风险:如果在训练集上直接计算目标均值并用于同一训练集,模型会学到“类别→目标”的捷径,导致严重过拟合。正确做法是使用交叉验证目标编码:在每一折中,用其他折的数据计算目标均值,仅对当前折的样本进行编码。
# 目标编码示例(Python + category_encoders)
import category_encoders as ce

encoder = ce.TargetEncoder(cols=['city'], smoothing=10)
data['city_encoded'] = encoder.fit_transform(data['city'], data['price'])

4.5 频率编码(Frequency Encoding)

频率编码将每个类别替换为该类别在数据集中出现的频次或频率。例如“北京”出现 500 次,“上海”出现 200 次,则分别编码为 500 和 200。这种方法保留了类别的分布信息,且输出仅一列,不会增加维度。

适用场景:当类别出现频次与目标变量存在相关性时(例如高频类别往往对应更高或更低的目标值),频率编码非常有效。它天然对稀有类别进行了压缩,减少了噪声影响。

# 频率编码示例(Python + pandas)
freq_map = data['city'].value_counts(normalize=True)  # 频率
data['city_freq'] = data['city'].map(freq_map)

# 也可使用频次(绝对计数)
freq_map_abs = data['city'].value_counts()
data['city_freq_abs'] = data['city'].map(freq_map_abs)

4.6 编码方法选择决策流程

分类变量编码方法选择决策流程 分类变量需要编码 类别基数(K)是否 ≤ 10? 是(低基数) 独热编码(推荐) 否(高基数) 考虑降维或替代编码 使用树模型还是线性模型? 树模型 标签编码 / 目标编码 线性模型 频率编码 / 目标编码(CV) 💡 经验法则 低基数 → 独热编码;高基数 + 树模型 → 标签/目标编码;高基数 + 线性模型 → 频率/目标编码(带CV)

4.7 实战对比:不同编码对模型的影响

以下表格展示了一个包含 3 个类别的城市特征,分别使用四种编码后的数值结果:

原始城市 标签编码 独热编码(3列) 目标编码(目标:房价) 频率编码
北京 0 [1,0,0] 85000(均价) 0.45(频率)
上海 1 [0,1,0] 78000(均价) 0.35(频率)
广州 2 [0,0,1] 62000(均价) 0.20(频率)
最佳实践总结:
  • 线性模型 / 神经网络:优先使用独热编码(低基数)或频率编码(高基数)。
  • 树模型:标签编码或目标编码通常效果更好,且内存效率高。
  • 高基数(K > 50):避免直接独热编码;考虑目标编码(带交叉验证)或频率编码。
  • 有序分类(如学历:小学 < 中学 < 大学):标签编码天然保留顺序,是合理选择。
  • 测试集处理:所有编码的映射字典必须从训练集学习,并应用到测试集,避免数据泄露。