如今在银行、消费金融公司等各种贷款业务机构,普遍使用信用评分,对客户实行打分制,以期对客户有一个优质与否的评判。评分卡分为三类分别为:

A卡(Application score card)申请评分卡

B卡(Behavior score card)行为评分卡

C卡(Collection score card)催收评分卡

评分机制的区别在于:

1.使用的时间不同。分别侧重贷前、贷中、贷后;

2.数据要求不同。A卡一般可做贷款0-1年的信用分析,B卡则是在申请人有了一定行为后,有了较大数据进行的分析,一般为3-5年,C卡则对数据要求更大,需加入催收后客户反应等属性数据。

3.每种评分卡的模型会不一样。在A卡中常用的有逻辑回归,AHP等,而在后面两种卡中,常使用多因素逻辑回归,精度等方面更好。

对于建立评分卡模型,我们参照以下的流程:

一. 数据预处理

此次的数据来源于Kaggle的Give Me Some Credit项目,首先来看一下数据:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor
import seaborn as sns
from scipy import stats
import copy%matplotlib inlinetrain_data = pd.read_csv('cs-training.csv')
train_data = train_data.iloc[:,1:]
train_data.info()

1.1 处理缺失值

可以看到数据方面,对于缺失比较多的MonthlyIncome,在此建立随机森林模型进行填补,而缺失较少的NumberOfDependts,则直接删除缺样本。

mData = train_data.iloc[:,[5,0,1,2,3,4,6,7,8,9]]
train_known = mData[mData.MonthlyIncome.notnull()].as_matrix()
train_unknown = mData[mData.MonthlyIncome.isnull()].as_matrix()
train_X = train_known[:,1:]
train_y = train_known[:,0]
rfr = RandomForestRegressor(random_state=0,n_estimators=200,max_depth=3,n_jobs=-1)
rfr.fit(train_X,train_y)
predicted_y = rfr.predict(train_unknown[:,1:]).round(0)
train_data.loc[train_data.MonthlyIncome.isnull(),'MonthlyIncome'] = predicted_ytrain_data = train_data.dropna()
train_data = train_data.drop_duplicates()

1.2 处理异常值

缺失值处理后,来处理异常值。异常值一般是指偏离数据较大的值。例如在统计学中,常把低于 Q1-1.5IQR的值和高于Q3+1.5IQR的值作为异常值。通过绘制箱型图能很明显的看到异常值,例如:

train_box = train_data.iloc[:,[3,7,9]]
train_box.boxplot()

很明显可以看到,在这三个特征之中有两组样本偏离了其他样本的分布,可以将其去除,此外,我们发现在age为0的样本,这很明显是不符合常识的,应同样作为异常值舍弃:

train_data = train_data[train_data['NumberOfTime30-59DaysPastDueNotWorse']<90]
train_data = train_data[train_data.age>0]
train_data['SeriousDlqin2yrs'] = 1-train_data['SeriousDlqin2yrs'] #使好客户为1,违约客户为0

1.3 数据切分

为了使得能够更好地检验模型效果,我们将数据切分化为训练集和测试集。测试集取原数据的30%:

from sklearn.cross_validation import train_test_split
y = train_data.iloc[:,0]
X = train_data.iloc[:,1:]
train_X,test_X,train_y,test_y = train_test_split(X,y,test_size =0.3,random_state=0)
ntrain_data = pd.concat([train_y,train_X],axis=1)
ntest_data = pd.concat([test_y,test_X],axis=1)

二. 探索性分析

在建立模型之前,我们一般会对现有的数据进行 探索性数据分析(Exploratory Data Analysis) 。 EDA是指对已有的数据(特别是调查或观察得来的原始数据)在尽量少的先验假定下进行探索。常用的探索性数据分析方法有:直方图、散点图和箱线图等。
age = ntrain_data['age']
sns.distplot(age)

可以看到,年龄的分布大致呈正态分布,符合统计分析假设。

mi = ntrain_data[['MonthlyIncome']]
sns.distplot(mi)

同样,收入的分布也大致呈正态分布。

三.变量选择

3.1 分箱处理

首先,需要将特征进行分箱处理。分箱是将连续特征离散化的一种方式,一般有等距,等频,卡方分箱的等多种方式,合理的分箱可以使模型更加精准。在此,我使用的是一种常见于SAS上的单调分箱,python代码由这为大神提供。

