kaggle数据挖掘竞赛----信用评分卡模型分析(一)数据预处理+特征工程阶段

原文地址:https://www.jianshu.com/p/f931a4df202c
https://www.jianshu.com/p/159f381c661d
https://blog.csdn.net/zs15321583801/article/details/81234446
https://blog.csdn.net/shenxiaoming77/article/details/78771698
https://cloud.tencent.com/developer/article/1092198

前言

信用风险计量体系包括主体评级模型和债项评级两部分。主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用“四张卡”来表示,分别是A卡、B卡、C卡和F卡;债项评级模型通常按照主体的融资用途,分为企业融资模型、现金流融资模型和项目融资模型等。 我们主要讨论主体评级模型的开发过程。

一、项目流程

典型的信用风险评级模型的主要开发流程如下:
(1) 数据获取,包括获取存量客户及潜在客户的数据。存量客户是指已经在证券公司开展相关融资类业务的客户,包括个人客户和机构客户;潜在客户是指未来拟在证券公司开展相关融资类业务的客户,主要包括机构客户,这也是解决证券业样本较少的常用方法,这些潜在机构客户包括上市公司、公开发行债券的发债主体、新三板上市公司、区域股权交易中心挂牌公司、非标融资机构等。
(2) 数据预处理,主要工作包括数据清洗、缺失值处理、异常值处理,主要是为了将获取的原始数据转化为可用作模型开发的格式化数据。
(3) 探索性数据分析,该步骤主要是获取样本总体的大概情况,描述样本总体情况的指标主要有直方图、箱形图等。
(4) 变量选择,该步骤主要是通过统计学的方法,筛选出对违约状态影响最显著的指标。主要有单变量特征选择方法和基于机器学习模型的方法 。
(5) 模型开发,该步骤主要包括变量分段、变量的WOE(证据权重)变换和逻辑回归估算三部分。
(6) 模型评估,该步骤主要是评估模型的区分能力、预测能力、稳定性,并形成模型评估报告,得出模型是否可以使用的结论。
(7) 信用评分,根据逻辑回归的系数和WOE等确定信用评分的方法。将Logistic模型转换为标准评分的形式。
(8) 建立自动评分系统,根据信用评分方法,建立自动信用评分系统。

二、数据获取

数据集地址:https://www.kaggle.com/c/GiveMeSomeCredit/data

数据属于个人消费类贷款,只考虑信用评分最终实施时能够使用到的数据应从如下一些方面获取数据:
– 基本属性:包括了借款人当时的年龄。
– 偿债能力:包括了借款人的月收入、负债比率。
– 信用往来:两年内35-59天逾期次数、两年内60-89天逾期次数、两年内90
天或高于90天逾期的次数。
– 财产状况:包括了开放式信贷和贷款数量、不动产贷款或额度数量。
– 贷款属性:暂无。
– 其他因素:包括了借款人的家属数量(不包括本人在内)。
– 时间窗口:自变量的观察窗口为过去两年,因变量表现窗口为未来两年。

变量名变量描述数据类型
SeriousDlqin2yrs是否有超过90天或更长时间逾期未还的不良行为二分类(0为好,1为坏)
RevolvingUtilizationOfUnsecuredLines信用卡和个人信用额度的总余额(除房地产以及分期付款债务(如汽车贷款))除以总信贷限额。定量
age借贷者的年龄定量
NumberOfTime30-59DaysPastDueNotWorse借款者逾期30至59天的次数(过去两年中没有恶化)定量
DebtRatio月债务支出、赡养费、生活费除以总收入(负债比率)定量
MonthlyIncome月收入定量
NumberOfOpenCreditLinesAndLoans公开贷款(如汽车和抵押的分期)和信用上线(比如信用卡)数量定量
NumberOfTimes90DaysLate90天逾期次数:借款者有90天或更高逾期的次数定量
NumberRealEstateLoansOrLines抵押和房地产数量(包括房屋净值信用额度)定量
NumberOfTime60-89DaysPastDueNotWorse借款者逾期30至59天的次数(过去两年中没有恶化)定量
NumberOfDependents家庭受抚养人数(不含自己)定量

