第1章:特征工程基础——特征构造、筛选与分箱
各位同学好,我是老周。在风控建模这行摸爬滚打了快十年,今天咱们聊聊特征工程。
很多人觉得建模嘛,算法调参最重要。其实不然。我见过太多项目,模型效果不好,90%的原因出在特征上。说白了,你喂给模型的是垃圾,它吐出来的只能是垃圾。
这一章,咱们把特征工程的三个核心环节讲透:特征构造、特征筛选、特征分箱。嗯,都是硬骨头,但啃下来之后,你会发现建模思路清晰很多。
1.1 特征构造:从原始数据里挖金子
特征构造,说白了就是创造新的特征。原始数据往往不够用,你得想办法从里面提炼出更有价值的信息。
衍生变量是最基础的操作。比如你手里有用户的「借款金额」和「月收入」,那「负债收入比」就是一个典型的衍生变量。我在项目中遇到过,光靠原始特征建模,KS值只有0.25,加了这个衍生变量后直接跳到0.33。你想想看,这就是信息增益的力量。
常见的衍生变量类型:
- 比率类:负债收入比、信用卡使用率、还款金额/应还金额
- 差值类:当前年龄-开户年龄、最近一次借款距今月数
- 聚合类:近3个月平均消费、近6个月最大逾期天数
交叉特征,就是把两个或多个特征组合起来。比如「年龄」和「职业」交叉,可以生成「年轻程序员」、「中年公务员」这样的组合特征。为什么这么做?因为单一特征可能区分度不够,组合起来往往能发现新规律。
我记得有个项目,单独看「性别」IV值只有0.02,单独看「职业」IV值0.08,但交叉后IV值飙到0.31。这就是1+1>2的效果。
# 交叉特征示例代码
import pandas as pd
# 假设有年龄和职业两个特征
df['age_group'] = pd.cut(df['age'], bins=[0,25,35,50,100], labels=['青年','中青年','中年','老年'])
df['age_job_cross'] = df['age_group'].astype(str) + '_' + df['job'].astype(str)
# 计算交叉特征的IV值
def calc_iv(df, feature, target):
# 这里省略IV计算细节,后面会讲
pass
时间窗口特征,这个在信贷风控里特别重要。借款人过去的行为,往往能预测未来的还款表现。比如「近3个月查询次数」、「近6个月逾期天数」、「近12个月平均使用额度」。
我的经验:时间窗口不是越大越好。窗口太长,数据稀疏;窗口太短,信息不足。我个人习惯先试3个月、6个月、12个月三个窗口,看哪个IV值最高就用哪个。
1.2 特征筛选:去粗取精的艺术
特征构造完了,你可能手里有几百个特征。但别高兴太早,这里面很多是噪音。特征筛选就是帮你把真正有用的特征挑出来。
IV值(Information Value)是风控领域最常用的筛选指标。它衡量的是特征对目标变量的区分能力。IV值越高,说明这个特征越能区分好坏客户。
| IV值范围 | 预测能力 |
|---|---|
| < 0.02 | 无预测能力,建议剔除 |
| 0.02 - 0.10 | 弱预测能力 |
| 0.10 - 0.30 | 中等预测能力 |
| 0.30 - 0.50 | 强预测能力 |
| > 0.50 | 可疑,需检查是否过拟合 |
注意:IV值超过0.5的特征要小心。我曾经遇到一个特征IV值高达0.8,后来发现是数据泄露——它包含了未来信息。嗯,这种坑踩过一次就记住了。
相关性分析,主要是解决特征之间的多重共线性问题。两个特征高度相关,模型会不稳定,解释性也会变差。我一般用皮尔逊相关系数,阈值设在0.7。超过0.7的,保留IV值高的那个,剔除另一个。
# 相关性分析示例
import seaborn as sns
import matplotlib.pyplot as plt
# 计算相关系数矩阵
corr_matrix = df.corr()
# 找出高度相关的特征对
high_corr_pairs = []
for i in range(len(corr_matrix.columns)):
for j in range(i+1, len(corr_matrix.columns)):
if abs(corr_matrix.iloc[i, j]) > 0.7:
high_corr_pairs.append((corr_matrix.columns[i],
corr_matrix.columns[j],
corr_matrix.iloc[i, j]))
方差过滤,这个最简单。如果一个特征在所有样本上的取值几乎一样,那它对模型没有任何贡献。比如「性别」在某个数据集里99%都是男性,那这个特征基本没用。我一般设置方差阈值,低于阈值的直接扔掉。
1.3 特征分箱:连续变量离散化
分箱,就是把连续变量变成几个区间。为什么要分箱?三个原因:
- 处理异常值:极端值会被分到边界箱子里,不会影响模型
- 增强稳定性:连续变量容易过拟合,分箱后更鲁棒
- 提升解释性:「年龄在30-40岁之间」比「年龄35.7岁」好理解得多
等距分箱,就是把数据按值域等分成N份。比如年龄0-100岁,等距分成5份就是0-20、20-40、40-60、60-80、80-100。但这种方法有个问题——如果数据分布不均匀,有的箱子里可能没几个样本。
等频分箱,就是让每个箱子里有相同数量的样本。比如1000个样本分5箱,每箱200个。这种方法能保证每个箱子都有足够的样本,但边界值可能不好解释。
卡方分箱,这是风控里最常用的方法。它基于卡方检验,把相邻且分布相似的箱子合并。说白了,就是让好客户和坏客户在每个箱子里的比例差异最大化。
分箱后的评估指标:
- WOE(Weight of Evidence):衡量每个箱子的好坏比相对于整体的差异
- IV值:分箱后重新计算IV,看分箱是否合理
- 单调性:好的分箱,WOE值应该随变量取值单调变化
# 卡方分箱示例(简化版)
import numpy as np
from scipy.stats import chi2
def chi_merge(df, feature, target, max_bins=5):
"""
卡方分箱:合并相邻且分布相似的箱子
"""
# 初始分箱:每个唯一值作为一个箱子
bins = sorted(df[feature].unique())
while len(bins) > max_bins:
# 计算相邻箱子的卡方值
chi_values = []
for i in range(len(bins)-1):
# 构建2x2列联表
left = df[df[feature] == bins[i]][target].value_counts()
right = df[df[feature] == bins[i+1]][target].value_counts()
# 计算卡方统计量(简化)
chi2_val = calc_chi2(left, right)
chi_values.append(chi2_val)
# 合并卡方值最小的相邻箱子
min_idx = np.argmin(chi_values)
bins[min_idx] = f"{bins[min_idx]}_{bins[min_idx+1]}"
bins.pop(min_idx+1)
return bins
避坑指南:我曾经在分箱时犯过一个错误——分箱后WOE值不单调。比如年龄特征,WOE值从青年到中年上升,到老年又下降。这种非单调的特征在逻辑回归里很难解释。后来我改用有监督的分箱方法,强制单调性,效果就好多了。
好了,特征工程的三个核心环节就讲到这里。特征构造是创造信息,特征筛选是去粗取精,特征分箱是化繁为简。这三步走扎实了,后面的建模工作会轻松很多。
下一章咱们聊特征工程里的一个进阶话题——时间序列特征的处理。嗯,到时候见。
公众号:蓝海资料掘金营,微信deep3321