def mono_bin(Y, X, n=10):r = 0good=Y.sum()bad=Y.count()-goodwhile np.abs(r) < 1: d1 = pd.DataFrame({"X": X, "Y": Y, "Bucket": pd.qcut(X, n)})d2 = d1.groupby('Bucket', as_index = True)r, p = stats.spearmanr(d2.mean().X, d2.mean().Y)  n = n - 1d3 = pd.DataFrame(d2.X.min(), columns = ['min'])d3['min']=d2.min().Xd3['max'] = d2.max().Xd3['sum'] = d2.sum().Yd3['total'] = d2.count().Yd3['rate'] = d2.mean().Yd3['woe']=np.log((d3['rate']/good)/((1-d3['rate'])/bad))d3['goodattribute']=d3['sum']/goodd3['badattribute']=(d3['total']-d3['sum'])/badiv=((d3['goodattribute']-d3['badattribute'])*d3['woe']).sum()d4 = (d3.sort_index(by = 'min')).reset_index(drop=True)woe=list(d4['woe'].round(3))cut=[]cut.append(float('-inf'))for i in range(1,n+1):qua=X.quantile(i/(n+1))cut.append(round(qua,4))cut.append(float('inf'))return d4,iv,cut,woe
x1_d,x1_iv,x1_cut,x1_woe = mono_bin(train_y,train_X.RevolvingUtilizationOfUnsecuredLines)x2_d,x2_iv,x2_cut,x2_woe = mono_bin(train_y,train_X.age)x4_d,x4_iv,x4_cut,x4_woe = mono_bin(train_y,train_X.DebtRatio)x5_d,x5_iv,x5_cut,x5_woe = mono_bin(train_y,train_X.MonthlyIncome)

对于 RevolvingUtilizationOfUnsecuredLines、age、DebtRatio和MonthlyIncome我们使用这种方式进行分类。

然而,其他的变量无法通过这种方式分箱,故我们使用人工选择的方式进行:

cutx3 = [-inf, 0, 1, 3, 5, +inf]
cutx6 = [-inf, 1, 2, 3, 5, +inf]
cutx7 = [-inf, 0, 1, 3, 5, +inf]
cutx8 = [-inf, 0,1,2, 3, +inf]
cutx9 = [-inf, 0, 1, 3, +inf]
cutx10 = [-inf, 0, 1, 2, 3, 5, +inf]
以NumberOfTime30-59DaysPastDueNotWorse为例:
def woe_value(d1):d2 = d1.groupby('Bucket', as_index = True)good=train_y.sum()bad=train_y.count()-goodd3 = pd.DataFrame(d2.X.min(), columns = ['min'])d3['min']=d2.min().Xd3['max'] = d2.max().Xd3['sum'] = d2.sum().Yd3['total'] = d2.count().Yd3['rate'] = d2.mean().Yd3['woe'] = np.log((d3['rate']/good)/((1-d3['rate'])/bad))d3['goodattribute']=d3['sum']/goodd3['badattribute']=(d3['total']-d3['sum'])/badiv=((d3['goodattribute']-d3['badattribute'])*d3['woe']).sum()d4 = (d3.sort_index(by = 'min')).reset_index(drop=True)woe=list(d4['woe'].round(3))return d4,iv,woe
d1 = pd.DataFrame({"X": train_X['NumberOfTime30-59DaysPastDueNotWorse'], "Y": train_y})
d1['Bucket'] = d1['X']
d1_x1 = d1.loc[(d1['Bucket']<=0)]
d1_x1.loc[:,'Bucket']="(-inf,0]"d1_x2 = d1.loc[(d1['Bucket']>0) & (d1['Bucket']<= 1)]
d1_x2.loc[:,'Bucket'] = "(0,1]"d1_x3 = d1.loc[(d1['Bucket']>1) & (d1['Bucket']<= 3)]
d1_x3.loc[:,'Bucket'] = "(1,3]"d1_x4 = d1.loc[(d1['Bucket']>3) & (d1['Bucket']<= 5)]
d1_x4.loc[:,'Bucket'] = "(3,5]"d1_x5 = d1.loc[(d1['Bucket']>5)]
d1_x5.loc[:,'Bucket']="(5,+inf)"
d1 = pd.concat([d1_x1,d1_x2,d1_x3,d1_x4,d1_x5])x3_d,x3_iv,x3_woe= woe_value(d1)
x3_cut = [float('-inf'),0,1,3,5,float('+inf')]

