4. 数据准备与探索:数据集介绍(模拟车险数据)、数据清洗、缺失值处理、异常值检测

在构建保费定价模型之前,必须对原始数据进行充分的准备与探索。本章使用的数据集为模拟车险承保与理赔数据,旨在模拟真实业务场景中的常见数据问题。我们将依次完成数据集介绍、数据清洗、缺失值处理与异常值检测四个核心步骤。

4.1 数据集介绍:模拟车险数据

本课程使用的数据集包含 10,000 条模拟车险保单记录,涵盖投保人、车辆、保单及历史理赔四个维度的信息。数据集的字段定义如下表所示:

字段名称 数据类型 字段说明 示例值
policy_id 字符串 保单唯一标识 POL0001
age_driver 整数 驾驶人年龄(岁) 35
gender 字符串 性别(M/F) M
marital_status 字符串 婚姻状况(Single/Married/Divorced) Married
vehicle_age 整数 车龄(年) 3
vehicle_value 浮点数 车辆价值(万元) 15.5
annual_mileage 浮点数 年行驶里程(万公里) 1.2
credit_score 整数 信用评分(300-850) 720
claim_count 整数 过去3年理赔次数 0
claim_amount 浮点数 过去3年理赔总金额(万元) 0.0
exposure 浮点数 风险暴露年数(0-1) 1.0
提示:模拟数据已预先注入缺失值、异常值及逻辑矛盾,以模拟真实数据清洗场景。所有字段均基于真实车险精算经验分布生成,但经过脱敏与随机化处理。

4.2 数据清洗:字段格式与逻辑校验

数据清洗是建模前的必要步骤。我们针对模拟数据中常见的三类问题进行处理:

  1. 字段格式统一:将 gender 字段统一为大写(M/F),去除首尾空格;将 marital_status 中的拼写变体(如“Married ”、“married”)标准化为标准值。
  2. 逻辑矛盾修正:检查 claim_count 与 claim_amount 的一致性——若理赔次数为 0 但理赔金额大于 0,则视为数据录入错误,将理赔金额置为 0。
  3. 范围校验:age_driver 应在 18-100 岁之间,credit_score 应在 300-850 之间,超出范围的值标记为待处理。
清洗示例代码(Python):
import pandas as pd

# 加载数据
df = pd.read_csv('simulated_auto_insurance.csv')

# 字段格式统一
df['gender'] = df['gender'].str.strip().str.upper()
df['marital_status'] = df['marital_status'].str.strip().str.title()

# 逻辑矛盾修正
mask = (df['claim_count'] == 0) & (df['claim_amount'] > 0)
df.loc[mask, 'claim_amount'] = 0.0

# 范围校验标记
df['age_invalid'] = ~df['age_driver'].between(18, 100)
df['credit_invalid'] = ~df['credit_score'].between(300, 850)

4.3 缺失值处理

模拟数据中,annual_mileage、credit_score 和 vehicle_value 三个字段存在不同程度的缺失。缺失比例与处理策略如下:

字段 缺失比例 处理策略 填充依据
annual_mileage 8.5% 中位数填充 分布右偏,中位数更稳健
credit_score 5.2% 均值填充 近似正态分布
vehicle_value 3.1% 按 vehicle_age 分组中位数填充 车龄与价值强相关
注意:缺失值处理策略需根据字段分布特征选择。对于 vehicle_value,直接使用全局中位数会引入偏差,按车龄分组填充能更好地保留变量间的结构关系。

4.4 异常值检测

异常值检测采用 IQR(四分位距)法与业务规则相结合的方式。以下为各连续变量的异常值判定标准:

  • age_driver:低于 18 岁或高于 80 岁视为异常(业务规则)。
  • vehicle_value:低于 Q1 - 1.5×IQR 或高于 Q3 + 1.5×IQR 视为异常。
  • annual_mileage:高于 Q3 + 3×IQR 视为极端异常(年行驶里程超过 5 万公里)。
  • credit_score:低于 350 或高于 850 视为异常(业务规则 + IQR 综合)。

检测到的异常值不直接删除,而是标记为 outlier_flag,在后续建模中作为特征或单独处理。

异常检测示例代码(Python):
def detect_outliers_iqr(series, multiplier=1.5):
    Q1 = series.quantile(0.25)
    Q3 = series.quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - multiplier * IQR
    upper = Q3 + multiplier * IQR
    return (series < lower) | (series > upper)

df['outlier_vehicle_value'] = detect_outliers_iqr(df['vehicle_value'])
df['outlier_annual_mileage'] = detect_outliers_iqr(df['annual_mileage'], multiplier=3)
df['outlier_credit_score'] = (df['credit_score'] < 350) | (df['credit_score'] > 850)

4.5 数据准备流程总览

下图展示了从原始数据到建模可用数据集的完整流程,包含清洗、缺失处理与异常检测三个并行处理分支:

数据准备与探索流程总览 数据准备与探索流程总览 原始模拟车险数据 数据清洗 缺失值处理 异常值检测 格式统一 · 逻辑修正 · 范围校验 中位数/均值/分组填充 IQR法 · 业务规则 · 标记 清洗后建模可用数据集 说明 三个处理分支并行执行,最终合并为清洗后数据集。异常值仅标记不删除,保留在数据集中供后续建模参考。

4.6 探索性数据分析(EDA)快照

完成清洗与处理后,对关键变量进行初步探索性分析,以验证数据质量并发现潜在模式:

  • 年龄分布:驾驶人年龄集中在 25-50 岁区间,中位数 38 岁,符合车险投保人群特征。
  • 车辆价值:呈右偏分布,大部分车辆价值在 10-25 万元之间,少数高端车拉高均值。
  • 理赔次数:约 85% 的保单过去 3 年无理赔记录,12% 有 1 次理赔,3% 有 2 次及以上理赔。
  • 缺失与异常标记:共标记 127 条异常记录(占 1.27%),缺失值已全部填充,无剩余缺失。
精算视角:在正式建模前,建议对 claim_count 与 exposure 构建初步的零膨胀泊松模型,以验证数据是否支持后续的 GLM 或 GBM 建模假设。此步骤可提前发现数据稀疏性问题。