申请评分卡对于从事信贷风控行业的人来说肯定不陌生,甚至每天都会应用到。申请评分,即对申请客户打分,对于业务专家来说可以是基于经验对客户资质进行评估,最终决定是否给予通过申请,首先基于经验的评估很难量化,可能还受各种主观因素的影响导致评估标准频繁波动,而基于数据的评估是直接以分数的形式来展现,更容易进行比较,且在建立好模型之后,这套评分标准就已经确定,除非重新构建模型,稳定性更胜一筹。这篇文章主要介绍自己的一些理解以及建立评分卡的过程,第一次尝试建模,如有错误的地方,请指出。
申请评分卡
- 评分卡中的一种,其他还有行为评分卡、催收评分卡等
- 原理是基于客户在过去某个时间点截止到本次贷款或信用卡申请时的各项数据,预测其未来某一段时间内的违约概率,而评分则是以分数的形式来体现这个违约概率,即违约概率越高,对应的评分越低
- 过去某个时间点截止到本次申请即观察期,未来某一段时间即表现期
Logistic回归
- 二分类问题中常用的一种算法,具体内容:机器学习算法系列(一):Logistic回归
本次建模流程目录
一、数据获取与目标变量定义
二、探索性数据分析
三、数据预处理
四、特征工程
五、建立模型
六、模型评估
七、建立评分卡
八、拒绝推论
一、数据获取与目标变量定义
- 导入需要用到的python库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime
from mpl_toolkits.mplot3d import Axes3D
import woe
import woe.feature_process as fp
import woe.eval as eva
from statsmodels.stats.outliers_influence import variance_inflation_factor
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressionCV
from sklearn.metrics import roc_curve,auc
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
- 数据获取:取自美国一家P2P平台Lending Club的官网,选取2017年三季度-2018年二季度的放贷数据,数据更新至2019年5月
data17Q3=pd.read_csv('C:\\Users\\honglihui\\Desktop\\LendingClubLoanData-UpdateTime201905\\LoanStats_2017Q3.csv',low_memory=False,header=1)
data17Q4=pd.read_csv('C:\\Users\\honglihui\\Desktop\\LendingClubLoanData-UpdateTime201905\\LoanStats_2017Q4.csv',low_memory=False,header=1)
data18Q1=pd.read_csv('C:\\Users\\honglihui\\Desktop\\LendingClubLoanData-UpdateTime201905\\LoanStats_2018Q1.csv',low_memory=False,header=1)
data18Q2=pd.read_csv('C:\\Users\\honglihui\\Desktop\\LendingClubLoanData-UpdateTime201905\\LoanStats_2018Q2.csv',low_memory=False,header=1)
data=pd.concat([data17Q3,data17Q4,data18Q1,data18Q2],ignore_index=True)#总数据集
data.info()
<共48万条数据,144个变量,其中float64类变量107个,object类变量37个>
- 目标变量定义:
建立模型预测客户在未来一段时间内是否会违约,那必然需要先定义违约,逾期多长时间算违约,不同业务标准不同。与数据集中还款表现loan_status字段相关,作为目标变量,剩下的字段则作为解释变量 - 还款状态数据分布:
data['loan_status'].groupby(data.loan_status).count()
- 客户的还款表现基本可以反映出这个客户的信用好坏程度,好坏程度可能偏向于主观的判断,在建模之前需要将它量化;
- 好坏客户定义需要根据实际业务来确定,比如可以通过计算滚动率,观察各放款时期中的贷款有多少比例会往更坏的方向发展,并结合表现期(vintage逾期率随账龄增加达到一个稳定值,其对应的账龄长度设定为表现期长度)内的逾期情况来定义好坏客户,例如各放款时间段的平均滚动率:
正常还款-M1为5%
M1-M2为30%
M2-M3为70%
M3-M4为85%
M4-M5为98%
可定义表现期内出现M3一次及以上、M2两次及以上为坏客户,
定义表现期内正常还款、M1不超过一次的为好客户,剩下的定义为不确定,从数据集中剔除;本次数据集缺少历史详细逾期数据,因此这里基于业务直接采取如下定义:其中坏客户标记为1,好客户标记为0,不确定的标记为Undefined:
loan_status_dict={'Charged Off':1,'Default':1,'Late (31-120 days)':1,
'Fully Paid':0,'Current':0,
'In Grace Period':'Undefined','Late (16-30 days)':'Undefined'}
替换原始还款状态为标记值{0,1,Undefined}:
data.loan_status.replace(to_replace=loan_status_dict,inplace=True)
值替换后的贷款状态字段:
data['loan_status'].groupby(data.loan_status).count()
更新数据,只保留{0,1}数据,并修改目标变量名为target:
data=data[data.loan_status.isin([1,0])]
data.rename(columns={'loan_status':'target'},inplace=True)
重置index:
data.reset_index(drop=True,inplace=True)
剩余475404组数据:
data.info()
- 好坏客户分布与占比:
sns.set_style('darkgrid')
plt.rcParams['font.sans-serif']='Microsoft YaHei'
plt.rcParams['figure.dpi']=150
plt.subplot(121)
bad=data.target.sum() #正样本量
good=data.shape[0]-bad #负样本量
sns.barplot(x=['good','bad'],y=[good,bad])
plt.text(0,good+10000,good,ha='center',va='baseline',fontsize=10)
plt.text(1,bad+10000,bad,ha='center',va='baseline',fontsize=10)
plt.subplot(122)
bad_ratio=data.target.sum()/data.shape[0]#正样本占比
good_ratio=1-bad#负样本占比
plt.pie([good,bad],labels=['good','bad'],autopct='%1.1f%%',startangle=30,explode=[0,0.3],textprops={'fontsize':10},shadow=True)
plt.axis('equal')
好坏客户分布与占比
二、探索性数据分析
- 数据基本分布
- 各个季度的放款量:
sns.barplot(x=('2017Q3','2017Q4','2018Q1','2018Q2'),
y=[data17Q3.shape[0],data17Q4.shape[0],data18Q1.shape[0],data18Q2.shape[0]])
各季度放款量
<各季度放款量基本一致,在12万左右>
- 贷款金额分布:
draw=data.copy()#用于作图的数据副本
sns.distplot(a=draw.loan_amnt,bins=20)
贷款金额分布
<申请金额均不超过4万,小额贷款占多数,金额分布主要集中在5000-20000>
- 借款期限、贷款金额分布:
draw['term']=draw['term'].str.replace('months',' ').str.strip().astype('int')
sns.violinplot(x=draw['term'],y=draw['loan_amnt'])
借款期限、贷款金额分布
<期限有36期和60期,36期的金额主要在10000及以下,60期的金额主要在10000以上>
- 借款金额、利率随客户等级的变化:
draw['int_rate']=draw['int_rate'].str.replace('%',' ').str.strip().astype('float')
plt.subplot(211)
sns.violinplot(x=draw['grade'],y=draw['loan_amnt'])
plt.subplot(212)
sns.violinplot(x=draw['grade'],y=draw['int_rate'])
plt.ylabel('int_rate(%)',fontdict={'fontsize':11},labelpad=20)