这篇文章主要用来记录学习如何建立信贷评分卡基本框架。
1 数据处理
在工作学习过程中感觉其实大部分数据处理过程在SQL中就应该完成,SQL输出的报表已经基本呈现了explanatory变量和response变量一一对应的关系。接下来在python则需要对报表进行进一步更加细节的处理。
1.1 变量类型
最终入模型的变量数据类型一般来说就是 numeric(continuous 和 discrete) 和 categorical 两种类型,但是在处理数据过程中还会有 string,time/date 等类型的数据。所以我们首先要做的就是确保这些数据类型的正确与一致。如统一转成时间戳形式;大小写不一致的数据;手机号格式统一等,这些信息都需要注意。
## 导入必要的模块
import numpy as np
import pandas as pd
import math
import datetime
import os
## 对于大部分的数据处理,以上几个python模块就足够了
rawdata.dtypes ## 首先查看各列的数据类型。
## 然后根据自己的需要转换成适合的数据类型
## 通用的数据转换方法
rawdata["phone"].astype("object") ## 还可以为“int”,“float”等
## 如果数据中含有缺失值、特殊字符astype()函数可能失效。可以先填充相应的缺失值为-999或者“-999”。
## timestamp,datetime,string之间的相互转换
## timestamp转成datetime
pd.datetime.fromtimestamp(1487312878); rawdata["date"].map(pd.datetime.fromtimestamp)
## string转成datetime
pd.to_datetime('2020-01-01'); rawdata["date"].map(pd.to_datetime)
## pd.to_datetime(data[['day', 'month', 'year']])
## datetime转成timestamp
date=pd.to_datetime('2020-01-01'); date.timestamp()
rawdata["date"].map(lambda x: x.timestamp()).astype(np.int64)
## or aa.map(pd.datetime.timestamp).astype(np.int64)
## datetime转成string
date.strftime('%Y-%m-%d')
rawdata["date"].map(lambda x: x.strftime('%Y-%m-%d %H-%m'))
1.2 新增可用变量
对于所拥有的不同数据变量,经常需要根据已有的变量进行处理,进一步处理成更为具体的变量。
如身份证得到年龄,性别以及省份信息;不同时间间隔的登录次数,放款次数以及违约次数。
1.3 缺失值以及极端值的处理
1 缺失值的处理:
1.1 对于numeric和categorical来说,缺失值占比超过80%均做删除处理;
1.2 对于numeric来说,缺失值占比较低的话,可以用均值来进行填充;
1.3 更好的处理方法,对于numeric和categorical来说,如果占比较低,就可以拿缺失值单独作为一类,或者在后面分箱过程中,将缺失值和某一类别进行合并确保woe值可以保持单调。
2 极端值的处理:
为了不丢失重要信息,先不做处理,在分箱过程中进行处理。
另外,所有变量值都要记得回溯,即要追溯到申请日当天,这样变量值才具有意义,因为同一个客户在不同的申请人通过情况会不一样,因此变量值一定要和申请日当日的实际情况相符。
2 数据分箱处理
数据分箱是指:对于numeric来说,把数据分为几个区间;对于categorical来说,把数据中类别较多的,进行重新划分,划分为类别数较少的特征。
具体分箱方法有以下几类:
1 无监督分箱:等宽分箱,等频分箱,聚类分箱,最小熵法等。
2 有监督分箱:卡方分箱,Best-KS分箱(只能二分类)等。
具体分箱方法学习笔记.
这里就以等频分箱方法来举例,做一个简单介绍:
等频分箱即按等分位数来进行分箱,每箱具有相同的记录数,每箱记录数称为箱子的深度。这是最简单的一种分箱方法。每个箱子的箱内数据的个数统一。但等频分箱法则容易将相同的值分到不同的箱中。
具体python实现如下:
## 这里主要对numeric类型数据进行分箱处理
## 首先创建等频
def cut_bin(series,n): ## 如n为5,则说明每个区间的记录数为总数的20%(1/5)
_bin=[-np.inf,-999,-1]
for i in range(n):
_bin.append(series.quantile(round((i+1)/n,2)))
_bin.append(series.max())
_bin.append(series.min())
_bin=list(set(_bin))
_bin.sort()
return _bin
## 对连续变量进行等分位数分箱操作,对标签变量不进行处理
## 对缺失值做"-999"处理
for i in var_list:
if var[i][i].dtype != "object":
new_name=i+"_bin"
var[i][new_name]=pd.cut(var[i][i].fillna(-999),cut_bin(var[i][i].fillna(-999),5),\
include_lowest=True)[:].astype('object')
if var[i][i].dtype == "object":
new_name=i+"_bin"
var[i][new_name]=var[i][i].fillna("-999")
## 这样就完成了对数据的等频分箱基本处理。
## 对于标签数据来说,则可以根据基本规律和经验手动调整完成初步分箱。
3 计算变量的WOE和IV值
完成初步分箱后,到了最为关键的WOE值和IV值的计算。
3.1 弃用dummy变量原因
其实在平时学习当中,在完成分箱操作以后,一般是进行dummy变量处理,即对于一个变量有三种标签a1,a2和a3,当该变量取值a1时,a1=1,a2=0;当该变量取值a2时,a1=0,a2=1;当该变量取值a3时,a1和a2均取0。在R中直接用factor()处理就行,非常方便。
那么为什么在这个地方不进行dummy变量处理,而计算每个变量的每个标签的WOE值?
主要dummy变量有以下几个缺陷:
1 无法准确计算出变量每个标签或区间的评分值;
2 回归模型筛选变量时可能出现某个自变量被部分地舍弃的情况。(该理由通过查资料得到,但还没有完全理解)
3.2 选择计算WOE原因
WOE又叫Weight of Evidence,即证据权重。是对初始explanatory变量的一种编码形式。它的计算公式为:
每个区间的WOE=ln((good/good.sum)/(bad/bad.sum))=ln(good/good.sum)-ln(bad/bad.sum)
=ln(good/bad)-ln(good.sum/bad.sum)
通过上面的计算公式可知,WOE越大的话,就说明该区间的好人与坏人区分效果越好,初始数据中好人,坏人都混在一起,是无法分清的。通过分箱的操作,我们就可以把好人坏人尽可能的分割开,而WOE就主要用来衡量分箱的效果,即好人坏人的分割程度的。
另外还可以从另外一个角度来理解:
Logit Model = ln(Pr(1)/(1-Pr(1)))=b0+b1x1+b2x2+······+bnxn
这样一来就很好理解了,上面函数的左边和WOE计算公式非常相像,这样一来,WOE和“ln(Pr(1)/(1-Pr(1)))”就相互对应起来,系数就可以理解为每个变量WOE的贡献度。
这样一来,WOE和ln(Pr(1)/(1-Pr(1)))就应该为正相关,也就可以很好理解b1,b2等系数应该均为正数。如果求得的系数为负数<0的话,则需要对该变量的分箱进行修正或者直接舍弃该变量(对于IV值一般以及解释变量足够多的情况下可以直接舍弃)。
对于计算的WOE需要满足单调性的理解:
根据WOE的计算公式可知,如果WOE未呈现出单调性特征的话,说明好坏特征区分度不明显,比如刚开始的区间坏人占比多,突然到了后面某个区间坏人数量有陡然增多,这就说明分箱的效果并不好或者该变量区分好人坏人的能力较差,此时就应该重新手动调整分箱区间,或者观察IV值如果太低的话就直接舍弃该变量。
另外计算WOE还有以下几个好处:
1 用WOE可以很好的解决缺失值问题
2 对于categorical变量,WOE可以用来观察各个level间的跳转对"odds=p/(1-p)"的提升是否呈线性
3 对于numeric变量,WOE值对分箱是否合理提供了有效的参考
4 有了WOE值,计算相应的IV和IV.cumsum也就方便了
3.3 计算IV值
IV(information value)=ln(Pr(good)/Pr(bad))*(Pr(good)-Pr(bad))=WOE(Pr(good)-Pr(bad))
最后是根据IV.cumsum的值来判断相应的变量是否合适。
IV值起到了从一堆解释变量找出最有预测能力的解释变量集合,也就是说IV值衡量的是解释变量的预测能力。另外类似的方法指标还有基尼系数等等。
对于IV值来说,当然越高越好,一般只要IV值大于0.1的解释变量就可入模,但需要注意如果IV值太高(大于0.5)同样是值得怀疑的,需要进一步考虑。
最后,为了保证求出的IV值和WOE值是有意义的,要求每个分箱区间都需要有好样本以及坏样本。对于解释变量来说,还有可能存在多重共线性问题,不过这个问题只是对系数的估计有较大影响,对于最后的Y值预测影响不大,可以使用VIF方法来检测剔除此类异常解释变量。
4 生成信贷评分卡
在实际学习工作过程中,其实感觉变量处理是数据分析过程中最为关键的一步,只要变量处理的很好,没有过多的异常值,选择的解释变量与response变量也有一定的的相关性。只要选择对了合适的模型,一般来说构建的模型效果还是不错的。
4.1 构建Logit Model
核心公式就是:*score=base point-(PDO/ln2)ln(odds), odds=Pr(good)/Pr(bad)
#%% 导入需要的模块
from sklearn.cross_validation import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from sklearn.metrics import precision_recall_curve,roc_curve,auc
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import math
#%% 将数据2,8分,20%测试集和80%训练集
x_train,x_test,y_train,y_test=train_test_split(data_df.iloc[:,1:],data_df.iloc[:,0],\
test_size=0.2,random_state=0)
### 构建模型部分
model=LogisticRegression()
result=model.fit(x_train,y_train)
4.2 利用ROC曲线求出AUC值测试模型预测效果
对于真实值和预测值可以分为以下几类:
真实值 1,预测值 1:TP
真实值 1,预测值 0:FN
真实值 0,预测值 1:FP
真实值 0,预测值 0:TN
得出:TPR=TP/(TP+FN),FPR=FP/(FP+TN)
这样一来,TPR越大越好,FPR越小越好,以TPR作为Y轴上的值,FPR作为X轴上的值。对response变量依次求出相应的(FPR,TPR),ROC曲线就可以绘制出来。
AUC(Area Under ROC Curve)就自然可以利用积分求出来,AUC值最大为1,同时AUC值越大,分类器分类效果越好。
#%% 使用测试集利用ROC曲线计算AUC值
y_pred=result.predict(x_test)
fpr,tpr,thresholds=roc_curve(y_test,y_pred)
roc_auc=auc(tpr,fpr)
4.3 计算总分,计算IV值以及KS值得出cut_off值
基础公式为:Score=A-Bln(odds),其中,A,B为常数。负号可以使得违约概率越低,得分越高。通常,这是分值的理想方向,高分值代表低风险。
而常数A,B值可以由下面的假设和方程得到:
设比率a0的特定点的分值为p0;比率ba0的的特定点的分值为p0+PDO;其中,b一般设为2。
所以得到下面的方程式:
p0=A-Bln(a0)
p0-PDO=A-Bln(2a0)
推出:B=PDO/ln2;A=p0+Bln(a0);其中,PDO,p0,a0是由自己设置的;例如:PDO=20,p0=500,a0=1/19(即好坏比为19:1)
最终,总分数可以根据以下公式得到:
**Total_Score=A-(PDO/ln2)(ln(odds))**
得到总分数以后,就可以再次分箱计算WOE值和相应的IV值看得到的结果区分度明不明显,以及计算KS值设置cut_off值,低于该cut_off值的客户都应该拒绝或者转人工。
4.4 计算单个变量的分数值
通过逻辑回归可以得到一系列的变量的系数b0,b1,······,bn;
因此便可以根据Single_Score(i)=-(PDO/ln2)*WOE(i)b(i)
4.5 计算评分卡模型的PSI值
群体稳定性指标PSI(population stability index )用于衡量测试样本和建模样本分数间数据分布差异性,是模型稳定性的常见指标。具体做法是主要将评分卡上线后的样本与建模时的样本比较,看上线后的模型是否稳定。
公式:
*PSI=SUM((A%-B%)ln(A%/B%))
一般来说:
PSI<0.1 样本分布有微小变化,可以继续使用模型;
PSI 0.1~0.2 样本分布有变化,建议找出造成模型不稳定的原因,优化模型的变量参数;
PSI>0.2 样本分布有显著变化,建议需要重新构建模型。