三、数据预处理

利用pandas的describe()方法,对数据集进行探索性分析,了解数据的有效值,算术均值,标准差,最小值,最大值以及四分位数等。

#载入数据
data = pd.read_csv('cs-training.csv')
#数据集确实和分布情况
data.describe().to_csv('DataDescribe.csv')

在这里插入图片描述
从结果DataDescribe.csv中可以看出,量MonthlyIncome和NumberOfDependents存在缺失,变量MonthlyIncome共有缺失值29731个,NumberOfDependents有3924个缺失值。

3.1 缺失值处理

缺失值处理的方法,包括如下几种。
(1) 直接删除含有缺失值的样本。
(2) 根据样本之间的相似性填补缺失值。
(3) 根据变量之间的相关关系填补缺失值。
变量MonthlyIncome缺失率比较大,所以我们根据变量之间的相关关系填补缺失值,我们采用随机森林法:

# 用随机森林对缺失值预测填充函数
def set_missing(df):
    # 把已有的数值型特征取出来
    process_df = df.ix[:,[5,0,1,2,3,4,6,7,8,9]]
    # 分成已知该特征和未知该特征两部分
    # dataframe.values获取的是dataframe中的数据为数组array
    known = process_df[process_df.MonthlyIncome.notnull()].values
    unknown = process_df[process_df.MonthlyIncome.isnull()].values
    # X为已知MonthlyIncome的特征属性值
    X = known[:, 1:]
    # y为结果标签值MonthlyIncome
    y = known[:, 0]
    # X与y用于训练随机森林模型,fit到RandomForestRegressor之中
    rfr = RandomForestRegressor(random_state=0, n_estimators=200,max_depth=3,n_jobs=-1)
    rfr.fit(X,y)
    # 用得到的模型进行未知特征值预测
    predicted = rfr.predict(unknown[:, 1:]).round(0)
    # 用得到的预测结果填补原缺失数据
    df.loc[(df.MonthlyIncome.isnull()), 'MonthlyIncome'] = predicted
    return df
data=set_missing(data)              # 用随机森林填补比较多的缺失值
data=data.dropna()                  # 删除比较少的缺失值
data = data.drop_duplicates()       # 删除重复项
data.to_csv('MissingData.csv',index=False)

3.2 异常值处理

异常值是指明显偏离大多数抽样数据的数值,比如个人客户的年龄为0时,通常认为该值为异常值。找出样本总体中的异常值,通常采用离群值检测的方法。
可以通过箱线图观察异常值

import matplotlib.pyplot as plt #导入图像库
fig=plt.figure()
plt.rcParams['font.sans-serif'] = ['SimHei'] # 步骤一(替换sans-serif字体)
ax1=fig.add_subplot(131) 
ax1.boxplot(data['NumberOfTime30-59DaysPastDueNotWorse'])
ax1.set_title('逾期30-59天笔数')
ax2=fig.add_subplot(132)
ax2.boxplot(data['NumberOfTimes90DaysLate'])
ax2.set_title('逾期90天笔数')
ax3=fig.add_subplot(133)
ax3.boxplot(data['NumberOfTime60-89DaysPastDueNotWorse'])
ax3.set_title('逾期60-89天笔数')
plt.show()

在这里插入图片描述
对于’可用额度比值’,‘负债率’,‘信贷数量’,‘月收入’,‘逾期笔数’,'固定资产贷款量’等去除单侧99%上部分异常值:

for variable in ['RevolvingUtilizationOfUnsecuredLines','NumberOfTime30-59DaysPastDueNotWorse','MonthlyIncome','DebtRatio','NumberOfOpenCreditLinesAndLoans',
               'NumberRealEstateLoansOrLines']:
    data=data[data[variable]<data[variable].quantile(0.99)]
# 将预处理好的数据PKL化
import pickle
with open("DataProcessing.pkl", 'wb') as f:
    pickle.dump(data, f)

四、共线性问题

在这里插入图片描述
可以看到各变量之间的相关性比较小,所以不需要操作,一般相关系数大于0.6可以进行变量剔除。

