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 数据清洗:字段格式与逻辑校验
数据清洗是建模前的必要步骤。我们针对模拟数据中常见的三类问题进行处理:
- 字段格式统一:将
gender字段统一为大写(M/F),去除首尾空格;将marital_status中的拼写变体(如“Married ”、“married”)标准化为标准值。 - 逻辑矛盾修正:检查
claim_count与claim_amount的一致性——若理赔次数为 0 但理赔金额大于 0,则视为数据录入错误,将理赔金额置为 0。 - 范围校验:
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 数据准备流程总览
下图展示了从原始数据到建模可用数据集的完整流程,包含清洗、缺失处理与异常检测三个并行处理分支:
4.6 探索性数据分析(EDA)快照
完成清洗与处理后,对关键变量进行初步探索性分析,以验证数据质量并发现潜在模式:
- 年龄分布:驾驶人年龄集中在 25-50 岁区间,中位数 38 岁,符合车险投保人群特征。
- 车辆价值:呈右偏分布,大部分车辆价值在 10-25 万元之间,少数高端车拉高均值。
- 理赔次数:约 85% 的保单过去 3 年无理赔记录,12% 有 1 次理赔,3% 有 2 次及以上理赔。
- 缺失与异常标记:共标记 127 条异常记录(占 1.27%),缺失值已全部填充,无剩余缺失。
精算视角:在正式建模前,建议对
claim_count 与 exposure 构建初步的零膨胀泊松模型,以验证数据是否支持后续的 GLM 或 GBM 建模假设。此步骤可提前发现数据稀疏性问题。