WOE信用评分卡(Python+Excel实现)
一、项目实施背景
信用评分卡是近年来兴起的一种为保障银行和其他金融部门的金融安全而设立的一种关于人生金融权限的划定模型。该模型指根据用户的信用历史资料,利用一定的信用评分模型,得到不同等级的信用分数。根据用户的信用分数,来决定是否放贷,以及相应的授信额度。随着目前小额消费贷的蓬勃发展,银行方面面临着单笔额度较小,申请额度分散,缺乏抵押以及客户质量不确定等诸多挑战,信用评分卡将会有广泛的应用前景。
二、项目具体目标及使用数据
数据来自于Kaggle,cs-train.csv 中是关于客户申请贷款的15万条征信数据。其中SeriousDlqin2yrs,即是否存在2年内超过90天违约甚至更严重的情况。下表显示出了各个用户特征变量的描述。
|变量名 |类型 |描述|
|--------|--------|
|SeriousDlqin2yrs |布尔型 |Target变量,超过90天或更糟的逾期拖欠|
|RevolvingUtilizationOfUnsecuredLines |百分比 |无担保放款的循环利用:除了不动产和像车贷那样除以信用额度总和的无分期付款债务的信用卡和个人信用额度总额|
|Age |整型 |借款人当时的年龄|
|NumberOfTime30-59DaysPastDueNotWorse |整型 |35-59天逾期但不糟糕次数|
|DebtRatio |百分比 |负债比率|
|MonthlyIncome |浮点 |月收入
|NumberOfOpenCreditLinesAndLoans |整型 |开放式信贷和贷款数量,开放式贷款(分期付款如汽车贷款或抵押贷款)和信贷(如信用卡)的数量|
|NumberOfTimes90DaysLate |整型 |90天逾期次数:借款者有90天或更高逾期的次数
|NumberRealEstateLoansOrLines |整型| 不动产贷款或额度数量:抵押贷款和不动产放款包括房屋净值信贷额度
|NumberOfTime60-89DaysPastDueNotWorse| 整型 |60-89天逾期但不糟糕次数:借款人在在过去两年内有60-89天逾期还款但不糟糕的次数|
|NumberOfDependents |整型 |家属数量:不包括本人在内的家属数量|
三、项目的具体流程
评分卡模型的建立具体有四个过程:
过程一:数据预处理,对所有数据进行去极值以及对数据的缺失值进行处理。
过程二:数据的拆分,将原始数据拆成验证集和训练集
过程三:对数据进行WOE离散化处理
过程四:检验离散化后的数据是否相关,从而决定是否进行降维处理
过程五:调整训练集的平衡性
过程六:训练模型,并使用验证集调整模型参数
过程七:将通过验证后的模型的权重提取出来,计算测试样本中每个客户的违约率过程八:用验证集检验模型的有效性以及稳定性
过程九:通过客户违约率给客户进行打分
四、数据预处理
步骤1:加载数据以及去极值
第一步先加载numpy,pandas,matplotlib 然后将csv中数据读入dataframe中
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def load_data():
df_train=pd.read_csv('./Credit_Score_Data/cs-training.csv',sep=',',header='infer')
df_train.drop(['Unnamed: 0'],inplace=True,axis=1)
return df_train
df_train=load_data()
然后我们再通过Boxplot观察每个变量的极值问题
def outlier_check(df,c_name):
p=df_train[[c_name]].boxplot(return_type='dict')
x_outliers=p['fliers'][0].get_xdata()
y_outliers = p['fliers'][0].get_ydata()
for j in range(1):
plt.annotate(y_outliers[j], xy=(x_outliers[j], y_outliers[j]), xytext=(x_outliers[j] + 0.02, y_outliers[j]))
plt.show()
首先是变量 RevolvingUtilizationOfUnsecuredLines
outlier_check(df_train,'RevolvingUtilizationOfUnsecuredLines')
这里我们删除10000以上的记df_train=df_train[df_train['RevolvingUtilizationOfUnsecuredLines']<10000]
变量Age
这里我们删除0以及90以上的记录df_train=df_train[np.logical_and(df_train['age']>0,df_train['age']<90)]
变量NumberOfTime30-59DaysPastDueNotWorse
这里我们删除20以上的记录df_train = df_train[df_train['NumberOfTime30-59DaysPastDueNotWorse'] <20]
变量DebtRatio
这里我们删除50000以上的记录df_train = df_train[df_train['DebtRatio']<50000]
变量MonthlyIncome
这里我们删除500000以上的记录df_train = df_train[df_train['MonthlyIncome']<500000]
变量NumberOfOpenCreditLinesAndLoans
考虑到业务需要,我们认为这些借款机构特别多的人有欺诈的可能,因此我们这里不删除极值
变量NumberOfTimes90DaysLate
删除20以上的记录df_train = df_train[df_train['NumberOfTimes90DaysLate']<20]
变量NumberRealEstateLoansOrLines
删除6以上的记录df_train = df_train[df_train['NumberRealEstateLoansOrLines']<6]
变量NumberOfTime60-89DaysPastDueNotWorse
删除20以上的记录df_train = df_train[df_train['NumberOfTime60-89DaysPastDueNotWorse']<20]
变量NumberOfDependents
删除3以上的记录df_train = df_train[df_train['NumberOfDependents']<3]
步骤2:处理缺失值
接下来我们要明白数据的缺失值的分布情况, 使用count轻松得出。
def check_na(df):
print(df.count(axis=0))
显示结果为:
SeriousDlqin2yrs 106829
RevolvingUtilizationOfUnsecuredLines 106829
age 106829
NumberOfTime30-59DaysPastDueNotWorse 106829
DebtRatio 106829
MonthlyIncome 106829
NumberOfOpenCreditLinesAndLoans 106829
NumberOfTimes90DaysLate 106829
NumberRealEstateLoansOrLines 106829
NumberOfTime60-89DaysPastDueNotWorse 106829
NumberOfDependents 106829
通过len(df_train)
可以得出总共有106829个样本,因此在去除极值以后数据没有缺失值
五、数据拆分
在对数据进行进一步的拆分之前,我们要考虑到对我们训练后的机器学习算法进行有效的检验。因此我们有必要将所有的原始数据样本进行拆分,分为验证集以及训练集集。训练集用于训练模型从而使我们能够预测输入样本的违约率,而测试集则是用于验证我们训练后模型的有效性。
在进行数据拆分的过程中,要非常注意样本值的不平横性。在总共10万多个样本中SeriousDlqin2yrs为1的仅占1/10左右,如果完全采用随机抽样,那么违约样本的比例可能出现失真。因此,我们采取的方法是在违约与非违约的样本中各按一定的比例进行抽样。比如说如果验证集比例为0.2,我们在9万个非违约样本中抽出1.8万个,再从1万个违约样本中抽出2千个。抽取的代码如下所示:
def data_sample(inputX,index,test_Ratio=0.2):
from random import sample
data_array=np.atleast_1d(inputX)
class_array=np.unique(data_array)
test_list=[]
train_list=[]
for c in class_array:
temp=[]
for i,value in enumerate(data_array):
if value==c:
temp.append(index[i])
test_list.extend(sample(temp,int(len(temp)*test_Ratio)))
return list(set(index) - set(test_list)), test_list
def split_sample(df_train):
train_list,test_list=data_sample(df_train['SeriousDlqin2yrs'].tolist(),df_train.index.tolist())
df_train_section=df_train.ix[train_list,:]
df_test_section=df_train.ix[test_list,:]
return df_train_section,df_test_section
df_train,df_test=split_sample(df_train)
六、WOE数据离散化
WOE法是信用卡评分模型常用的数据离散化的方法。具体关于WOE法的介绍可以参考[http://blog.sina.com.cn/s/blog_8813a3ae0102uyo3.html]。我们这里将数据导出,然后使用Excel手工来决定数据离散的划分点。
writer=pd.ExcelWriter('C:/Users/Simonchen/Split_Result.xlsx')
df_train.to_excel(writer,'Train',index=False)
writer.save()
对于第一个变量RevolvingUtilizationOfUnsecuredLines ,其分析结果如下: