4. 类别特征编码:Label Encoding、One-Hot Encoding、Target Encoding 的原理与优缺点

在量化金融与机器学习建模中,原始数据常包含大量类别特征,如行业代码、股票板块、交易员ID、评级等级等。模型(尤其是线性模型与神经网络)无法直接处理字符串或枚举值,必须将其转换为数值形式。本章深入剖析三种最常用的类别编码方法:标签编码、独热编码与目标编码,从原理、数学表达、适用场景到优缺点进行系统对比。

核心原则:编码方式的选择直接影响模型对类别间关系的假设。错误的编码可能引入虚假的序关系或导致维度灾难,在因子回测中尤其需要谨慎。

4.1 Label Encoding(标签编码)

原理:将每个类别映射为一个唯一的整数,通常从 0 或 1 开始递增。例如:{'银行':0, '地产':1, '科技':2, '医药':3}。

数学表达:给定类别集合 C = {c₁, c₂, ..., cₖ},定义映射函数 f: C → {0, 1, ..., k-1},使得 f(cᵢ) = i-1(按某种顺序排列)。

# Python 示例:Label Encoding
from sklearn.preprocessing import LabelEncoder

sectors = ['银行', '地产', '科技', '医药', '银行', '科技']
le = LabelEncoder()
encoded = le.fit_transform(sectors)
print(encoded)  # 输出: [0 1 2 3 0 2]
print(le.classes_)  # 输出: ['地产' '科技' '医药' '银行']
维度 优点 缺点
空间效率 仅需一列,内存占用极低 —
模型兼容性 适用于树模型(决策树、随机森林、XGBoost、LightGBM) 线性模型、KNN、SVM 会误认为类别间存在序关系(如 3 > 2)
信息保留 简单快速,适合高基数类别(如股票代码) 丢失类别间的平等性,引入虚假的数值距离
量化金融场景 行业板块、交易所代码等树模型因子 不适用于线性因子模型(如 Fama-French 回归)
⚠ 常见陷阱:在量化回测中,若对时间序列数据使用 Label Encoding,不同时间段的同一类别可能被赋予不同整数(如 fit 顺序变化),导致模型无法跨时间泛化。务必在训练集上 fit 编码器,并持久化映射字典。

4.2 One-Hot Encoding(独热编码)

原理:为每个类别创建一个二元虚拟变量(0/1),每个样本在对应类别的位置为 1,其余为 0。若有 k 个类别,则生成 k 列(或 k-1 列,若使用 drop_first)。

数学表达:对于类别 cᵢ,生成向量 v ∈ {0,1}ᵏ,其中 vᵢ = 1,vⱼ = 0(j ≠ i)。

# Python 示例:One-Hot Encoding
import pandas as pd

sectors = pd.Series(['银行', '地产', '科技', '医药', '银行', '科技'])
one_hot = pd.get_dummies(sectors, prefix='sector')
print(one_hot.head())
#    sector_地产  sector_科技  sector_医药  sector_银行
# 0       0       0       0       1
# 1       1       0       0       0
# 2       0       1       0       0
# 3       0       0       1       0
# 4       0       0       0       1
# 5       0       1       0       0
维度 优点 缺点
模型兼容性 适用于线性模型、神经网络、KNN、SVM 等所有模型 —
信息保留 完全保留类别间的平等性,无虚假序关系 当 k 很大时,产生高维稀疏矩阵,导致维度灾难
空间效率 — 每增加一个类别就增加一列,内存开销大
量化金融场景 低基数类别(如季度、月份、评级等级 ≤ 10 类) 高基数类别(如股票代码 3000+)几乎不可用
💡 实践建议:在量化因子工程中,对于基数 ≤ 10 的类别(如行业板块、市场状态),优先使用 One-Hot Encoding。对于基数在 10~50 之间的类别,可结合 PCA 或嵌入层降维。基数 > 50 时,考虑 Target Encoding 或 Count Encoding。
类别特征编码:三种核心方法对比 原始类别:A, B, C, D Label Encoding A→0, B→1, C→2, D→3 One-Hot Encoding A→[1,0,0,0], B→[0,1,0,0] Target Encoding A→mean(y|A), B→mean(y|B) ⚠ 缺点 • 引入虚假序关系 • 线性模型不适用 • 树模型可接受 • 空间效率高 ⚠ 缺点 • 高基数导致维度灾难 • 内存开销大 • 稀疏矩阵 • 适合低基数 ⚠ 缺点 • 易过拟合 • 需交叉验证 • 数据泄露风险 • 需平滑处理

4.3 Target Encoding(目标编码)

原理:用目标变量(如未来收益率、涨跌标签)在各类别上的统计量(通常是均值)来替换原始类别。例如,若“银行”板块的历史平均收益率为 0.02,则所有“银行”样本的该特征值替换为 0.02。