在分箱的过程中,同时计算了WOE(Weight of Evidence)和IV(Information Value),前者在建立逻辑回归模型是需要将所有的变量转为WOE,而后者则可以很好的展示变量的预测能力。这两个值的计算方式如下:

在通过IV值判断之前可以先检查一下变量之间的相关性,对变量有个直观的了解:

corr = train_data.corr()
xticks = ['x0','x1','x2','x3','x4','x5','x6','x7','x8','x9','x10']
yticks = list(corr.index)
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
sns.heatmap(corr, annot=True, cmap='rainbow', ax=ax1, annot_kws={'size': 5,  'color': 'blue'})
ax1.set_xticklabels(xticks, rotation=0, fontsize=10)
ax1.set_yticklabels(yticks, rotation=0, fontsize=10)
plt.show()

可以看到 NumberOfTime30-59DaysPastDueNotWorse,NumberOfOpenCreditLinesAndLoans和NumberOfTime60-89DaysPastDueNotWorse这三个特征对于我们所要预测的值有较强的相关性。

接下来,看一下各个变量的IV值:

informationValue = []
informationValue.append(x1_iv)
informationValue.append(x2_iv)
informationValue.append(x3_iv)
informationValue.append(x4_iv)
informationValue.append(x5_iv)
informationValue.append(x6_iv)
informationValue.append(x7_iv)
informationValue.append(x8_iv)
informationValue.append(x9_iv)
informationValue.append(x10_iv)
informationValueindex=['x1','x2','x3','x4','x5','x6','x7','x8','x9','x10']
index_num = range(len(index))
ax=plt.bar(index_num,informationValue,tick_label=index)
plt.show()

通过IV值判断变量预测能力的标准是:

< 0.02: unpredictive

0.02 to 0.1: weak

0.1 to 0.3: medium

0.3 to 0.5: strong

> 0.5: suspicious

可以看到,对于X4,X5,X6,X8,以及X10而言,IV值都比较低,因此可以舍弃这些预言能力较差的特征

3.2 WOE转换

接下来,将所有的需要的特征woe化,并将不需要的特征舍弃,仅保留WOE转码后的变量:

def trans_woe(var,var_name,x_woe,x_cut):woe_name = var_name + '_woe'for i in range(len(x_woe)):if i == 0:var.loc[(var[var_name]<=x_cut[i+1]),woe_name] = x_woe[i]elif (i>0) and (i<= len(x_woe)-2):var.loc[((var[var_name]>x_cut[i])&(var[var_name]<=x_cut[i+1])),woe_name] = x_woe[i]else:var.loc[(var[var_name]>x_cut[len(x_woe)-1]),woe_name] = x_woe[len(x_woe)-1]return varx1_name = 'RevolvingUtilizationOfUnsecuredLines'
x2_name = 'age'
x3_name = 'NumberOfTime30-59DaysPastDueNotWorse'
x7_name = 'NumberOfTimes90DaysLate'
x9_name = 'NumberOfTime60-89DaysPastDueNotWorse'train_X = trans_woe(train_X,x1_name,x1_woe,x1_cut)
train_X = trans_woe(train_X,x2_name,x2_woe,x2_cut)
train_X = trans_woe(train_X,x3_name,x3_woe,x3_cut)
train_X = trans_woe(train_X,x7_name,x7_woe,x7_cut)
train_X = trans_woe(train_X,x9_name,x9_woe,x9_cut)
train_X = train_X.iloc[:,-5:]

此时数据如下所示:

四.模型分析

4.1 模型建立

通过调用STATSMODEL包来建立逻辑回归模型:

import statsmodels.api as sm
X1=sm.add_constant(train_X)
logit=sm.Logit(train_y,X1)
result=logit.fit()
print(result.summary())

结果如下:

4.2 模型检验

模型建立后,可以通过导入测试集的数据,画出ROC曲线来判断模型的准确性:

1.对测试集进行woe转化

