评分卡模型

评分卡模型

数据导入

使用kaggle上的Give Me Some Credit数据

1
df = pd.read_csv('cs-training.csv')

数据预处理

缺失值处理

1
print(df.info())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150000 entries, 0 to 149999
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 用户ID 150000 non-null int64
1 好坏客户 150000 non-null int64
2 可用额度比值 150000 non-null float64
3 年龄 150000 non-null int64
4 逾期30-59天笔数 150000 non-null int64
5 负债率 150000 non-null float64
6 月收入 120269 non-null float64
7 信贷数量 150000 non-null int64
8 逾期90天笔数 150000 non-null int64
9 固定资产贷款数量 150000 non-null int64
10 逾期60-89天笔数 150000 non-null int64
11 家属数量 146076 non-null float64
dtypes: float64(4), int64(8)
memory usage: 13.7 MB

月收入缺失比:19.82%,家属数量缺失比:2.62%。

家属数量对应的缺失比例低于5%,可直接删除;月收入缺失比比较高,不能直接删除,利用填充平均值的方法进行补充。

1
2
3
4
# 缺失值填充
df = df.fillna({'月收入':df['月收入'].mean()})
# 缺失值删除
df.dropna(inplace = True)

异常值处理

用箱形图判断异常值,再过滤异常值。

1
2
3
4
5
# 异常值过滤
df = df[df["固定资产贷款数量"]<50]
df = df[df["逾期30-59天笔数"]<80]
df = df[df["年龄"]>0]
df = df[df['可用额度比值']<=1]

特征选择

过滤掉一些对于目标变量影响权重较小的特征变量,这里使用IV值进行特征筛选。

WOE分箱

WOE(Weight of Evidence)即证据权重,可以将logistic回归模型转化为标准评分卡格式,WOE是对原始自变量的一种编码形式,要对一个变量进行WOE编码,需要首先把这个变量进行分组处理(也叫离散化、分箱)。WOE=ln(坏样本占比/好样本占比)

WOEi=ln(BadX=Xi/BadtatolGoodX=Xi/Goodtatol)WOE_i=\ln(\frac{Bad_{X=X_i}/Bad_{tatol}}{Good_{X=X_i}/Good_{tatol}})

WOE分箱就是将连续变量离散化, 即切分成不同的区间段,离散化后的变量具有很好的稳定性,比如年龄这个连续变量,如果是连续值的时候21和29就是两个不同的值,对模型的效果可能就不一样,如果离散化成20-30的时候,这两个年龄对模型的效果就是一样,更加稳定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 分箱
cut1, bins1 = pd.qcut(df["可用额度比值"],4,labels=False, retbins=True)
cut2, bins2 = pd.qcut(df["年龄"],8,labels=False, retbins=True)
bins3=[-1,0,1,3,5,13]
cut3=pd.cut(df["逾期30-59天笔数"],bins3,labels=False)
cut4=pd.qcut(df["负债率"],3,labels=False)
cut5=pd.qcut(df["月收入"],4,labels=False)
cut6=pd.qcut(df["信贷数量"],4,labels=False)
bins7=[-1, 0, 1, 3,5, 20]
cut7=pd.cut(df["逾期90天笔数"],bins7,labels=False)
bins8=[-1, 0,1,2, 3, 33]
cut8=pd.cut(df["固定资产贷款数量"],bins8,labels=False)
bins9=[-1, 0, 1, 3, 12]
cut9=pd.cut(df["逾期60-89天笔数"],bins9,labels=False)
bins10=[-1, 0, 1, 2, 3, 5, 21]
cut10=pd.cut(df["家属数量"],bins10,labels=False)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 计算WOE
# 整体好坏比(定义标签为1为好客户)
total_rate = df['好坏客户'].sum()/(df['好坏客户'].count()-df['好坏客户'].sum())

# 定义WOE函数
def get_woe(cut):
grouped = df.groupby(cut)['好坏客户'].value_counts()
# 组内好坏比
woe = np.log(pd.DataFrame(grouped).unstack().iloc[:,1]/pd.DataFrame(grouped).unstack().iloc[:,0]/total_rate)
return woe
# 计算WOE值,如果计算出来的WOE不是单调的就需要重新分箱
cut1_woe = get_woe(cut1)
cut2_woe = get_woe(cut2)
cut3_woe = get_woe(cut3)
cut4_woe = get_woe(cut4)
cut5_woe = get_woe(cut5)
cut6_woe = get_woe(cut6)
cut7_woe = get_woe(cut7)
cut8_woe = get_woe(cut8)
cut9_woe = get_woe(cut9)
cut10_woe = get_woe(cut10)

计算IV值

IV(Information value,IV)信息值,IV值衡量一个变量的信息量,计算公式为SUM((好样本占比-坏样本占比)* 迹象权数)

IV=i=1N(GoodX=Xi/GoodtatolBadX=Xi/Badtatol)×WOEiIV=\sum_{i=1}^{N}(Good_{X=X_i}/Good_{tatol}-Bad_{X=X_i}/Bad_{tatol})\times WOE_i

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 计算每个变量的IV值
def get_iv_data(cut,cut_woe):
grouped = df.groupby(cut)['好坏客户'].value_counts()
cut_iv = ((grouped.unstack().iloc[:,1]/df['好坏客户'].sum()- grouped.unstack().iloc[:,0]/(df['好坏客户'].count()-df['好坏客户'].sum()))*cut_woe).sum()
return cut_iv


