4、特征工程基础:时间窗口特征、聚合特征、比率特征、文本特征提取(如理赔描述)
在保险精算坏样本归因分析中,原始理赔数据通常以事务日志形式存在,无法直接用于模型训练。特征工程是将原始日志转化为结构化、可计算特征的关键步骤。本章聚焦于四类核心特征:时间窗口特征、聚合特征、比率特征以及文本特征,并结合理赔场景给出具体实现。
核心原则:特征工程必须与业务逻辑对齐。例如,理赔次数的时间窗口不应跨越保单生效日;文本特征需关注理赔描述中的“拒赔”、“欺诈”、“事故原因”等关键词。
4.1 时间窗口特征
时间窗口特征用于捕捉理赔行为在特定时间段内的统计规律。常见的窗口类型包括:固定长度窗口(如过去30天)、滑动窗口(如过去7天滚动)以及业务周期窗口(如保单年度内)。
| 窗口类型 | 定义 | 保险精算应用场景 |
|---|---|---|
| 固定长度窗口 | 以当前时间点为基准,向前取固定天数 | 过去90天内理赔次数、过去180天内赔付金额 |
| 滑动窗口 | 按天/周滚动计算统计量 | 近7天日均报案量、近30天赔付率趋势 |
| 业务周期窗口 | 以保单生效日为起点,按自然月/季度划分 | 保单年度内累计出险次数、免赔期内理赔行为 |
| 事件间隔窗口 | 两次理赔事件之间的时间差 | 相邻两次车险报案间隔天数、医疗险住院间隔 |
实战案例:在车险理赔归因中,构建“过去30天同一被保险人理赔次数”特征,若该值超过3次,则坏样本概率显著上升。代码实现如下:
import pandas as pd
# 假设 df 包含 claim_date, policy_id, claim_amount 等字段
df['claim_date'] = pd.to_datetime(df['claim_date'])
df = df.sort_values(['policy_id', 'claim_date'])
# 固定长度窗口:过去30天理赔次数
df['claim_count_30d'] = df.groupby('policy_id')['claim_date'].transform(
lambda x: x.rolling(window='30D', min_periods=1).count()
)
# 事件间隔窗口:相邻理赔间隔天数
df['days_since_last_claim'] = df.groupby('policy_id')['claim_date'].diff().dt.days
4.2 聚合特征
聚合特征通过对理赔记录按某个维度(如保单、客户、车辆)进行分组,计算统计量来刻画群体行为。常用的聚合函数包括:计数、求和、均值、标准差、最大值、最小值、分位数等。
| 聚合维度 | 聚合函数 | 生成特征示例 |
|---|---|---|
| 保单级别 | count, sum, mean | 保单累计理赔次数、累计赔付金额、平均赔付金额 |
| 客户级别 | nunique, std, max | 客户涉及险种数、理赔金额标准差、单次最大赔付 |
| 车辆级别 | min, quantile | 车辆首次出险时间、理赔金额中位数 |
| 地理区域 | count, sum | 区域报案量、区域总赔付金额 |
关键技巧:聚合特征容易产生数据泄露。例如,使用全量历史数据计算均值会引入未来信息。正确的做法是:在训练集中,每个样本只使用其时间点之前的聚合统计量。
# 按保单分组,计算截止当前时间之前的聚合特征
def safe_agg(group):
# 对每个样本,只使用该样本之前的数据
group = group.sort_values('claim_date')
group['cum_claim_count'] = range(1, len(group) + 1)
group['cum_claim_amount'] = group['claim_amount'].cumsum()
group['avg_claim_amount'] = group['claim_amount'].expanding().mean()
return group
df = df.groupby('policy_id').apply(safe_agg).reset_index(drop=True)
4.3 比率特征
比率特征通过两个或多个数值字段的比值来消除量纲影响,反映相对关系。在保险精算中,比率特征常用于衡量风险密度、赔付效率等。
| 比率类型 | 计算公式 | 业务含义 |
|---|---|---|
| 赔付率 | 赔付金额 / 已赚保费 | 衡量保单盈利能力,>100%表示亏损 |
| 案均赔付 | 总赔付金额 / 理赔次数 | 反映单次事故严重程度 |
| 出险频率 | 理赔次数 / 保险期间(年) | 单位时间内的出险概率 |
| 免赔覆盖率 | 免赔额 / 总赔付金额 | 客户自付比例,影响理赔意愿 |
| 时间密度比 | 理赔间隔天数 / 保单剩余天数 | 理赔时间集中度,高值表示近期频繁出险 |
注意:比率特征的分母不能为零。对于理赔次数为0的保单,案均赔付应填充为0或使用全局均值平滑。建议使用
np.where(denom == 0, 0, num/denom) 进行安全处理。
import numpy as np
# 计算案均赔付,处理分母为0的情况
df['claim_count'] = df['claim_count'].fillna(0)
df['avg_payout'] = np.where(
df['claim_count'] > 0,
df['total_claim_amount'] / df['claim_count'],
0
)
# 计算赔付率(假设已赚保费字段为 earned_premium)
df['loss_ratio'] = np.where(
df['earned_premium'] > 0,
df['total_claim_amount'] / df['earned_premium'],
0
)
4.4 文本特征提取(理赔描述)
理赔描述文本中蕴含大量非结构化信息,如事故原因、责任认定、欺诈线索等。文本特征提取的目标是将这些信息转化为数值向量,供模型使用。
常用的文本特征提取方法包括:
- 词袋模型(Bag of Words, BoW):统计每个词在文本中出现的次数,忽略词序。适用于高频关键词检测。
- TF-IDF:考虑词频和逆文档频率,降低常见词的权重,突出特定文档中的关键词。
- N-gram:提取连续的N个词作为特征,保留局部词序信息。例如,2-gram可捕捉“虚假报案”、“故意碰撞”等短语。
- 预训练词向量:使用Word2Vec、FastText等模型将词映射为稠密向量,再对文档取平均或加权求和。
- 规则关键词匹配:基于业务词典,直接标记是否包含“拒赔”、“欺诈”、“酒驾”等敏感词。
| 方法 | 维度 | 优点 | 缺点 | 保险场景适用性 |
|---|---|---|---|---|
| 词袋模型 | 高维稀疏 | 简单、可解释性强 | 忽略词序和语义 | 适合关键词统计,如“事故”、“维修” |
| TF-IDF | 高维稀疏 | 突出重要词,抑制常见词 | 仍忽略词序 | 适合区分不同事故类型 |
| N-gram | 高维稀疏 | 保留局部短语信息 | 维度爆炸,需特征选择 | 适合“伪造现场”、“虚假理赔”等短语 |
| 预训练词向量 | 低维稠密 | 语义相似度好,泛化能力强 | 需要领域微调,可解释性差 | 适合大规模文本,如理赔报告摘要 |
| 规则关键词 | 低维二值 | 高精度、高可解释性 | 依赖专家知识,覆盖不全 | 适合欺诈检测、拒赔判定 |
实战建议:在坏样本归因场景中,推荐组合使用“规则关键词 + TF-IDF”。先用规则关键词快速标记高风险样本,再用TF-IDF提取剩余文本中的潜在模式。以下为TF-IDF提取示例:
from sklearn.feature_extraction.text import TfidfVectorizer
# 假设 df['claim_description'] 为理赔描述文本
tfidf = TfidfVectorizer(
max_features=500, # 限制最大特征数,避免维度爆炸
stop_words='english', # 去除停用词
ngram_range=(1, 2), # 使用1-gram和2-gram
min_df=5, # 至少在5个文档中出现
max_df=0.8 # 忽略在80%以上文档中出现的词
)
# 拟合转换
tfidf_matrix = tfidf.fit_transform(df['claim_description'].fillna(''))
# 转换为DataFrame
tfidf_df = pd.DataFrame(
tfidf_matrix.toarray(),
columns=tfidf.get_feature_names_out()
)
# 与原数据合并
df = pd.concat([df, tfidf_df], axis=1)
特征工程检查清单:
- 时间窗口特征是否避免了数据泄露?(使用截止时间前的数据)
- 聚合特征是否按业务维度正确分组?(保单 vs 客户 vs 车辆)
- 比率特征的分母是否做了零值保护?
- 文本特征是否进行了预处理(分词、去停用词)?
- 特征维度是否可控?(建议使用特征选择或降维)