4. 数据预处理基础:缺失值处理、类别特征编码与数据标准化
在将数据输入 XGBoost 模型之前,原始数据通常包含缺失值、非数值型类别特征以及量纲不一致的数值特征。本章将系统讲解这三类核心预处理操作,确保数据质量满足 XGBoost 的训练要求。
4.1 缺失值处理
XGBoost 原生支持处理缺失值,在训练过程中会自动学习缺失值的最佳分裂方向。然而,在实际业务场景中,合理的缺失值填充仍能提升模型稳定性与解释性。常见的缺失值处理策略如下:
| 策略 | 适用场景 | 实现方式 | 注意事项 |
|---|---|---|---|
| 直接保留 | 树模型默认路径 | XGBoost 自动处理 | 缺失比例过高时可能影响分裂质量 |
| 均值/中位数填充 | 数值型特征,缺失率 < 20% | df.fillna(df.mean()) |
会降低特征方差,可能引入偏差 |
| 众数填充 | 类别型特征 | df.fillna(df.mode()[0]) |
适用于分布集中的类别 |
| 前向/后向填充 | 时间序列数据 | df.fillna(method='ffill') |
需确保时间顺序有意义 |
| KNN 填充 | 小数据集,特征间相关性较强 | KNNImputer |
计算成本较高,需标准化 |
实践建议:在 A/B 测试场景中,若缺失值在实验组与对照组分布不均,建议先分析缺失机制(MCAR/MAR/MNAR),再选择填充策略,避免引入系统性偏差。
import pandas as pd
import numpy as np
# 示例:均值填充与中位数填充
df = pd.DataFrame({'feature_a': [1.2, np.nan, 3.4, 2.1, np.nan],
'feature_b': [10, 15, np.nan, 12, 18]})
# 均值填充
df['feature_a'] = df['feature_a'].fillna(df['feature_a'].mean())
# 中位数填充
df['feature_b'] = df['feature_b'].fillna(df['feature_b'].median())
print(df)
4.2 类别特征编码
XGBoost 要求输入特征为数值型。类别特征必须转换为数值表示,常用的编码方式包括 LabelEncoder 和 OneHotEncoder。两种编码方式在信息保留与维度控制上各有优劣。
核心对比:LabelEncoder 将类别映射为整数(0, 1, 2…),适合有序类别;OneHotEncoder 将每个类别扩展为二进制向量,适合无序类别,但会显著增加特征维度。
| 编码方式 | 输出维度 | 有序性假设 | XGBoost 适用性 | 典型场景 |
|---|---|---|---|---|
| LabelEncoder | 1 列 | 隐含顺序 | 高基数类别(如城市 ID) | 有序类别(学历:小学<中学<大学) |
| OneHotEncoder | k 列(k 为类别数) | 无顺序 | 低基数类别(如性别、颜色) | 无序类别(国家、品牌) |
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
import pandas as pd
# 示例数据
df = pd.DataFrame({'color': ['red', 'blue', 'green', 'blue', 'red']})
# LabelEncoder
le = LabelEncoder()
df['color_label'] = le.fit_transform(df['color'])
print("LabelEncoder 结果:", df['color_label'].tolist())
# OneHotEncoder
ohe = OneHotEncoder(sparse_output=False)
encoded = ohe.fit_transform(df[['color']])
df_ohe = pd.DataFrame(encoded, columns=ohe.get_feature_names_out(['color']))
print("OneHotEncoder 结果:\n", df_ohe)
避坑提示:LabelEncoder 输出的整数编码(如 0, 1, 2)会被 XGBoost 视为连续数值,可能引入虚假的数值大小关系。对于无序类别,优先使用 OneHotEncoder 或 XGBoost 1.6+ 版本内置的
enable_categorical 参数直接处理类别特征。
4.3 数据标准化
虽然 XGBoost 作为树模型对特征量纲不敏感,但在以下场景中标准化仍然必要:
- 特征重要性解释:标准化后特征系数或增益可比性更强。
- 结合正则化:当使用 L1/L2 正则化时,标准化可防止量纲大的特征主导惩罚项。
- 与线性模型集成:在 Stacking 或 Blending 中,若基模型包含线性模型,标准化是前置条件。
常用的标准化方法包括 Z-score 标准化(StandardScaler)和 Min-Max 缩放(MinMaxScaler)。
| 方法 | 公式 | 输出范围 | 适用场景 |
|---|---|---|---|
| StandardScaler | \( z = \frac{x - \mu}{\sigma} \) | 均值 0,标准差 1 | 数据近似正态分布,或需保留异常值信息 |
| MinMaxScaler | \( x' = \frac{x - min}{max - min} \) | [0, 1] | 数据有明确边界,或需将特征缩放到同一量级 |
from sklearn.preprocessing import StandardScaler, MinMaxScaler
import numpy as np
# 示例数据
data = np.array([[100, 0.001],
[200, 0.002],
[150, 0.0015]])
# StandardScaler
scaler_std = StandardScaler()
data_std = scaler_std.fit_transform(data)
print("StandardScaler:\n", data_std)
# MinMaxScaler
scaler_mm = MinMaxScaler()
data_mm = scaler_mm.fit_transform(data)
print("MinMaxScaler:\n", data_mm)
4.4 数据预处理流程总览
下图展示了从原始数据到 XGBoost 模型输入的完整预处理流水线,涵盖缺失值处理、类别编码与标准化三个核心环节。
流水线整合建议:在实际项目中,建议使用
sklearn.pipeline.Pipeline 将缺失值填充、编码、标准化串联为统一流程,避免数据泄露(data leakage)。在 A/B 测试场景中,务必确保实验组与对照组使用相同的预处理参数(如填充均值、编码映射表、标准化均值和标准差)。
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
# 定义数值与类别列
numeric_features = ['age', 'income']
categorical_features = ['gender', 'city']
# 数值流水线:缺失值填充 + 标准化
numeric_pipeline = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# 类别流水线:缺失值填充 + OneHot编码
categorical_pipeline = Pipeline([
('imputer', SimpleImputer(strategy='most_frequent')),
('encoder', OneHotEncoder(handle_unknown='ignore'))
])
# 组合预处理
preprocessor = ColumnTransformer([
('num', numeric_pipeline, numeric_features),
('cat', categorical_pipeline, categorical_features)
])
# 示例数据
import pandas as pd
X_train = pd.DataFrame({
'age': [25, 30, None, 35],
'income': [50000, 60000, 55000, None],
'gender': ['M', 'F', 'F', None],
'city': ['NYC', 'LA', 'NYC', 'SF']
})
X_processed = preprocessor.fit_transform(X_train)
print("预处理后特征矩阵形状:", X_processed.shape)
XGBoost 原生支持:从 XGBoost 1.6 版本开始,可以直接在
DMatrix 中传入类别特征索引,由模型内部自动处理编码。对于大规模类别特征(如用户 ID),建议使用 enable_categorical=True 配合 pd.Categorical 类型,避免 OneHot 编码导致的维度爆炸。