cut1_iv = get_iv_data(cut1,cut1_woe)
cut2_iv = get_iv_data(cut2,cut2_woe)
cut3_iv = get_iv_data(cut3,cut3_woe)
cut4_iv = get_iv_data(cut4,cut4_woe)
cut5_iv = get_iv_data(cut5,cut5_woe)
cut6_iv = get_iv_data(cut6,cut6_woe)
cut7_iv = get_iv_data(cut7,cut7_woe)
cut8_iv = get_iv_data(cut8,cut8_woe)
cut9_iv = get_iv_data(cut9,cut9_woe)
cut10_iv = get_iv_data(cut10,cut10_woe)

可以看到[“负债率”,“月收入”,“信贷数量”,“固定资产贷款量”,“家属数量”]这几个特征的IV值过低,对目标变量的影响较小,将其过滤掉。

用WOE值代替原始数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 原始分组后的数据集,根据各组标签替代为对应的WOE值
def replace_data(cut,cut_woe):
# 保存各箱标签
a = []
for i in cut.unique():
a.append(i)
a.sort() # 每箱从小到大排序,并计算对应的WOE值
for m in range(len(a)):
cut.replace(a[m],cut_woe.values[m],inplace = True)
return cut
# 进行替换
df_new = pd.DataFrame()
df_new['可用额度比值'] = replace_data(cut1,cut1_woe)
df_new['年龄']=replace_data(cut2,cut2_woe)
df_new['逾期30-59天笔数'] = replace_data(cut3,cut3_woe)
df_new['逾期90天笔数'] = replace_data(cut7,cut7_woe)
df_new['逾期60-89天笔数'] = replace_data(cut9,cut9_woe)
df_new['好坏客户'] = df['好坏客户']

模型训练

1
2
3
4
5
6
7
8
9
# 模型训练及测试
x = df_new.iloc[:,0:-1]
y = df_new.iloc[:,-1]
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size = 0.4,random_state = 0)
model = LogisticRegression()
clf = model.fit(x_train,y_train)
# 预测标签
y_pred = clf.predict(x_test)
print("测试集的平均正确率:{}".format(clf.score(x_test,y_test)))
1
测试集的平均正确率:0.9423646788588662

计算得分

Score=A+B×ln(odds)=A+B×(ω0+ω1x1++ωnxx)=(A+Bω0)+Bω1x1++Bωnxx\begin{align} Score&=A+B\times\ln(odds) \\ &=A+B\times(\omega_0+\omega_1x_1+\cdots+\omega_nx_x)\\ &=(A+B\omega_0)+B\omega_1x_1+\cdots+B\omega_nx_x \end{align}

约定:当odds增加一倍,分数增加20分;当odds = 1,分数为600分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 系数值
coe = clf.coef_
bias = clf.intercept_
# score = offset+factor*log(odds)
factor = 20 / np.log(2)
offset = 600

# 定义变量分数计算函数
def get_score(coe,woe,factor):
scores = []
for w in woe:
score=round(coe*w*factor,0)
scores.append(score)
return scores

# 计算每个变量得分
x1 = get_score(coe[0][0],cut1_woe,factor)
x2 = get_score(coe[0][1],cut2_woe,factor)
x3 = get_score(coe[0][2],cut3_woe,factor)
x7 = get_score(coe[0][3],cut7_woe,factor)
x9 = get_score(coe[0][4],cut9_woe,factor)
base_score = offset + factor * bias;
1
2
3
4
5
6
7
8
基础分数:[521.32931339]
可用额度比值对应分箱:[0., 0.02953715 0.1480833, 0.52137127, 1.]
可用额度比值对应的分数:[-22.0, -21.0, -5.0, 19.0]
年龄对应分箱:[ 21. 35. 41. 47. 52. 57. 63. 70. 107.]
年龄对应的分数:[8.0, 5.0, 4.0, 3.0, -0.0, -6.0, -13.0, -16.0]
逾期30-59天笔数对应的分数:[-8.0, 14.0, 27.0, 37.0, 42.0]
逾期90天笔数对应的分数:[-6.0, 34.0, 48.0, 57.0, 57.0]
逾期60-89天笔数对应的分数:[-3.0, 24.0, 35.0, 39.0]

获得评分卡

变量 分箱类别 分数
基础分数 - 521.32931339
可用额度 0 - 0.02953715 -22.0
0.02953715 - 0.1480833 -21.0
0.1480833 - 0.52137127 -5.0
0.52137127 - 1 19.0
年龄 21 - 35 8.0
35 - 41 5.0
41 - 47 4.0
47 - 52 3.0
52 - 57 -0.0
57 - 63 -6.0
63 - 70 -13.0
70 - 107 -16.0
逾期30-59天笔数 -1 - 0 -8.0
0 - 1 14.0
1 - 3 27.0
3 - 5 37.0
5 - 13 42.0
逾期60-89天笔数 -1 - 0 -3.0
0 - 1 24.0
1 - 3 35.0
3 - 12 39.0
逾期90天笔数 -1 - 0 -6.0
0 - 1 34.0
1 - 3 48.0
3 - 5 57.0
5 - 20 57.0

上面得到的是不同特征值对应的分数,评分越高表明该用户越有可能相应目标变量,成为坏用户;特征划分区间是依次递增的,特征区间值与得分是相对应的,年龄越大,坏账的可能性越低;逾期笔数越多,坏账可能性越高,得分越高;最后将所有的变量对应的得分相加,就是每个用户的得分。


评分卡模型
https://wangyinan.cn/评分卡模型
作者
yinan
发布于
2023年8月13日
许可协议