4. 精算数据预处理:数据清洗、缺失值处理、异常值检测、特征工程在精算中的应用
精算模型对数据质量高度敏感。原始业务数据通常包含重复记录、格式不一致、缺失字段、极端离群值以及冗余特征。本章聚焦于将原始数据转化为适合精算评分模型的结构化、高质量特征集,涵盖数据清洗、缺失值处理、异常值检测与特征工程四大核心环节。
4.1 数据清洗:精算数据的第一道防线
数据清洗的目标是消除不一致、重复和格式错误。精算数据常见问题包括:保单号重复、日期格式混乱、费率代码拼写错误、金额字段包含货币符号等。
| 清洗类型 | 典型问题 | Python 处理方式 |
|---|---|---|
| 重复记录 | 同一保单号出现多次 | df.drop_duplicates(subset='policy_id') |
| 格式统一 | 日期列含 "2024-01-01" 与 "01/01/2024" | pd.to_datetime(df['date'], format='mixed') |
| 类别标准化 | "Male" / "M" / "男" 混用 | 映射字典 + df['gender'].map(mapping_dict) |
| 金额清洗 | 字段含 "$" 或 "," | df['amount'].str.replace('[\$,]', '', regex=True).astype(float) |
精算提示: 在清洗阶段保留原始数据副本,并记录每步转换逻辑。精算审计要求数据转换过程可追溯。
import pandas as pd
# 示例:清洗保费金额字段
df['premium_raw'] = df['premium_raw'].str.replace('[\$,]', '', regex=True)
df['premium'] = pd.to_numeric(df['premium_raw'], errors='coerce')
# 去除完全重复的保单记录
df_clean = df.drop_duplicates(subset=['policy_id', 'effective_date'])
4.2 缺失值处理:精算场景下的策略选择
精算数据缺失并非随机。例如,高保额保单的财务信息更完整,而小额保单常缺失收入字段。处理策略需结合缺失机制与业务含义。
注意: 精算模型中,直接删除含缺失值的记录可能导致选择偏差,尤其当缺失与风险水平相关时。
4.2.1 缺失机制分类
- 完全随机缺失 (MCAR):缺失与数据本身无关,可直接删除或简单填充。
- 随机缺失 (MAR):缺失依赖于其他观测变量,需借助模型填充。
- 非随机缺失 (MNAR):缺失与缺失值本身相关,需引入外部信息或业务假设。
4.2.2 常用填充方法对比
| 方法 | 适用场景 | 精算注意事项 |
|---|---|---|
| 均值/中位数填充 | 连续变量,缺失率 < 5% | 会压缩方差,低估风险波动 |
| 众数填充 | 分类变量,如婚姻状况 | 可能强化主导类别权重 |
| 前向/后向填充 | 时间序列精算数据 | 需确保时间顺序正确 |
| KNN 填充 | 多维特征,缺失率 5%-20% | 计算量大,需标准化 |
| 模型预测填充 | 缺失率 > 20%,有辅助变量 | 避免数据泄露,使用交叉验证 |
from sklearn.impute import KNNImputer
# 对数值型精算特征进行 KNN 填充
imputer = KNNImputer(n_neighbors=5)
df_filled = pd.DataFrame(
imputer.fit_transform(df[['age', 'income', 'claim_count']]),
columns=['age', 'income', 'claim_count']
)
4.3 异常值检测:识别精算极端风险
异常值在精算中可能代表欺诈、录入错误或真正的极端风险。检测方法需区分全局异常与局部异常。
4.3.1 常用检测方法
- Z-Score 法:假设正态分布,|Z| > 3 视为异常。适用于近似正态的精算指标如 BMI、年龄。
- IQR 法:Q1 - 1.5×IQR 以下或 Q3 + 1.5×IQR 以上。对偏态分布更稳健,如赔付金额。
- 孤立森林:基于随机切割的集成方法,适合高维精算特征。
- 局部异常因子 (LOF):检测局部密度异常,适用于欺诈检测场景。
from sklearn.ensemble import IsolationForest
# 孤立森林检测异常赔付记录
iso_forest = IsolationForest(contamination=0.05, random_state=42)
df['anomaly_flag'] = iso_forest.fit_predict(df[['claim_amount', 'age', 'policy_tenure']])
# -1 表示异常,1 表示正常
df_anomalies = df[df['anomaly_flag'] == -1]
4.4 特征工程:精算领域知识的量化
特征工程是将精算业务经验转化为模型可理解数值变量的过程。好的特征能显著提升评分模型的区分度与稳定性。
4.4.1 精算特征构建策略
| 特征类型 | 示例 | 精算含义 |
|---|---|---|
| 聚合统计 | 过去 12 个月平均赔付金额 | 反映历史风险暴露 |
| 比率特征 | 赔付金额 / 已缴保费 | 损失率,核心精算指标 |
| 时间特征 | 保单生效天数、续保间隔 | 刻画客户生命周期 |
| 交叉特征 | 年龄 × 吸烟状态 | 捕捉交互效应 |
| 编码特征 | 职业风险等级 (1-5) | 将类别转化为有序变量 |
4.4.2 特征变换与选择
- 对数变换:对右偏分布(如赔付金额)取 log,使分布更接近正态。
- 分箱:将连续变量(如年龄)划分为精算风险区间:18-25, 26-35, 36-50, 51-65, 65+。
- WOE 编码:证据权重编码,常用于精算评分卡模型,将类别变量映射为对数赔率。
- 特征选择:使用方差阈值、相关系数、互信息或 L1 正则化筛选重要特征。
精算最佳实践: 特征工程完成后,使用
df.describe() 和 df['feature'].hist() 检查分布合理性。确保所有特征在业务上可解释,避免使用黑箱特征导致监管合规风险。
import numpy as np
# 构建损失率特征
df['loss_ratio'] = df['total_claim_amount'] / (df['earned_premium'] + 1e-6)
# 年龄分箱
bins = [18, 25, 35, 50, 65, 100]
labels = ['18-25', '26-35', '36-50', '51-65', '65+']
df['age_band'] = pd.cut(df['age'], bins=bins, labels=labels, right=False)
# 对数变换赔付金额
df['log_claim'] = np.log1p(df['claim_amount'])