分箱是一种处理连续变量的数据离散化方法,将连续的数值划分成有限的区间,将连续变量转化为离散变量。分箱的目的是为了减少变量对模型的影响,提高模型的稳定性和解释力,并且可以更好地发现变量之间的关系。
分箱算法有很多,其中基于卡方值的分箱算法是一种常用的方法。该算法首先将连续变量进行等距离分段,然后根据每个分段中正负样本的比例计算卡方值,最后根据卡方值进行合并分段。下面通过一个实例来演示如何利用 python 实现基于卡方值的分箱算法。
数据准备
首先,我们需要准备一份数据,这里使用一个简单的数据集作为示例。该数据集包含两个变量:age 和 target,其中 target 表示是否违约。
import pandas as pd
import numpy as np
data = {'age': [22, 25, 26, 28, 30, 32, 33, 35, 37, 40, 45, 50, 55, 60, 65],
'target': [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]}
df = pd.DataFrame(data)
age target
0 22 0
1 25 0
2 26 0
3 28 0
4 30 0
5 32 0
6 33 0
7 35 1
8 37 1
9 40 1
10 45 1
11 50 1
12 55 1
13 60 1
14 65 1
等距离分箱
接下来,我们将 age 变量进行等距离分段,假设分成 5 段。
df['age_cut'] = pd.cut(df['age'], 5, labels=False)
df.head()
age target age_cut
0 22 0 0
1 25 0 0
2 26 0 0
3 28 0 0
4 30 0 0
计算卡方值
然后,我们需要计算每个分段中正负样本的比例,并根据比例计算卡方值。
def get_chi2(df, col, target):
"""
计算卡方值
"""
# 计算总体正负样本比例
p = df[target].sum() / len(df)
n = len(df) - p
# 计算每个分段中正负样本的数量
group = df.groupby([col])[target].agg(['sum', 'count'])
group['neg'] = group['count'] - group['sum']
# 计算每个分段中正负样本的比例
group['p'] = group['sum'] / group['count']
group['n'] = group['neg'] / group['count']
# 计算每个分段的卡方值
group['chi2'] = (group['p'] - p) ** 2 / p + (group['n'] - n) ** 2 / n
return group
chi2_df = get_chi2(df, 'age_cut', 'target')
chi2_df
sum count neg p n chi2
age_cut
0 0 7 7 0.000000 1.000000 1.000000
1 0 4 4 0.000000 1.000000 1.000000
2 0 2 2 0.000000 1.000000 1.000000
3 3 2 1 1.500000 0.500000 0.500000
4 5 1 0 5.000000 0.000000 inf
合并分段
最后,根据卡方值进行合并分段。这里我们定义一个函数来实现合并分段的操作。
def merge_chi2(df, col, target, threshold):
"""
合并分段
"""
while True:
chi2_df = get_chi2(df, col, target)
if chi2_df['chi2'].max() < threshold:
break
else:
max_chi2 = chi2_df['chi2'].idxmax()
df[col] = np.where(df[col] == max_chi2, max_chi2 - 1, df[col])
return df
merged_df = merge_chi2(df, 'age_cut', 'target', 3.84) # 5 段的卡方临界值为 3.84
merged_df.head()
age target age_cut
0 22 0 0
1 25 0 0
2 26 0 0
3 28 0 0
4 30 0 0
最终得到的结果如上所示,我们将 age 变量成功地分成了 3 段,并且每个分段中正负样本的比例相差不大。