五、特征工程

挑选入模变量过程是个比较复杂的过程,需要考虑的因素很多,比如:变量的预测能力,变量之间的相关性,变量的简单性(容易生成和使用),变量的强壮性(不容易被绕过),变量在业务上的可解释性(被挑战时可以解释的通)等等。但是,其中最主要和最直接的衡量标准是变量的预测能力。IV(Information Value)衡量信用风险模型中每个自变量的预测能力,并根据这些量化指标的大小,来确定哪些变量进入模型。而IV的计算是以WOE为基础的。WOE(Weight of Evidence),WOE是对原始自变量的一种编码形式,要对一个变量进行WOE编码,需要首先把这个变量进行分组处理(也叫离散化、分箱等等,说的都是一个意思)。分组后,对于第i组,WOE的计算公式如下:
在这里插入图片描述
其中,pyi是这个组中响应客户(风险模型中,对应的是违约客户,总之,指的是模型中预测变量取值为“是”或者说1的个体)占所有样本中所有响应客户的比例,pni是这个组中未响应客户占样本中所有未响应客户的比例,#yi是这个组中响应客户的数量,#ni是这个组中未响应客户的数量,#yT是样本中所有响应客户的数量,#nT是样本中所有未响应客户的数量。
WOE表示的实际上是**“当前分组中响应客户占所有响应客户的比例”和“当前分组中没有响应的客户占所有没有响应的客户的比例”的差异**。
对上面的公式进行一个简单变换:
在这里插入图片描述变换以后我们可以看出,WOE也可以这么理解,他表示的是当前这个组中响应的客户和未响应客户的比值,和所有样本中这个比值的差异。这个差异是用这两个比值的比值,再取对数来表示的。
WOE越大,这种差异越大,这个分组里的样本响应的可能性就越大,WOE越小,差异越小,这个分组里的样本响应的可能性就越小。

同样,对于分组i,也会有一个对应的IV值,计算公式如下:
在这里插入图片描述
有了一个变量各分组的IV值,我们就可以计算整个变量的IV值,方法很简单,就是把各分组的IV相加:
在这里插入图片描述
其中,n为变量分组个数。。信用风险模型一般使用使用IV值进行特征选择

5.1 分箱处理

变量**分箱(binning)**即是对连续变量离散化(discretization),将连续变量离散化,特征离散化后,模型会更稳定,降低了模型过拟合的风险,这里的分箱使用了等距分箱和主观分箱:

(1)分箱