数学表达:对于类别 c,编码值 = (∑_{i: cᵢ=c} yᵢ + m × global_mean) / (n_c + m),其中 n_c 是类别 c 的样本数,m 是平滑参数(防止小样本过拟合)。

# Python 示例:Target Encoding(带平滑)
import pandas as pd
import numpy as np

def target_encoding(df, feature, target, m=10):
    global_mean = df[target].mean()
    stats = df.groupby(feature)[target].agg(['count', 'mean'])
    stats['encoded'] = (stats['count'] * stats['mean'] + m * global_mean) / (stats['count'] + m)
    return df[feature].map(stats['encoded'])

# 示例数据
df = pd.DataFrame({
    'sector': ['银行', '银行', '科技', '科技', '科技', '医药'],
    'return': [0.01, 0.02, 0.05, 0.06, 0.04, -0.01]
})
df['sector_encoded'] = target_encoding(df, 'sector', 'return', m=5)
print(df)
#   sector  return  sector_encoded
# 0   银行    0.01        0.015714
# 1   银行    0.02        0.015714
# 2   科技    0.05        0.050000
# 3   科技    0.06        0.050000
# 4   科技    0.04        0.050000
# 5   医药   -0.01       -0.010000
维度 优点 缺点
维度效率 仅一列,适合高基数类别(如股票代码、分析师ID) —
信息密度 直接编码目标相关性,提升模型预测能力 极易过拟合,尤其在小样本类别上
数据泄露风险 — 若使用全样本计算均值,则引入未来信息,导致回测虚高
量化金融场景 股票代码、行业子类、分析师评级等高频高基类别 必须使用交叉验证或时序折叠计算,避免数据泄露
⚠ 量化金融关键约束:在时间序列回测中,Target Encoding 必须使用 时序折叠(Time Series Split) 或 Expanding Window 计算。即:对于 t 时刻的样本,只能使用 t 时刻之前的数据计算目标均值。否则,未来信息会污染编码,导致回测结果不可信。
Target Encoding 时序折叠流程(避免数据泄露) 时间轴(t₁ → t₂ → t₃ → ... → tₙ) 训练窗口 1(t₁~tₖ) 编码 tₖ₊₁ 训练窗口 2(t₁~tₖ₊₁) 编码 tₖ₊₂ ✅ 正确做法 • 每个时间点只用历史数据 • 编码值随新数据动态更新 • 避免未来信息污染 • 配合平滑参数 m 控制过拟合 • 回测结果更可信

4.4 三种编码方法综合对比

对比维度 Label Encoding One-Hot Encoding Target Encoding
输出维度 1 列 k 列(k 为类别数) 1 列
序关系假设 有(虚假序) 无 无(基于目标统计)
适用模型 树模型 所有模型 所有模型(需防过拟合)
高基数处理 优秀 差(维度爆炸) 优秀
过拟合风险 低 低 高(需平滑+交叉验证)
数据泄露风险 无 无 高(必须时序折叠)
量化金融推荐度 ★★☆☆☆(仅树模型) ★★★★☆(低基数) ★★★★★(高基数,需谨慎)
决策树参考:在量化因子工程中,建议按以下优先级选择编码方式:
1. 若类别基数 ≤ 10 → One-Hot Encoding
2. 若类别基数 10~50 且使用树模型 → Label Encoding 或 Target Encoding
3. 若类别基数 > 50 → Target Encoding(必须使用时序折叠 + 平滑参数 m ≥ 10)
4. 若模型为线性模型或神经网络且基数 > 50 → 考虑 Count Encoding 或 Embedding

4.5 量化金融中的特殊注意事项

  • 时序依赖性:所有编码器必须在训练集上 fit,并持久化映射字典。测试集/验证集使用相同的映射,避免因新类别导致编码失败。
  • 新类别处理:在线上推理时,可能出现训练集中未出现的新类别。建议设置 unknown_value 或使用全局均值/众数填充。
  • 多重共线性:One-Hot Encoding 在使用线性模型时,需删除第一列(drop_first)以避免完全多重共线性。
  • 内存优化:对于高基数 One-Hot,使用稀疏矩阵存储(如 scipy.sparse)可大幅降低内存占用。
  • 回测一致性:在 Walk-Forward 回测中,每个窗口需独立 fit 编码器,并仅使用窗口内的历史数据计算 Target Encoding 的统计量。
💡 进阶技巧:对于超高基数类别(如股票代码 5000+),可尝试 Count Encoding(用类别出现频次代替类别本身)或 Weight of Evidence (WoE) 编码,后者在信用评分模型中广泛使用,对二分类目标变量效果显著。