4. 数据预处理实战

在保险理赔 Severity(损失金额)预测中,原始理赔数据往往来自报案系统、查勘系统、定损系统与第三方数据源,字段口径不一致、缺失普遍、长尾极端值频繁出现。模型效果的上限,很大程度上由这一步的数据预处理决定。本节围绕五个关键动作展开:缺失值填充、异常值处理、类别编码、数值标准化与特征对齐,并给出可直接落地的工程做法。

核心目标:把“脏、散、偏”的原始理赔表,转换为“完整、稳健、可训练、可复现”的特征矩阵,同时保证训练集与线上推理的特征顺序、类型、量纲完全一致。

一、核心原理:为什么预处理决定 Severity 预测成败

Severity 预测是典型的回归任务,目标变量通常是赔案总损失金额或案均赔付金额。这类变量具有三个显著特点:

  • 右偏长尾:多数案件金额集中在数千元,但少量重大案件可达数十万甚至上百万元,直接建模会被极端值主导。
  • 缺失非随机:高价值案件往往查勘信息更全,低价值小额案件可能缺失维修厂、伤情等字段,缺失本身携带信息。
  • 类别高基数:车型、维修厂、事故原因、地区等类别字段取值多,独热编码会迅速膨胀维度。

因此,预处理不是“清洗脏数据”的机械劳动,而是把业务先验注入特征的过程。缺失值填充策略、异常值截断阈值、编码方式与标准化范围,都会直接影响模型对“大案”和“小案”的区分能力。

理赔 Severity 预处理流水线:从原始表到对齐特征矩阵 原始理赔表 报案/查勘/定损 缺失·异常·多源 缺失值填充 中位数/分组填充 缺失指示变量 异常值处理 IQR / 分位数截断 log1p 压缩长尾 类别编码 低频合并+目标编码 独热/嵌入 标准化 Z-score Robust 特征对齐层:训练/验证/线上共用同一 ColumnTransformer 列顺序 · 数据类型 · 缺失填充值 · 编码映射 · 标准化参数 全部固化 训练集 fit_transform 学习填充/编码/缩放参数 验证集 transform 复用训练参数,禁止重学 线上推理 transform 特征顺序与训练完全一致

二、方法流程:五步预处理的标准动作

  1. 缺失值填充:数值字段优先用中位数或分组中位数(如按险种、地区分组);类别字段用“未知”占位;对缺失率高于 60% 的字段评估是否直接剔除,同时为重要字段增加 is_missing 指示列。
  2. 异常值处理:对 Severity 目标与金额类特征先做 log1p 变换,再用 IQR 或 1%/99% 分位数截断;业务上真实的大案不应被简单删除,而应保留并加“大案标记”。
  3. 类别编码:低基数用独热编码,高基数(车型、维修厂)用目标编码或频率编码,并配合 K 折平滑防止泄漏。
  4. 数值标准化:线性模型用 Z-score,含异常值时用 RobustScaler;树模型可跳过,但为统一流水线仍建议保留。
  5. 特征对齐:用 ColumnTransformer 固化列顺序与变换器,训练集 fit_transform,验证与线上只 transform。

三、案例场景:车险理赔 Severity 预处理片段

假设原始表包含 claim_amount(目标)、vehicle_age、repair_shop、accident_cause、region 等字段。下面给出关键代码骨架:

from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import RobustScaler, OneHotEncoder
from sklearn.preprocessing import FunctionTransformer
import numpy as np

num_cols = ["vehicle_age", "driver_age", "claim_amount"]
cat_cols = ["repair_shop", "accident_cause", "region"]

log_then_scale = Pipeline([
    ("log", FunctionTransformer(np.log1p, validate=True)),
    ("scale", RobustScaler())
])

preprocess = ColumnTransformer([
    ("num", Pipeline([
        ("imputer", SimpleImputer(strategy="median")),
        ("scaler", RobustScaler())
    ]), ["vehicle_age", "driver_age"]),
    ("amount", log_then_scale, ["claim_amount"]),
    ("cat", Pipeline([
        ("imputer", SimpleImputer(strategy="constant", fill_value="未知")),
        ("ohe", OneHotEncoder(handle_unknown="ignore", min_frequency=20))
    ]), cat_cols)
], remainder="drop")

X_train = preprocess.fit_transform(df_train)
X_valid = preprocess.transform(df_valid)   # 只 transform,不重学
对高基数类别字段,可先用 min_frequency 合并低频类别,再对合并后的类别做目标编码,能显著降低维度并保留业务区分度。

四、误区与复盘:常见踩坑清单

误区 后果 正确做法
用全量数据计算填充值/标准化参数 数据泄漏,验证分数虚高 只在训练集 fit,验证与线上 transform
直接删除大额赔案 模型低估大案,业务不可用 log 变换 + 截断 + 大案标记
训练与线上列顺序不一致 推理结果错乱或报错 用 ColumnTransformer 固化列顺序
目标编码未做 K 折平滑 类别泄漏,过拟合 折内统计 + 平滑先验
忽略缺失指示变量 丢失“缺失即信息”的业务信号 为关键字段增加 is_missing 列
预处理流水线必须与模型一起保存(如 joblib 持久化整个 Pipeline),否则线上推理时极易出现“训练用 A 套参数、线上用 B 套参数”的隐性事故。

复盘时建议固定三件事:第一,记录每个字段的缺失率、异常截断阈值与编码映射;第二,在验证集上对比“原始 vs 预处理后”的 MAE 与分位数损失;第三,把预处理配置纳入版本管理,确保任何一次模型上线都能追溯到对应的数据变换逻辑。做到这三点,Severity 预测的稳定性与可解释性都会明显提升。