test_X = trans_woe(test_X,x1_name,x1_woe,x1_cut)
test_X = trans_woe(test_X,x2_name,x2_woe,x2_cut)
test_X = trans_woe(test_X,x3_name,x3_woe,x3_cut)
test_X = trans_woe(test_X,x7_name,x7_woe,x7_cut)
test_X = trans_woe(test_X,x9_name,x9_woe,x9_cut)test_X = test_X.iloc[:,-5:]

2.拟合模型,画出ROC曲线得到AUC值

from sklearn import metrics
X3 = sm.add_constant(test_X)
resu = result.predict(X3)
fpr, tpr, threshold = metrics.roc_curve(test_y, resu)
rocauc = metrics.auc(fpr, tpr)
plt.plot(fpr, tpr, 'b', label='AUC = %0.2f' % rocauc)
plt.legend(loc='lower right')
plt.plot([0, 1], [0, 1], 'r--')
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.ylabel('TPR')
plt.xlabel('FPR')
plt.show()

可以看到,ACU=0.85,是可以接受的。

五.建立评分卡

5.1 评分标准

依据以上论文资料得到:

a=log(p_good/P_bad)

Score = offset + factor * log(odds)

在建立标准评分卡之前,我们需要选取几个评分卡参数:基础分值、 PDO(比率翻倍的分值)和好坏比。 这里, 我们取600分为基础分值,PDO为20 (每高20分好坏比翻一倍),好坏比取20。

5.2 建立评分卡

p = 20/np.log(2)
q = 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 scoresx_coe = [2.6084,0.6327,0.5151,0.5520,0.5747,0.4074]
baseScore = round(q + p * x_coe[0], 0)x1_score = get_score(x_coe[1], x1_woe, p)
x1_score = get_score(x_coe[1], x1_woe, p)
x2_score = get_score(x_coe[2], x2_woe, p)
x3_score = get_score(x_coe[3], x3_woe, p)
x7_score = get_score(x_coe[4], x7_woe, p)
x9_score = get_score(x_coe[5], x9_woe, p)

x_coe是之前逻辑回归模型得到的系数。最后BaseScore等于589分。

通过get_score可以得到所有分段的分数,如下:
根据前面章节的分箱结果和得到的分数,可以建立评分卡:

5.3 自动计算评分

建立一个函数使得当输入x1,x2,x3,x7,x9的值时可以返回评分数

cut_t = [x1_cut,x2_cut,x3_cut,x7_cut,x9_cut]
def compute_score(x):        #x为数组,包含x1,x2,x3,x7和x9的取值tot_score = baseScorecut_d = copy.deepcopy(cut_t)for j in range(len(cut_d)):cut_d[j].append(x[j])cut_d[j].sort()for i in range(len(cut_d[j])):if cut_d[j][i] == x[j]:tot_score = score[j][i-1] +tot_scorereturn tot_score

来测试一下:

总结

至此此次基于python制作的行为评分卡就此完成。本文通过对于Kaggle上项目的数据进行分析,利用逻辑回归制作了一个简单的评分卡。在建立评分卡的过程中,首先进行了数据清洗,对缺失值和异常值进行了处理并对数据分布进行了宏观展示。然后对特征值进行了处理,将连续的变量分箱,同时计算了woe和iv值,并保留了iv值较高的变量对其woe转化。最后将woe转化后的数据进行逻辑回归分析,利用得到变量系数并自行拟定了评分标准建立了评分卡。

在整体过程中,并没有对数据进行过多的挖掘。例如:只舍弃了个别变量的异常值,亦或是对于不能自动分箱的变量采取了直观分箱的方式,并没有过多的去探究其可能对于模型的影响。这可以为后续的模型优化奠定方向。