# 分箱转化
cut1=pd.qcut(data["RevolvingUtilizationOfUnsecuredLines"],4,labels=False)
cut2=pd.qcut(data["age"],8,labels=False)
bins3=[-1,0,1,3,5,13] # 指定多个区间 左开右闭
cut3=pd.cut(data["NumberOfTime30-59DaysPastDueNotWorse"],bins3,labels=False)
cut4=pd.qcut(data["DebtRatio"],3,labels=False)
cut5=pd.qcut(data["MonthlyIncome"],4,labels=False)
cut6=pd.qcut(data["NumberOfOpenCreditLinesAndLoans"],4,labels=False)
bins7=[-1, 0, 1, 3,5, 20]
cut7=pd.cut(data["NumberOfTimes90DaysLate"],bins7,labels=False)
bins8=[-1, 0,1,2, 3, 33]
cut8=pd.cut(data["NumberRealEstateLoansOrLines"],bins8,labels=False)
bins9=[-1, 0, 1, 3, 12]
cut9=pd.cut(data["NumberOfTime60-89DaysPastDueNotWorse"],bins9,labels=False)
bins10=[-1, 0, 1, 2, 3, 5, 21]
cut10=pd.cut(data["NumberOfDependents"],bins10,labels=False)```

5.2 分箱后WOE的计算

(2)WOE

# WOE值计算
import numpy as np
rate=data["SeriousDlqin2yrs"].sum()/(data["SeriousDlqin2yrs"].count()-data["SeriousDlqin2yrs"].sum())
def get_woe_data(cut,data):
    grouped=data["SeriousDlqin2yrs"].groupby(cut,as_index = True).value_counts()
    woe=np.log(grouped.unstack().iloc[:,1]/grouped.unstack().iloc[:,0]/rate)
    return woe
cut1_woe=get_woe_data(cut1,data)
cut2_woe=get_woe_data(cut2,data)
cut3_woe=get_woe_data(cut3,data)
cut4_woe=get_woe_data(cut4,data)
cut5_woe=get_woe_data(cut5,data)
cut6_woe=get_woe_data(cut6,data)
cut7_woe=get_woe_data(cut7,data)
cut8_woe=get_woe_data(cut8,data)
cut9_woe=get_woe_data(cut9,data)
cut10_woe=get_woe_data(cut10,data)

5.3 基于WOE的IV计算

(3)IV

# IV值计算
def get_IV_data(cut,cut_woe,data):
    grouped=data["SeriousDlqin2yrs"].groupby(cut,as_index = True).value_counts()
    cut_IV=((grouped.unstack().iloc[:,1]/data["SeriousDlqin2yrs"].sum()-grouped.unstack().iloc[:,0]/(data["SeriousDlqin2yrs"].count()-data["SeriousDlqin2yrs"].sum()))*cut_woe).sum()    
    return cut_IV
#计算各分组的IV值
cut1_IV=get_IV_data(cut1,cut1_woe,data)
cut2_IV=get_IV_data(cut2,cut2_woe,data)
cut3_IV=get_IV_data(cut3,cut3_woe,data)
cut4_IV=get_IV_data(cut4,cut4_woe,data)
cut5_IV=get_IV_data(cut5,cut5_woe,data)
cut6_IV=get_IV_data(cut6,cut6_woe,data)
cut7_IV=get_IV_data(cut7,cut7_woe,data)
cut8_IV=get_IV_data(cut8,cut8_woe,data)
cut9_IV=get_IV_data(cut9,cut9_woe,data)
cut10_IV=get_IV_data(cut10,cut10_woe,data)
IV=pd.DataFrame([cut1_IV,cut2_IV,cut3_IV,cut4_IV,cut5_IV,cut6_IV,cut7_IV,cut8_IV,cut9_IV,cut10_IV],index=['可用额度比值','年龄','逾期30-59天笔数','负债率','月收入','信贷数量','逾期90天笔数','固定资产贷款量','逾期60-89天笔数','家属数量'],columns=['IV'])
plt.rcParams['font.sans-serif'] = ['SimHei'] # 图中显示中文字体
iv=IV.plot.bar(color='b',alpha=0.3,rot=30,figsize=(10,5),fontsize=(10))
iv.set_title('特征变量与IV值分布图',fontsize=(15))
iv.set_xlabel('特征变量',fontsize=(15))
iv.set_ylabel('IV',fontsize=(15))
plt.show()

在这里插入图片描述
在这里插入图片描述
一般选取IV大于0.02的特征变量进行后续训练,从以上可以看出所有变量均满足,所以选取全部的变量。

(4)存放计算WOE后的数据进行后续分析

# 新建df_new存放woe转换后的数据
df_new=pd.DataFrame()   
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["好坏客户"]=data["SeriousDlqin2yrs"]
df_new["可用额度比值WOE"]=replace_data(cut1,cut1_woe)
df_new["年龄WOE"]=replace_data(cut2,cut2_woe)
df_new["逾期30-59天笔数WOE"]=replace_data(cut3,cut3_woe)
df_new["负债率WOE"]=replace_data(cut4,cut4_woe)
df_new["月收入WOE"]=replace_data(cut5,cut5_woe)
df_new["信贷数量WOE"]=replace_data(cut6,cut6_woe)
df_new["逾期90天笔数WOE"]=replace_data(cut7,cut7_woe)
df_new["固定资产贷款量WOE"]=replace_data(cut8,cut8_woe)
df_new["逾期60-89天笔数WOE"]=replace_data(cut9,cut9_woe)
df_new["家属数量WOE"]=replace_data(cut10,cut10_woe)
with open("WOE.pkl", 'wb') as f:
pickle.dump(df_new, f)
  • 15
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值