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 编码导致的维度爆炸。