(七)申请评分卡模型Python实现(图文+代码实现)
贷前准入环节流程图大致如下
为什么需要建立评分卡?
所有的模型一定是服务于业务的,那么业务上到底出现了什么问题,需要用到评分卡模型去解决呢?我们先从金融机构传统定价模式说起。
我们知道银行将钱借出去是要收取利息的,那么收取多少利息是合理的呢?
利息的本质是租金,银行借钱给客户,客户获得了一定时间内这笔钱的使用权,从而需要支付租金,就好像你租房子需要付房租一个道理。但是借钱和借房子不一样的地方在于,借钱出去有信用风险,借款人有可能到期无法偿还这笔钱,所以利息包括了这两方面的价值:
利息=时间价值+风险价值
其中,时间价值取决于市场供需关系(银行同业拆借利率),风险价值取决于客户的违约率。
那么核心问题就是如何量化风险价值,银行的传统模式是对同一类客户群体根据历史表现计算一个平均的违约率。这种做法会存在什么问题呢?
现在我们假设市场上有A和B两家银行,A采取传统定价(计算的平均违约率3%),B采取风险定价(对不同违约率的客户采取不同的定价),想想会发生什么现象?
逆向选择(劣币驱逐良币):那些违约率低于3%的优质客户会从A银行转去B银行借钱,因为B银行根据这个客户的违约率给了更低的利息。从而A银行的优质客户会流失,导致A银行的平均违约率上升,比如上升到5%,A银行被迫上调贷款利率,这时候低于5%违约率的客户又会从A银行流失…
所以银行需要对贷款客户进行风险评级,并且对不同风险等级的客户采取不同的定价,这样就可以有效避免逆向选择问题,那么为什么由模型而不是人来完成这一任务呢?
稳定:人工判断具有一定的随意性和波动性,没有模型稳定
效率:模型的判断效率远远高于人工判断
成本:模型的边际成本为0,也就是开发好模型后,再多的客户数量也不会增加成本。
评分卡的建立步骤
- 数据获取,包括获取存量客户及潜在客户的数据。存量客户是指已经在证券公司开展相关融资类业务的客户,包括个人客户和机构客户;潜在客户是指未来拟在证券公司开展相关融资类业务的客户,主要包括机构客户,这也是解决证券业样本较少的常用方法,这些潜在机构客户包括上市公司、公开发行债券的发债主体、新三板上市公司、区域股权交易中心挂牌公司、非标融资机构等。
- 数据预处理,主要工作包括数据清洗、缺失值处理、异常值处理,主要是为了将获取的原始数据转化为可用作模型开发的格式化数据。
- 探索性数据分析,该步骤主要是获取样本总体的大概情况,描述样本总体情况的指标主要有直方图、箱形图等。
- 变量选择,该步骤主要是通过统计学的方法,筛选出对违约状态影响最显著的指标。主要有单变量特征选择方法和基于机器学习模型的方法 。
- 模型开发,该步骤主要包括变量分段、变量的WOE(证据权重)变换和逻辑回归估算三部分。
- 模型评估,该步骤主要是评估模型的区分能力、预测能力、稳定性,并形成模型评估报告,得出模型是否可以使用的结论。
- 信用评分,根据逻辑回归的系数和WOE等确定信用评分的方法。将Logistic模型转换为标准评分的形式。
- 建立评分系统,根据信用评分方法,建立自动信用评分系统。
大家有不懂的可以去看查看我的相关博客,申请评分卡(A卡)共有六篇相关博客,代码附上,数据集在我的博客资料可以下载
给出数据集的字典
字段 | 名称 |
member_id | ID |
loan_amnt | 申请额度 |
term | 产品期限 |
int_rate | 利率 |
emp_length | 工作期限 |
home_ownership | 是否有自有住宅 |
annual_inc | 年收入 |
verification_status | 收入核验状态 |
desc | 描述 |
purpose | 贷款目的 |
title | 贷款目的描述 |
zip_code | 联系地址邮政编码 |
addr_state | 联系地址所属州 |
delinq_2yrs | 申贷日期前2年逾期次数 |
inq_last_6mths | 申请日前6个月咨询次数 |
mths_since_last_delinq | 上次逾期距今月份数 |
mths_since_last_record | 上次登记公众记录距今的月份数 |
open_acc | 征信局中记录的信用产品数 |
pub_rec | 公众不良记录数 |
total_acc | 正在使用的信用产品数 |
pub_rec_bankruptcies | 公众破产记录数 |
earliest_cr_line | 第一次借贷时间 |
loan_status | 贷款状态—目标变量 |
# -*- coding:utf-8 -*-
import pandas as pd
import re
import time
import datetime
import numpy as np
import pickle
from dateutil.relativedelta import relativedelta
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import seaborn as sns
from statsmodels.stats.outliers_influence import variance_inflation_factor
from sklearn.linear_model import LogisticRegressionCV
import statsmodels.api as sm
from sklearn.ensemble import RandomForestClassifier
from numpy import log
from sklearn.metrics import roc_auc_score
'''
作者:小象学院
时间:20190224
'''
#当连续变量的初始取值集合太多时(>100),我们先对其进行初步划分
def SplitData(df, col, numOfSplit, special_attribute=[]):
'''
:param df: 按照col排序后的数据集
:param col: 待分箱的变量
:param numOfSplit: 切分的组别数
:param special_attribute: 在切分数据集的时候,某些特殊值需要排除在外
:return: 在原数据集上增加一列,把原始细粒度的col重新划分成粗粒度的值,便于分箱中的合并处理
'''
df2 = df.copy()
if special_attribute != []:
df2 = df.loc[~df[col].isin(special_attribute)]
N = df2.shape[0]
n = N//numOfSplit #每组样本数
splitPointIndex = [i*n for i in range(1,numOfSplit)] #分割点的下标
rawValues = sorted(list(df2[col])) #对取值进行排序
splitPoint = [rawValues[i] for i in splitPointIndex] #分割点的取值
splitPoint = sorted(list(set(splitPoint)))
return splitPoint
def Chi2(df, total_col, bad_col, overallRate):
'''
:param df: 包含全部样本总计与坏样本总计的数据框
:param total_col: 全部样本的个数
:param bad_col: 坏样本的个数
:param overallRate: 全体样本的坏样本占比
:return: 卡方值
'''
df2 = df.copy()
# 期望坏样本个数=全部样本个数*平均坏样本占比
df2['expected'] = df[total_col].apply(lambda x: x*overallRate)
combined = zip(df2['expected'], df2[bad_col])
chi = [(i[0]-i[1])**2/i[0] for i in combined]
ch