4. 数据准备与清洗

在保险费率表构建与Rating Plan落地的整个工程链条中,数据准备与清洗往往占据60%以上的工作量,却最容易被低估。费率表本质上是“风险因子—费率值”的映射矩阵,而这张矩阵的每一个单元格都来自历史赔付、承保、再保与外部数据。如果输入数据存在来源混乱、缺失、异常或量纲不统一,后续无论使用GLM、GBM还是评分卡,都会得到“垃圾进、垃圾出”的结果。本节围绕数据来源、缺失值处理、异常值识别、数据标准化四个环节,给出可落地的工程方法。

一、核心原理:数据质量决定费率表的可信边界

费率表构建对数据的要求可以概括为三个维度:完整性(每条风险记录的关键因子是否齐全)、一致性(同一因子在不同系统、不同时间口径是否统一)、代表性(样本是否覆盖目标客群与风险分布)。数据清洗不是“把脏数据删掉”,而是通过可追溯的规则,把原始数据转化为可建模、可解释、可审计的建模宽表。其核心原则是:清洗规则必须可复现、可版本化、可回溯到原始记录,否则费率表在监管或再保审核时无法自证。

数据清洗的最终交付物不是“干净数据”,而是“带清洗日志的建模宽表 + 数据字典 + 规则版本号”。

二、数据来源:多源异构数据的整合策略

车险、健康险、财产险的费率表数据通常来自以下五类源系统,整合时需要明确主键、时间口径与责任范围:

  • 承保系统:保单号、被保险人、标的、保额、保费、起保止期、渠道、折扣系数。
  • 理赔系统:赔案号、出险时间、报案时间、立案金额、已决/未决金额、责任类型。
  • 再保/共保系统:分出比例、摊回金额、再保层结构。
  • 外部数据:行业经验赔付率、车型零整比、地区风险系数、征信或健康评分。
  • 精算中间表:风险暴露量、已赚保费、赔付率、案均赔款。

整合时建议以“保单—标的—赔案”三级粒度建立主键,并统一时间口径为事故年或保单年。例如,用SQL将理赔数据按事故年汇总到标的粒度:

SELECT policy_id, item_id,
       SUM(CASE WHEN accident_year = 2023 THEN paid_loss ELSE 0 END) AS loss_2023,
       COUNT(DISTINCT claim_id) AS claim_cnt_2023
FROM claim_fact
GROUP BY policy_id, item_id;

三、缺失值处理:从“删”到“补”再到“标记”

缺失值处理的第一步是诊断缺失机制:完全随机缺失(MCAR)、随机缺失(MAR)还是非随机缺失(MNAR)。费率表中常见的缺失包括:新车无历史赔付、客户未填写职业、外部数据未匹配到车型。处理策略按缺失比例分层:

缺失比例推荐策略风险提示
< 5%删除记录或均值/中位数填充需确认缺失随机,避免样本偏差
5%~20%分组填充、模型预测填充、增加“缺失”哑变量哑变量可保留缺失信息,防止信息丢失
> 20%重新评估该因子是否可用,或单独建缺失层高缺失因子直接入模会放大噪声

在费率表中,缺失值本身可能携带风险信号。例如“职业未知”的客户,其赔付经验可能显著高于“职业明确”的客户。因此,推荐做法是将缺失作为一个独立类别,而不是简单填充。代码示例:

import pandas as pd
df['occupation'] = df['occupation'].fillna('UNKNOWN')
df['vehicle_age_missing'] = df['vehicle_age'].isna().astype(int)
df['vehicle_age'] = df.groupby('vehicle_type')['vehicle_age'].transform(
    lambda x: x.fillna(x.median())
)

四、异常值识别与数据标准化

异常值识别要区分“错误数据”与“真实极端风险”。错误数据如年龄为200岁、保额为负数,应直接修正或剔除;真实极端值如高额赔案,应保留但做截断(Winsorize)或分层处理。常用方法包括:

  • 统计法:Z-score > 3、IQR 超出1.5倍四分位距。
  • 业务规则法:年龄不在0~120、车龄为负、保费为0但保额为正。
  • 模型法:孤立森林、DBSCAN、分位数回归残差。

数据标准化则解决量纲与分布问题。费率表中连续变量(如年龄、车龄、保额)常需分箱或标准化。标准化方法包括Min-Max、Z-score、Robust Scaling,以及针对偏态分布的分位数映射。例如,将赔付率按分位数映射到0~1区间,便于评分卡转换:

from sklearn.preprocessing import QuantileTransformer
qt = QuantileTransformer(n_quantiles=1000, output_distribution='uniform')
df['loss_ratio_scaled'] = qt.fit_transform(df[['loss_ratio']])
标准化参数必须保存为“训练集参数”,在验证集和未来新单上复用,禁止在全量数据上重新拟合,否则会造成数据泄露。

五、案例与复盘:一个车险费率表的数据清洗实战

某车险公司构建“车型—地区—年龄”三维费率表时,发现模型AUC仅0.58。复盘发现三个问题:一是理赔数据按“报案年”而非“事故年”汇总,导致赔付率被低估;二是“车龄”缺失率18%,直接删除后样本偏向新车;三是“地区”编码在承保与理赔系统中不一致,导致匹配失败。修正后:统一事故年口径、将车龄缺失标记为独立层、建立地区编码映射表,AUC提升至0.67,费率表区分度显著改善。

常见误区:把缺失值填充当成“美化数据”,把异常值一律删除,把标准化在全量数据上执行。这三类操作都会让费率表在落地时出现系统性偏差,且难以追溯。
数据准备与清洗四阶段:从原始数据到建模宽表 ① 数据来源 承保 / 理赔 / 再保 外部数据 / 精算中间表 统一主键与事故年口径 ② 缺失值处理 诊断 MCAR / MAR / MNAR 分组填充 + 缺失哑变量 高缺失因子单独建层 ③ 异常值识别 Z-score / IQR / 业务规则 孤立森林 / 分位数残差 截断或分层保留 ④ 数据标准化 Min-Max / Z-score Robust / 分位数映射 训练集参数复用 清洗日志与数据字典(可追溯、可版本化) 规则编号 · 影响记录数 · 处理前后分布 · 参数文件版本 · 审批人 输出:建模宽表 + 数据字典 + 规则版本号 完整性 关键因子齐全率 ≥ 95% 一致性 跨系统编码映射统一 代表性 样本覆盖目标风险分布

总结来看,数据准备与清洗是费率表构建的“地基工程”。数据来源决定口径,缺失值处理决定信息保留度,异常值识别决定模型稳健性,数据标准化决定因子可比性。四者环环相扣,任何一环的偷工减料都会在Rating Plan落地时以“费率不敏感”“赔付率倒挂”“监管问询”等形式暴露。建议团队建立标准化的清洗流水线,把每一步规则固化为可执行脚本与日志,让费率表不仅“算得准”,更“说得清”。