邮箱:dengzy95@163.com 欢迎交流建议
项目简介
信用评分技术是一种应用统计模型,其作用是对贷款申请人做风险评估分值的方法。在互金公司等各种贷款业务机构中,普遍使用信用评分,对客户实行打分制,以期对客户有一个优质与否的评判。评分卡主要分为三类A卡(申请评分卡)、B卡(行为评分卡)、C卡(贷后评分卡)。我们主要讨论的是A卡即申请评分卡,用于贷前审批阶段对借款申请人的量化评估;
评分卡原理:
申请评分卡是一种统计模型,它可基于对当前申请人的各项资料进行评估并给出一个分数,该评分能定量对申请人的偿债能力作出预判。
客户申请评分卡由一系列特征项组成,每个特征项相当于申请表上的一个问题(例如,年龄、银行流水、收入等)。每一个特征项都有一系列可能的属性,相当于每一个问题的一系列可能答案(例如,对于年龄这个问题,答案可能就有30岁以下、30到45等)。在开发评分卡系统模型中,先确定属性与申请人未来信用表现之间的相互关系,然后给属性分配适当的分数权重,分配的分数权重要反映这种相互关系。分数权重越大,说明该属性表示的信用表现越好。一个申请的得分是其属性分值的简单求和。如果申请人的信用评分大于等于金融放款机构所设定的界限分数,此申请处于可接受的风险水平并将被批准;低于界限分数的申请人将被拒绝或给予标示以便进一步审查。
数据来源
数据集来自kaggle中GiveMeSomeCredit项目。地址:https://www.kaggle.com/c/GiveMeSomeCredit/data
开发流程
一、导入数据集
二、数据预处理
三、数据探索
四、特征工程
五、建立模型
六、建立评分卡
一、数据集导入
数据集相对来说比较干净我们可以直接导入数据。
#先加载后续分箱需要用到的包和模块 import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as sns%matplotlib inlinedf = pd.read_csv("E:\python\data\GiveMeSomeCredit\cs-training.csv")df.head()
特征名称比较阅读起来比较困难,让我们重命名为中文形式。
states={ "Unnamed: 0":"用户ID", "SeriousDlqin2yrs":"好坏客户", "RevolvingUtilizationOfUnsecuredLines":"可用额度比值", # "age":"年龄", "NumberOfTime30-59DaysPastDueNotWorse":"逾期30-59天笔数", "DebtRatio":"负债率", "MonthlyIncome":"月收入", "NumberOfOpenCreditLinesAndLoans":"信贷数量", "NumberOfTimes90DaysLate":"逾期90天笔数", "NumberRealEstateLoansOrLines":"固定资产贷款量", "NumberOfTime60-89DaysPastDueNotWorse":"逾期60-89天笔数", "NumberOfDependents":"家属数量"}df.rename(columns=states,inplace=True)df.head()
二、数据预处理
2.1查看数据信息
df.info()
可以看出数据类型为浮点数和整数形式,样本总量在15000其中月收入和家属数量特征中有缺失数据。
#计算特征的缺失比例 round(df.isnull().sum()/df.好坏客户.count(),3)
2.2数据中‘月收入’的缺失值过多,我们利用均值来进行缺失值的填补工作。
对于缺失值较少的‘家属数量’我们可以直接删除缺失值。及对数据集进行去重处理。
df = df.fillna({'月收入':df['月收入'].mean()})
df1 = df.dropna()df1 = df.drop_duplicates()#删除重复项df.info()
2.3异常值检测及处理
利用箱型图对特征进行可视化来检测异常数据。
x4=df1["逾期30-59天笔数"]x5=df1["逾期60-89天笔数"]x6=df1["逾期90天笔数"]fig=plt.figure(3)ax=fig.add_subplot(111)ax.boxplot([x4,x5,x6])ax.set_xticklabels(["逾期30-59天笔数","逾期60-89天笔数","逾期90天笔数"])
通过以上特征可视化我们可以对明显偏离的样本,比如年龄为0,或是逾期次数过高进行盖帽或者是删除操作,这里采用直接删除。
#异常值过滤df1=df1[df1["可用额度比值"]<=1]df1=df1[df1["年龄"]>0]df1=df1[df1["逾期30-59天笔数"]<80]df1=df1[df1["固定资产贷款量"]<50]
三、数据可视化分析
3.1单变量可视化
cut_bins=[0,5000,10000,15000,20000,100000]month_cut=pd.cut(df1["月收入"],cut_bins)month_cut_grouped=df1["好坏客户"].groupby(month_cut).count()month_cut_grouped1=df1["好坏客户"].groupby(month_cut).sum()df3=pd.merge(pd.DataFrame(month_cut_grouped), pd.DataFrame(month_cut_grouped1),right_index=True,left_index=True)df3.rename(columns={ "好坏客户_x":"好客户","好坏客户_y":"坏客户"},inplace=True)df3.insert(2,"坏客户率",df3["坏客户"]/df3["好客户"])plt.figure()ax23=df3[["好客户","坏客户"]].plot.bar()ax23.set_xticklabels(df3.index,rotation=15)ax23.set_ylabel("客户数")ax23.set_title("好坏客户数与月收入关系")plt.figure()ax231=df3["坏客户率"].plot()ax231.set_ylabel("坏客户率")ax231.set_title("月收入与坏客户率关系")
可以看出客户主体集中在月收入10000以下的人群,月收入在15000之前的坏客率和月收入呈负相关收入越高坏客率越低,后进入一段平稳但当收入超过20000后,坏客率又在上升。这表明收入在15000以下的人群收入比较稳定,随着收入越高坏账率也就越高。而收入大于20000的人群可能从事炒股,创业等风险较大的工作,所以坏账率增大。
cut_bins=[0,2,4,20]family_cut=pd.cut(df1["家属数量"],cut_bins)family_cut_grouped=df1["好坏客户"].groupby(family_cut).count()family_cut_grouped1=df1["好坏客户"].groupby(family_cut).sum()df4=pd.merge(pd.DataFrame(family_cut_grouped), pd.DataFrame(family_cut_grouped1),right_index=True,left_index=True)df4.rename(columns={ "好坏客户_x":"好客户","好坏客户_y":"坏客户"},inplace=True)df4.insert(2,"坏客户率",df4["坏客户"]/df4["好客户"])plt.figure()ax24=df4[["好客户","坏客户"]].plot.bar()ax24.set_xticklabels(df4.index,rotation=15)ax24.set_ylabel("客户数")ax24.set_title("好坏客户数与家属数量关系")plt.figure()ax241=df4["坏客户率"].plot()ax241.set_ylabel("坏客户率")ax241.set_title("坏客户率与家属数量的关系")
可以看出客户的家属数量的主要集中在0-2之间,家属数量和坏客率呈线性相关,也就是说随着家属数量提升,坏客率也在显著提升。可能是由于家属数量大,家庭的支出也就增大,所以更容易出现坏账的情况。
3.2多变量可视化
通过变量直接的相关性系数,建立相关性矩阵,观察变量之间的关系,可以进行初步的多重共线性筛选。
plt.rcParams["font.sans-serif"]='SimHei'plt.rcParams['axes.unicode_minus'] = Falsecorr = df1.corr()#计算各变量的相关性系数xticks = list(corr.index)yticks = list(corr.index)fig = plt.figure(figsize=(10,7))ax1 = fig.add_subplot(1, 1, 1)sns.heatmap(corr, annot=True, cmap="rainbow",ax=ax1,linewidths=.5, annot_kws={ 'size': 9, 'weight': 'bold', 'color': 'blue'})ax1.set_xticklabels(xticks, rotation=35, fontsize=10)ax1.set_yticklabels(yticks, rotation=0, fontsize=10)plt.show()
热力图的颜色表示变量之间的相关性程度,可以看出变量之间没有相关性过高的情况,所以暂时不需要考虑多重共线性的问题。
四、特征工程
4.1 特征分箱
在建立风控评分卡中,一般会对特征进行分箱,以提高模型的稳定性和健壮性,消除了异常波动对评分结果的影响。
#数据分箱cut1=pd.qcut(df1["可用额度比值"],4,labels=False)cut2=pd.qcut(df1["年龄"],8,labels=False)bins3=[-1,0,1,3,5,13]cut3=pd.cut(df1["逾期30-59天笔数"],bins3,labels=False)cut4=pd.qcut(df1["负债率"],3,labels=False)cut5=pd.qcut(df1["月收入"],4,labels=False)cut6=pd.qcut(df1["信贷数量"],4,labels=False)bins7=[-1, 0, 1, 3,5, 20]cut7=pd.cut(df1["逾期90天笔数"],bins7,labels=False)bins8=[-1, 0,1,2, 3, 33]cut8=pd.cut(df1["固定资产贷款量"],bins8,labels=False)bins9=[-1, 0, 1, 3, 12]cut9=pd.cut(df1["逾期60-89天笔数"],bins9,labels=False)bins10=[-1, 0, 1, 2, 3, 5, 21]cut10=pd.cut(df1["家属数量"],bins10,labels=False)
4.2 woe转换
接下来给分箱后的数据计算woe值,woe算是一种编码形式,但是和普通的编码它实际代表了响应客户和未响应客户之间的差异情况。
公式如下:#好坏客户比率rate=df1["好坏客户"].sum()/(df1["好坏客户"].count()-df1["好坏客户"].sum())#定义woe计算函数def get_woe_data(cut): grouped=df1["好坏客户"].groupby(cut,as_index = True).value_counts() woe=np.log(pd.DataFrame(grouped).unstack().iloc[:,1]/pd.DataFrame(grouped).unstack().iloc[:,0]/rate)#计算每个分组的woe值 return woecut1_woe=get_woe_data(cut1)cut2_woe=get_woe_data(cut2)cut3_woe=get_woe_data(cut3)cut4_woe=get_woe_data(cut4)cut5_woe=get_woe_data(cut5)cut6_woe=get_woe_data(cut6)cut7_woe=get_woe_data(cut7)cut8_woe=get_woe_data(cut8)cut9_woe=get_woe_data(cut9)cut10_woe=get_woe_data(cut10)
4.3 IV值计算
IV的全称是Information Value,中文意思是信息价值,或者信息量。它的作用其实和gini和信息熵类似,都是用来衡量变量的预测能力,可以通过IV值来达到特征筛选的目的。
#定义IV值计算函数def get_IV_data(cut,cut_woe): grouped=df1["好坏客户"].groupby(cut,as_index = True).value_counts() cut_IV=((pd.DataFrame(grouped).unstack().iloc[:,1]/df1["好坏客户"].sum()-pd.DataFrame(grouped).unstack().iloc[:,0]/(df1["好坏客户"].count()-df1["好坏客户"].sum()))*cut_woe).sum() return cut_IV#计算各分组的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值可视化df_IV=pd.DataFrame([cut1_IV,cut2_IV,cut3_IV,cut4_IV,cut5_IV,cut6_IV,cut7_IV,cut8_IV,cut9_IV,cut10_IV],index=df1.columns[2:])df_IV.plot(kind="bar")
通过特征IV的可视化,可以很直观的观察特征之间的差异,我们选择IV较高的特征代入模型。
#定义一个替换函数def replace_data(cut,cut_woe): a=[] for i in cut.unique(): a.append(i) a.sort() 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)
五、建立模型
建立logistics模型,logistics回归是广义线性回归,它的在建立后和线性回归一样会赋值给特征不同的权重,很符合建立评分卡的概念。
from sklearn.linear_model import LogisticRegressionfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import roc_curve, aucx=df_newy=df1.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)print("测试成绩:{}".format(clf.score(x_test,y_test)))y_pred=clf.predict(x_test)y_pred1=clf.decision_function(x_test)
模型测试效果看起来不错,但是准确率并不能反映模型的真实效果,下面利用ROC曲线来评估模型。
#绘制ROC曲线以及计算AUC值fpr, tpr, threshold = roc_curve(y_test, y_pred1)roc_auc = auc(fpr, tpr)plt.plot(fpr, tpr, color='darkorange', label='ROC curve (area = %0.2f)' % roc_auc)plt.plot([0, 1], [0, 1], color='navy', linestyle='--')plt.xlim([0.0, 1.0])plt.ylim([0.0, 1.0])plt.xlabel('False Positive Rate')plt.ylabel('True Positive Rate')plt.title('ROC_curve')plt.legend(loc="lower right")plt.show()
模型在ROC曲线上大致表现不错,AUC也达到了0.84。
六、建立评分卡
根据资料查得评分卡创建公式
coe = clf.coef_factor = clf.intercept_import numpy as npfactor = 20 / np.log(2)offset = 600 - 20 * np.log(20) / np.log(2)#定义变量分数计算函数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)#打印输出每个特征对应的分数print("可用额度比值对应的分数:{}".format(x1))print("年龄对应的分数:{}".format(x2))print("逾期30-59天笔数对应的分数:{}".format(x3))print("逾期90天笔数对应的分数:{}".format(x7))print("逾期60-89天笔数对应的分数:{}".format(x9))
将用户数据代入到评分标准后求和,就可以得到该用户的总分。得分越高代表其越有可能成为坏账客户。