GiveMeSomeCredit——信用评分卡模型相关推荐

  1. 信用评分卡模型(R语言)

    贷款风险预测-信用评分卡模型(R语言) 时间:2018年10月9日 本次的分析数据来自Kaggle数据竞赛平台的"give me some credit"竞赛项目.下载地址为:ht ...

  2. python信用评分卡_基于Python的信用评分卡模型分析(二)

    上一篇文章基于Python的信用评分卡模型分析(一)已经介绍了信用评分卡模型的数据预处理.探索性数据分析.变量分箱和变量选择等.接下来我们将继续讨论信用评分卡的模型实现和分析,信用评分的方法和自动评分 ...

  3. 3分钟搞明白信用评分卡模型模型验证

    2019独角兽企业重金招聘Python工程师标准>>> 信用评分卡模型在国外是一种成熟的预测方法,尤其在信用风险评估以及金融风险控制领域更是得到了比较广泛的使用,其原理是将模型变量W ...

  4. 信用评分卡模型开发中双峰分布原因及解决方案

    信用评分卡模型开发中双峰分布原因及解决方案   文: 郑旻圻 邹钰 刘巧莉 转自:  数信互融 在信用评分卡模型开发过程中,正态性是检验模型信用评分分布是否有效的一个重要指标.正常情况下,标准的正态分 ...

  5. 基于Python的信用评分卡模型-give me some credit数据集,AUC 0.93 KS 0.71

    信用风险计量模型可以包括跟个人信用评级,企业信用评级和国家信用评级.人信用评级有一系列评级模型组成,常见是A卡(申请评分卡).B卡(行为模型).C卡(催收模型)和F卡(反欺诈模型). 今天我们展示的是 ...

  6. 互联网金融信用评分卡模型构建

    互联网金融信用评分卡模型构建 背景介绍 信用风险计量体系包括主体评级模型和债项评级两部分. 主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用"四张卡"来表示,分别是A ...

  7. 信用评分卡模型开发及评估指标

    版权声明:本文为博主原创文章,未经博主允许不得转载. 一.信用风险评级模型的类型 信用风险计量体系包括主体评级模型和债项评级两部分.主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用&qu ...

  8. 信用评分卡模型的理论准备

    目录 0 前言 1 构建评分卡的整个流程图 2 信息值 IV(Information Value)和 证据权重 WOE(Weight of Evidence) 2.1 WOE 定义 2.2 IV 定义 ...

  9. 基于R的信用评分卡模型解析

    信用评分流程 1.数据获取 我使用的信贷数据共有3000条数据,每条数据11个特征. rm(list=ls()) setwd("D:\\case") library(xlsx) d ...

最新文章

  1. Java Web 项目配置 环境搭建 如何安装jdk jre
  2. C#基础知识梳理系列十:异常处理 System.Exception
  3. Spring Boot 实现扫码登录,这种方式太香了!!
  4. sublime java cmd_在sublime text3设置java环境
  5. C++ 11 深度学习(五)类型转换:static_cast dynamic_cast const_cast reinterpret_cast
  6. java当前月份减一个月_Java获取当前时间的上一个月和下一个月,第一天和最后一天,任意时间的第一天和最后一天,任意时间上一个月和下一个月...
  7. java invoke 泛型_如何通过java反射调用含有泛型数组参数的方法
  8. hadoop hdfs 单机配置
  9. 视频动作检测最新发展调研(Action Detection)
  10. python分布式任务调度_Python开源任务调度框架介绍
  11. 关于 javadoc
  12. 抖音做综艺,差点意思
  13. 保存Simulink仿真模型为图片或者PDF的方法
  14. HTML中怎么从图片里取色,图片取色配色法——从电影画面中取色
  15. 如何用ps制作GIF动图
  16. python入门知识以及print的用法
  17. Chapter 2 认识游戏
  18. 推荐几款实用的思维导图工具
  19. python判断纯英文和纯数字
  20. 制造业企业防范勒索病毒攻击的一些建议措施

热门文章

  1. 晋城一中oj 神奇的序列
  2. 【Error】解决:Not creating XLA devices, tf_xla_enable_xla_devices not set
  3. 老师教我们用计算机画画就是彩虹,拼音ang、eg、ing、ong教学课件.ppt
  4. 梯形求积公式 和 复合梯形求积公式 Matlab 实现
  5. 基于PCA的TE过程故障诊断(matlab)
  6. 计算机867怎么学,2018年考研湖南大学867计算机系统考试大纲
  7. Netty应用实战:客户端重连
  8. 2017年5月14日 星期日 --出埃及记 Exodus 26:2
  9. Android-图片预览(自定义ImageView实现图片缩放,多点触控,自由移动)
  10. 马斯克与SEC再次达成和解 发布特斯拉财务等推特需要得到律师批准