信用卡评分模型(数据获取+数据预处理+探索分析+变量选择+模型开发+模型评估+信用评分+建立评分系统)

article/2025/8/3 13:35:18

最近两次遇到关于信用卡评分的题目,遂了解一波。

Reference: 

基于python的信用卡评分模型(超详细!!!)

https://www.jianshu.com/p/f931a4df202c

https://blog.csdn.net/zs15321583801/article/details/81234446

https://blog.csdn.net/han_xiaoyang/article/details/52788775

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

一、项目流程

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

1. 数据获取:

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

这里的变量有11个,第一个是label,后面10个是自变量,经过后面的IV值筛选后,删除了4,5,6,8,10这5个特征;剩下的有效特征是1,2,3,7,9。

2. 数据预处理:

针对缺失值用RF和直接剔除法进行处理,对于异常值主要根据实际情况和箱型图的数据分布对异常值进行删除。

代码包括缺失值和异常值的处理:

# Missvalue.py
# 数据预处理1:  随机森林处理缺失值。
import pandas as pd
import matplotlib.pyplot as plt #导入图像库
from sklearn.ensemble import RandomForestRegressor# 用随机森林对缺失值预测填充函数
def set_missing(df):# 把已有的数值型特征取出来process_df = df.ix[:,[5,0,1,2,3,4,6,7,8,9]]# 分成已知该特征和未知该特征两部分known = process_df[process_df.MonthlyIncome.notnull()].as_matrix()unknown = process_df[process_df.MonthlyIncome.isnull()].as_matrix()# X为特征属性值X = known[:, 1:]# y为结果标签值y = known[:, 0]# 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)print(predicted)# 用得到的预测结果填补原缺失数据df.loc[(df.MonthlyIncome.isnull()), 'MonthlyIncome'] = predictedreturn dfif __name__ == '__main__':# 载入数据data = pd.read_csv('cs-training.csv')# 数据集确实和分布情况data.describe().to_csv('DataDescribe.csv')  # 了解数据集的分布情况data=set_missing(data)  # 用随机森林填补比较多的缺失值data=data.dropna()  # 删除比较少的缺失值data = data.drop_duplicates()  # 删除重复项data.to_csv('MissingData.csv', index=False)data.describe().to_csv('MissingDataDescribe.csv')"""#异常值处理#年龄等于0的异常值进行剔除data=data[data['age']>0]# 箱形图data379=data[['NumberOfTime30-59DaysPastDueNotWorse','NumberOfTimes90DaysLate','NumberOfTime60-89DaysPastDueNotWorse']]data379.boxplot()data = data[data['NumberOfTime30-59DaysPastDueNotWorse'] < 90]data379 = data[['NumberOfTime30-59DaysPastDueNotWorse', 'NumberOfTimes90DaysLate', 'NumberOfTime60-89DaysPastDueNotWorse']]#data379.boxplot()plt.show()#data.to_csv('PretreatmentData.csv')"""
# Outlier.py
# 数据预处理2   异常值处理
import pandas as pd
import matplotlib.pyplot as plt #导入图像库
from sklearn.cross_validation import train_test_splitdef outlier_processing(df,col):s=df[col]oneQuoter=s.quantile(0.25)threeQuote=s.quantile(0.75)irq=threeQuote-oneQuotermin=oneQuoter-1.5*irqmax=threeQuote+1.5*irqdf=df[df[col]<=max]df=df[df[col]>=min]return dfif __name__ == '__main__':data = pd.read_csv('MissingData.csv')# 年龄等于0的异常值进行剔除data = data[data['age'] > 0]data = data[data['NumberOfTime30-59DaysPastDueNotWorse'] < 90] # 剔除异常值data['SeriousDlqin2yrs']=1-data['SeriousDlqin2yrs']Y = data['SeriousDlqin2yrs']X = data.ix[:, 1:]X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3, random_state=0)# print(Y_train)train = pd.concat([Y_train, X_train], axis=1)test = pd.concat([Y_test, X_test], axis=1)clasTest = test.groupby('SeriousDlqin2yrs')['SeriousDlqin2yrs'].count()train.to_csv('TrainData.csv',index=False)test.to_csv('TestData.csv',index=False)print(train.shape)print(test.shape)

3.  探索性分析:

在建立模型之前,我们一般会对现有的数据进行 探索性数据分析(Exploratory Data Analysis) 。 EDA是指对已有的数据(特别是调查或观察得来的原始数据)在尽量少的先验假定下进行探索。常用的探索性数据分析方法有:直方图、散点图和箱线图等。
客户年龄分布如图4-1所示,可以看到年龄变量大致呈正态分布,符合统计分析的假设。

客户年收入分布如图4-2所示,月收入也大致呈正态分布,符合统计分析的需要。

4.  变量选择:

特征变量选择(排序)对于数据分析、机器学习从业者来说非常重要。好的特征选择能够提升模型的性能,更能帮助我们理解数据的特点、底层结构,这对进一步改善模型、算法都有着重要作用。至于Python的变量选择代码实现可以参考结合Scikit-learn介绍几种常用的特征选择方法。
在本文中,我们采用信用评分模型的变量选择方法,通过WOE分析方法,即是通过比较指标分箱和对应分箱的违约概率来确定指标是否符合经济意义。首先我们对变量进行离散化(分箱)处理。

  1 分箱处理

变量分箱(binning)是对连续变量离散化(discretization)的一种称呼。信用评分卡开发中一般有常用的等距分段、等深分段、最优分段。其中等距分段(Equval length intervals)是指分段的区间是一致的,比如年龄以十年作为一个分段;等深分段(Equal frequency intervals)是先确定分段数量,然后令每个分段中数据数量大致相等;最优分段(Optimal Binning)又叫监督离散化(supervised discretizaion),使用递归划分(Recursive Partitioning)将连续变量分为分段,背后是一种基于条件推断查找较佳分组的算法。
我们首先选择对连续变量进行最优分段,在连续变量的分布不满足最优分段的要求时,再考虑对连续变量进行等距分段。最优分箱的代码如下:

# part 
# 定义自动分箱函数
def mono_bin(Y, X, n = 20):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']/(1-d3['rate']))/(good/bad))d4 = (d3.sort_index(by = 'min')).reset_index(drop=True)print("=" * 60)print(d4)return d4

针对我们将使用最优分段对于数据集中的RevolvingUtilizationOfUnsecuredLines、age、DebtRatio和MonthlyIncome进行分类。

      

 

针对不能最优分箱的变量,分箱如下:

# part  连续变量离散化cutx3 = [ninf, 0, 1, 3, 5, pinf]cutx6 = [ninf, 1, 2, 3, 5, pinf]cutx7 = [ninf, 0, 1, 3, 5, pinf]cutx8 = [ninf, 0,1,2, 3, pinf]cutx9 = [ninf, 0, 1, 3, pinf]cutx10 = [ninf, 0, 1, 2, 3, 5, pinf]

   2 WOE

WoE分析, 是对指标分箱、计算各个档位的WoE值并观察WoE值随指标变化的趋势。其中WoE的数学定义是:
woe=ln(goodattribute/badattribute)
在进行分析时,我们需要对各指标从小到大排列,并计算出相应分档的WoE值。其中正向指标越大,WoE值越小;反向指标越大,WoE值越大。正向指标的WoE值负斜率越大,反响指标的正斜率越大,则说明指标区分能力好。WoE值趋近于直线,则意味指标判断能力较弱。若正向指标和WoE正相关趋势、反向指标同WoE出现负相关趋势,则说明此指标不符合经济意义,则应当予以去除。
woe函数实现在上一节的mono_bin()函数里面已经包含,这里不再重复。

    3 相关性分析和IV筛选

接下来,我们会用经过清洗后的数据看一下变量间的相关性。注意,这里的相关性分析只是初步的检查,进一步检查模型的VI(证据权重)作为变量筛选的依据。
相关性图我们通过Python里面的seaborn包,调用heatmap()绘图函数进行绘制,实现代码如下:

# part
corr = data.corr()#计算各变量的相关性系数xticks = ['x0','x1','x2','x3','x4','x5','x6','x7','x8','x9','x10']#x轴标签yticks = list(corr.index)#y轴标签fig = plt.figure()ax1 = fig.add_subplot(1, 1, 1)sns.heatmap(corr, annot=True, cmap='rainbow', ax=ax1, annot_kws={'size': 9, 'weight': 'bold', 'color': 'blue'})#绘制相关性系数热力图ax1.set_xticklabels(xticks, rotation=0, fontsize=10)ax1.set_yticklabels(yticks, rotation=0, fontsize=10)plt.show()

生成的图形如图5-5所示:

 

由上图可以看出,各变量之间的相关性是非常小的。NumberOfOpenCreditLinesAndLoans和NumberRealEstateLoansOrLines的相关性系数为0.43。
接下来,我进一步计算每个变量的Infomation Value(IV)。IV指标是一般用来确定自变量的预测能力。 其公式为:
IV=sum((goodattribute-badattribute)*ln(goodattribute/badattribute))
通过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
IV的实现放在mono_bin()函数里面,代码实现如下:

# part
# 定义自动分箱函数
def mono_bin(Y, X, n = 20):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']/(1-d3['rate']))/(good/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)print("=" * 60)print(d4)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'))woe=list(d4['woe'].round(3))return d4,iv,cut,woe

IV代码如下:

# part
ivlist=[ivx1,ivx2,ivx3,ivx4,ivx5,ivx6,ivx7,ivx8,ivx9,ivx10]#各变量IVindex=['x1','x2','x3','x4','x5','x6','x7','x8','x9','x10']#x轴的标签fig1 = plt.figure(1)ax1 = fig1.add_subplot(1, 1, 1)x = np.arange(len(index))+1ax1.bar(x, ivlist, width=0.4)#生成柱状图ax1.set_xticks(x)ax1.set_xticklabels(index, rotation=0, fontsize=12)ax1.set_ylabel('IV(Information Value)', fontsize=14)#在柱状图上添加数字标签for a, b in zip(x, ivlist):plt.text(a, b + 0.01, '%.4f' % b, ha='center', va='bottom', fontsize=10)plt.show()

可以看出,DebtRatio、MonthlyIncome、NumberOfOpenCreditLinesAndLoans、NumberRealEstateLoansOrLines和NumberOfDependents变量的IV值明显较低,所以予以删除。

5.  模型分析:

证据权重(Weight of Evidence,WOE)转换可以将Logistic回归模型转变为标准评分卡格式。引入WOE转换的目的并不是为了提高模型质量,只是一些变量不应该被纳入模型,这或者是因为它们不能增加模型值,或者是因为与其模型相关系数有关的误差较大,其实建立标准信用评分卡也可以不采用WOE转换。这种情况下,Logistic回归模型需要处理更大数量的自变量。尽管这样会增加建模程序的复杂性,但最终得到的评分卡都是一样的。
在建立模型之前,我们需要将筛选后的变量转换为WoE值,便于信用评分。

  1 WOE转换

我们已经能获取了每个变量的分箱数据和woe数据,只需要根据各变量数据进行替换,实现代码如下:

#替换成woe函数
def replace_woe(series,cut,woe):list=[]i=0while i<len(series):value=series[i]j=len(cut)-2m=len(cut)-2while j>=0:if value>=cut[j]:j=-1else:j -=1m -= 1list.append(woe[m])i += 1return list

我们将每个变量都进行替换,并将其保存到WoeData.csv文件中:【训练集和测试集】

# 训练集的替换成woedata['RevolvingUtilizationOfUnsecuredLines'] = Series(replace_woe(data['RevolvingUtilizationOfUnsecuredLines'], cutx1, woex1))data['age'] = Series(replace_woe(data['age'], cutx2, woex2))data['NumberOfTime30-59DaysPastDueNotWorse'] = Series(replace_woe(data['NumberOfTime30-59DaysPastDueNotWorse'], cutx3, woex3))data['DebtRatio'] = Series(replace_woe(data['DebtRatio'], cutx4, woex4))data['MonthlyIncome'] = Series(replace_woe(data['MonthlyIncome'], cutx5, woex5))data['NumberOfOpenCreditLinesAndLoans'] = Series(replace_woe(data['NumberOfOpenCreditLinesAndLoans'], cutx6, woex6))data['NumberOfTimes90DaysLate'] = Series(replace_woe(data['NumberOfTimes90DaysLate'], cutx7, woex7))data['NumberRealEstateLoansOrLines'] = Series(replace_woe(data['NumberRealEstateLoansOrLines'], cutx8, woex8))data['NumberOfTime60-89DaysPastDueNotWorse'] = Series(replace_woe(data['NumberOfTime60-89DaysPastDueNotWorse'], cutx9, woex9))data['NumberOfDependents'] = Series(replace_woe(data['NumberOfDependents'], cutx10, woex10))data.to_csv('WoeData.csv', index=False)test= pd.read_csv('TestData.csv')# 测试集的替换成woetest['RevolvingUtilizationOfUnsecuredLines'] = Series(replace_woe(test['RevolvingUtilizationOfUnsecuredLines'], cutx1, woex1))test['age'] = Series(replace_woe(test['age'], cutx2, woex2))test['NumberOfTime30-59DaysPastDueNotWorse'] = Series(replace_woe(test['NumberOfTime30-59DaysPastDueNotWorse'], cutx3, woex3))test['DebtRatio'] = Series(replace_woe(test['DebtRatio'], cutx4, woex4))test['MonthlyIncome'] = Series(replace_woe(test['MonthlyIncome'], cutx5, woex5))test['NumberOfOpenCreditLinesAndLoans'] = Series(replace_woe(test['NumberOfOpenCreditLinesAndLoans'], cutx6, woex6))test['NumberOfTimes90DaysLate'] = Series(replace_woe(test['NumberOfTimes90DaysLate'], cutx7, woex7))test['NumberRealEstateLoansOrLines'] = Series(replace_woe(test['NumberRealEstateLoansOrLines'], cutx8, woex8))test['NumberOfTime60-89DaysPastDueNotWorse'] = Series(replace_woe(test['NumberOfTime60-89DaysPastDueNotWorse'], cutx9, woex9))test['NumberOfDependents'] = Series(replace_woe(test['NumberOfDependents'], cutx10, woex10))test.to_csv('TestWoeData.csv', index=False)

变量选择和模型分析的数据转换的代码整合如下:

# 3  探索分析 + 变量选择 + 模型开发(变量离散化 + 变量的woe变换)
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
import scipy.stats.stats as stats
import matplotlib.pyplot as plt
import statsmodels.api as sm
import math# 变量选择部分的代码
# 定义自动分箱函数
def mono_bin(Y, X, n = 20):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']/(1-d3['rate']))/(good/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'))print("=" * 60)print(d4)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'))woe=list(d4['woe'].round(3))return d4,iv,cut,woe# 自定义分箱函数
def self_bin(Y,X,cat):good=Y.sum()bad=Y.count()-goodd1=pd.DataFrame({'X':X,'Y':Y,'Bucket':pd.cut(X,cat)})d2=d1.groupby('Bucket', as_index = True)d3 = 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'] / (1 - d3['rate'])) / (good / 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'))print("=" * 60)print(d4)woe = list(d4['woe'].round(3))return d4, iv,woe# 模型分析开始的代码
# 用woe代替
def replace_woe(series,cut,woe):list=[]i=0while i<len(series):value=series[i]j=len(cut)-2m=len(cut)-2while j>=0:if value>=cut[j]:j=-1else:j -=1m -= 1list.append(woe[m])i += 1return list# 计算分数函数
def get_score(coe,woe,factor):scores=[]for w in woe:score=round(coe*w*factor,0)scores.append(score)return scores# 根据变量计算分数
def compute_score(series,cut,score):list = []i = 0while i < len(series):value = series[i]j = len(cut) - 2m = len(cut) - 2while j >= 0:if value >= cut[j]:j = -1else:j -= 1m -= 1list.append(score[m])i += 1return listif __name__ == '__main__':data = pd.read_csv('TrainData.csv')pinf = float('inf')  # 正无穷大ninf = float('-inf')  # 负无穷大dfx1, ivx1,cutx1,woex1=mono_bin(data.SeriousDlqin2yrs,data.RevolvingUtilizationOfUnsecuredLines,n=10)dfx2, ivx2,cutx2,woex2=mono_bin(data.SeriousDlqin2yrs, data.age, n=10)dfx4, ivx4,cutx4,woex4 =mono_bin(data.SeriousDlqin2yrs, data.DebtRatio, n=20)dfx5, ivx5,cutx5,woex5 =mono_bin(data.SeriousDlqin2yrs, data.MonthlyIncome, n=10)# 连续变量离散化cutx3 = [ninf, 0, 1, 3, 5, pinf]cutx6 = [ninf, 1, 2, 3, 5, pinf]cutx7 = [ninf, 0, 1, 3, 5, pinf]cutx8 = [ninf, 0,1,2, 3, pinf]cutx9 = [ninf, 0, 1, 3, pinf]cutx10 = [ninf, 0, 1, 2, 3, 5, pinf]dfx3, ivx3,woex3 = self_bin(data.SeriousDlqin2yrs, data['NumberOfTime30-59DaysPastDueNotWorse'], cutx3)dfx6, ivx6 ,woex6= self_bin(data.SeriousDlqin2yrs, data['NumberOfOpenCreditLinesAndLoans'], cutx6)dfx7, ivx7,woex7 = self_bin(data.SeriousDlqin2yrs, data['NumberOfTimes90DaysLate'], cutx7)dfx8, ivx8,woex8 = self_bin(data.SeriousDlqin2yrs, data['NumberRealEstateLoansOrLines'], cutx8)dfx9, ivx9,woex9 = self_bin(data.SeriousDlqin2yrs, data['NumberOfTime60-89DaysPastDueNotWorse'], cutx9)dfx10, ivx10,woex10 = self_bin(data.SeriousDlqin2yrs, data['NumberOfDependents'], cutx10)ivlist=[ivx1,ivx2,ivx3,ivx4,ivx5,ivx6,ivx7,ivx8,ivx9,ivx10]index=['x1','x2','x3','x4','x5','x6','x7','x8','x9','x10']fig1 = plt.figure(1)ax1 = fig1.add_subplot(1, 1, 1)x = np.arange(len(index))+1ax1.bar(x, ivlist, width=0.4)ax1.set_xticks(x)ax1.set_xticklabels(index, rotation=0, fontsize=12)ax1.set_ylabel('IV(Information Value)', fontsize=14)for a, b in zip(x, ivlist):plt.text(a, b + 0.01, '%.4f' % b, ha='center', va='bottom', fontsize=10)# 训练集的替换成woedata['RevolvingUtilizationOfUnsecuredLines'] = Series(replace_woe(data['RevolvingUtilizationOfUnsecuredLines'], cutx1, woex1))data['age'] = Series(replace_woe(data['age'], cutx2, woex2))data['NumberOfTime30-59DaysPastDueNotWorse'] = Series(replace_woe(data['NumberOfTime30-59DaysPastDueNotWorse'], cutx3, woex3))data['DebtRatio'] = Series(replace_woe(data['DebtRatio'], cutx4, woex4))data['MonthlyIncome'] = Series(replace_woe(data['MonthlyIncome'], cutx5, woex5))data['NumberOfOpenCreditLinesAndLoans'] = Series(replace_woe(data['NumberOfOpenCreditLinesAndLoans'], cutx6, woex6))data['NumberOfTimes90DaysLate'] = Series(replace_woe(data['NumberOfTimes90DaysLate'], cutx7, woex7))data['NumberRealEstateLoansOrLines'] = Series(replace_woe(data['NumberRealEstateLoansOrLines'], cutx8, woex8))data['NumberOfTime60-89DaysPastDueNotWorse'] = Series(replace_woe(data['NumberOfTime60-89DaysPastDueNotWorse'], cutx9, woex9))data['NumberOfDependents'] = Series(replace_woe(data['NumberOfDependents'], cutx10, woex10))data.to_csv('WoeData.csv', index=False)test= pd.read_csv('TestData.csv')# 测试集的替换成woetest['RevolvingUtilizationOfUnsecuredLines'] = Series(replace_woe(test['RevolvingUtilizationOfUnsecuredLines'], cutx1, woex1))test['age'] = Series(replace_woe(test['age'], cutx2, woex2))test['NumberOfTime30-59DaysPastDueNotWorse'] = Series(replace_woe(test['NumberOfTime30-59DaysPastDueNotWorse'], cutx3, woex3))test['DebtRatio'] = Series(replace_woe(test['DebtRatio'], cutx4, woex4))test['MonthlyIncome'] = Series(replace_woe(test['MonthlyIncome'], cutx5, woex5))test['NumberOfOpenCreditLinesAndLoans'] = Series(replace_woe(test['NumberOfOpenCreditLinesAndLoans'], cutx6, woex6))test['NumberOfTimes90DaysLate'] = Series(replace_woe(test['NumberOfTimes90DaysLate'], cutx7, woex7))test['NumberRealEstateLoansOrLines'] = Series(replace_woe(test['NumberRealEstateLoansOrLines'], cutx8, woex8))test['NumberOfTime60-89DaysPastDueNotWorse'] = Series(replace_woe(test['NumberOfTime60-89DaysPastDueNotWorse'], cutx9, woex9))test['NumberOfDependents'] = Series(replace_woe(test['NumberOfDependents'], cutx10, woex10))test.to_csv('TestWoeData.csv', index=False)# 计算分数# coe为逻辑回归模型的系数coe=[9.738849,0.638002,0.505995,1.032246,1.790041,1.131956]# 我们取600分为基础分值,PDO为20(每高20分好坏比翻一倍),好坏比取20。p = 20 / math.log(2)q = 600 - 20 * math.log(20) / math.log(2)baseScore = round(q + p * coe[0], 0)# 各项部分分数x1 = get_score(coe[1], woex1, p)x2 = get_score(coe[2], woex2, p)x3 = get_score(coe[3], woex3, p)x7 = get_score(coe[4], woex7, p)x9 = get_score(coe[5], woex9, p)print(x1,x2, x3, x7, x9)test1 = pd.read_csv('TestData.csv')test1['BaseScore']=Series(np.zeros(len(test1)))+baseScoretest1['x1'] = Series(compute_score(test1['RevolvingUtilizationOfUnsecuredLines'], cutx1, x1))test1['x2'] = Series(compute_score(test1['age'], cutx2, x2))test1['x3'] = Series(compute_score(test1['NumberOfTime30-59DaysPastDueNotWorse'], cutx3, x3))test1['x7'] = Series(compute_score(test1['NumberOfTimes90DaysLate'], cutx7, x7))test1['x9'] = Series(compute_score(test1['NumberOfTime60-89DaysPastDueNotWorse'], cutx9, x9))test1['Score'] = test1['x1'] + test1['x2'] + test1['x3'] + test1['x7'] +test1['x9']  + baseScoretest1.to_csv('ScoreData.csv', index=False)plt.show()

   2 Logisic模型建立

我们直接调用statsmodels包来实现逻辑回归:

    导入数据data = pd.read_csv('WoeData.csv')#应变量Y=data['SeriousDlqin2yrs']#自变量,剔除对因变量影响不明显的变量X=data.drop(['SeriousDlqin2yrs','DebtRatio','MonthlyIncome', 'NumberOfOpenCreditLinesAndLoans','NumberRealEstateLoansOrLines','NumberOfDependents'],axis=1)X1=sm.add_constant(X)logit=sm.Logit(Y,X1)result=logit.fit()print(result.summary())

 

通过图6-1可知,逻辑回归各变量都已通过显著性检验,满足要求。

   3 模型检验

到这里,我们的建模部分基本结束了。我们需要验证一下模型的预测能力如何。我们使用在建模开始阶段预留的test数据进行检验。通过ROC曲线和AUC来评估模型的拟合能力。
在Python中,可以利用sklearn.metrics,它能方便比较两个分类器,自动计算ROC和AUC。
实现代码:

    #因变量Y_test = test['SeriousDlqin2yrs']#自变量,剔除对因变量影响不明显的变量,与模型变量对应X_test = test.drop(['SeriousDlqin2yrs', 'DebtRatio', 'MonthlyIncome', 'NumberOfOpenCreditLinesAndLoans','NumberRealEstateLoansOrLines', 'NumberOfDependents'], axis=1)X3 = sm.add_constant(X_test)resu = result.predict(X3)#进行预测fpr, tpr, threshold = roc_curve(Y_test, resu)rocauc = auc(fpr, tpr)#计算AUCplt.plot(fpr, tpr, 'b', label='AUC = %0.2f' % rocauc)#生成ROC曲线plt.legend(loc='lower right')plt.plot([0, 1], [0, 1], 'r--')plt.xlim([0, 1])plt.ylim([0, 1])plt.ylabel('真正率')plt.xlabel('假正率')plt.show()

结果图,AUC值为0.85,说明该模型的预测效果还是不错的,正确率较高。

训练逻辑回归模型和在测试集上进行验证的代码:

# 4 根据处理后的数据集进行逻辑回归模型的训练,然后基于训练好的模型进行测试效果。
import pandas as pd
import matplotlib.pyplot as plt  # 导入图像库
import matplotlib
import seaborn as sns
import statsmodels.api as sm
from sklearn.metrics import roc_curve, aucif __name__ == '__main__':# 先根据替换成woe后的数据集WoeData.csv进行逻辑回归的训练matplotlib.rcParams['axes.unicode_minus'] = Falsedata = pd.read_csv('WoeData.csv')Y=data['SeriousDlqin2yrs']X=data.drop(['SeriousDlqin2yrs','DebtRatio','MonthlyIncome', 'NumberOfOpenCreditLinesAndLoans','NumberRealEstateLoansOrLines','NumberOfDependents'],axis=1)X1=sm.add_constant(X)logit=sm.Logit(Y,X1)result=logit.fit()print(result.params)# 然后测试集TestWoeData.csv上进行上述训练好的逻辑回归模型的测试效果test = pd.read_csv('TestWoeData.csv')Y_test = test['SeriousDlqin2yrs']X_test = test.drop(['SeriousDlqin2yrs', 'DebtRatio', 'MonthlyIncome', 'NumberOfOpenCreditLinesAndLoans','NumberRealEstateLoansOrLines', 'NumberOfDependents'], axis=1)X3 = sm.add_constant(X_test)resu = result.predict(X3)fpr, tpr, threshold = roc_curve(Y_test, resu)rocauc = 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('真正率')plt.xlabel('假正率')plt.show()

6.建立统一的信用卡评分系统:

将逻辑回归模型转换成标准评分卡的形式。


http://chatgpt.dhexx.cn/article/n0LYxY0W.shtml

相关文章

信用评分模型详解(下)之 信用评分系统搭建

信用评分问题中一般使用逻辑回归作为主要的模型。过程主要包括变量分箱、变量的WOE&#xff08;证据权重&#xff09;变换和变量选择&#xff08;IV值&#xff09;、逻辑回归估算。 一个完整的评分卡流程主要包括以下几个步骤&#xff1a; 数据准备数据探索性分析数据预处理&…

【信用评分预测模型(一)】信用评分预测模型简介

文章目录 前言一、数据准备1.文章所使用的数据都是基于下面这两个网站&#xff1a;2.观察期和表现期 二、数据处理1.缺失值和异常值2.筛选数据 三、相关性检验四、特征工程1.特征变量 五、模型构建1.随机森林模型2.决策树模型3.AHP分析 总结 前言 在传统的信用风险预测的模型中…

大数据分析案例-基于多元线性回归算法构建用户信用评分模型

🤵‍♂️ 个人主页:@艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ 喜欢大数据分析项目的小伙伴,希望可以多多支持该系列的其他文章 大数据分析案例合集…

【综合案例】信用评分模型开发

目录 一、案例背景二、前置知识2.1 数据分箱2.2 属性选择2.3 回归方程 三、数据处理3.1 数据清洗3.2 因素分析 四、模型训练五、模型预测六、结语后记 一、案例背景 在上一篇文章网络贷款违约预测案例中&#xff0c;我们在分析属性关系时发现&#xff1a;FICO信用评分与分类标…

信用评分模型(R语言)

信用评分 2016年1月10日 本文详细的介绍了信用评分卡的开发流程,开发语言为R语言,python版本请见:一行代码搞定信用评分模型(python) python版实例和数据请见我的github:https://github.com/chengsong990020186/CreditScoreModel,如觉得实用就点下star,欢迎大家一起…

信用评分卡模型分析(基于Python)--理论部分

信用风险计量体系包括主体评级模型和债项评级两部分。主体评级和债项评级均有一系列评级模型组成&#xff0c;其中主体评级模型可用“四张卡”来表示&#xff0c;分别是A卡、B卡、C卡和F卡&#xff1b;债项评级模型通常按照主体的融资用途&#xff0c;分为企业融资模型、现金流…

企业信用评分卡模型实战(python,附代码)

企业信用评级在全球金融领域有广泛应用。它涉及到公司管理&#xff0c;企业债发行&#xff0c;企业融资&#xff0c;企业上市&#xff0c;企业并购&#xff0c;个人炒股和购买公司债券等多个场景。企业债发行 企业主体信用评级越高&#xff0c;意味着该企业抗风险能力、盈利能力…

银行信用卡评分模型(一)| python

文章目录 背景介绍题目分析一、申请者评级模型二、欺诈评级模型三、行为评级模型四、催收评级模型总结 背景介绍 信用风险和评分卡模型的基本概念&#xff1a;信用风险指的是交易对手未能履行约定合同中的义务造成经济损失的风险&#xff0c;即受信人不能履行还本付息的责任而使…

【单片机毕业设计】【mcuclub-cl-010】基于单片机的灭火小车的设计

最近设计了一个项目基于单片机的灭火小车系统&#xff0c;与大家分享一下&#xff1a; 一、基本介绍 项目名&#xff1a;灭火小车 项目编号&#xff1a;mcuclub-cl-010 单片机类型&#xff1a;STC89C52、STM32F103C8T6 具体功能&#xff1a; 1、通过一对红外寻迹管进行寻迹&a…

详细介绍如何从零开始制作51单片机控制的智能小车(四)———通过蓝牙模块实现数据传输以及通过手机蓝牙实现对小车运动状态的控制

我会通过本系列文章&#xff0c;详细介绍如何从零开始用51单片机去实现智能小车的控制&#xff0c;在本系列的上一篇文章中介绍了如何让小车实现自动避障&#xff0c;本文作为本系列的第四篇文章&#xff0c;主要介绍蓝牙模块的使用&#xff0c;如何通过蓝牙进行数据传输&#…

课设-基于51单片机的智能小车(循迹+避障+APP控制)

51单片机课设—智能小车 一、课设任务二、硬件设计及相关传感器模块1、车模2、51单片机3、红外循迹模块4、电机及驱动5、蓝牙模块6、硬件搭建 三、程序代码1、main.c文件2、motor.c文件 四、我的大学--“造车”史 注&#xff1a;本设计资源可在微信公众号&#xff1a;“Kevin的…

基于51单片机智能小车循迹功能的实现

基于51单片机智能小车循迹功能的实现 目录 基于51单片机智能小车循迹功能的实现一、思路&#xff08;仅供参考&#xff09;二、部分硬件模块介绍1、L298n电机驱动模块2、两路红外循迹模块 三、实现1、代码2、运行视频(...注意&#xff1a;该视频有声音&#xff01;&#xff01;…

基于51单片机的循迹小车

在大学期间&#xff0c;单片机是部分学生的专业课之一&#xff0c;在学完课本上枯燥的知识后&#xff0c;需要实践加强巩固。简单且综合性尚可的循迹小车必然是实践的首要选择。下面将主要从机械、电控、代码三个部分对以51单片机为主控芯片的循迹小车进行简述 目录 1.总体介…

51单片机智能蓝牙小车

作为大一电子小趴菜&#xff0c;在和队友学习制作蓝牙小车过程中遇到了许多困难。本文既为大家避坑&#xff0c;也作为团队总结。 这篇文章分为两部分 1&#xff0c;组装篇 2&#xff0c;代码篇 思维导图&#xff1a; 组装篇 先准备好一块电源拓展板&#xff0c;用于为电机驱…

浅谈51单片机简易循迹小车制作方案

目录 省流助手&#xff08;方案清单&#xff09; 前言 一、小车模型 二、电机 三、电源 1、电池&#xff1a; 2、PCB电源选择&#xff1a; 3、MCU供电方案&#xff1a; 4、MCU和电机的供电方案&#xff1a; 四、主控MCU最小系统 五、电机驱动 1、让电机动起来&#xff1a;&…

基于51单片机制作的蓝牙小车

基于51单片机制作的蓝牙小车 这是本人&#xff08;小菜鸡一枚&#xff09;于大一寒假所做的小玩意&#xff0c;以展现我这几月所学&#xff0c;这也是我发的第一篇博客&#xff0c;准确来说这可能是我搞51单片机的巅峰所做了&#xff0c;因为部长说以后要学stm32开发板了&…

【项目实战】基于STM32单片机的智能小车设计(有代码)

【1】背景意义 近些年随着国民生活水平的提升&#xff0c;以小车为载体的轮式机器人进入了我们的生活&#xff0c;尤其是在一些布线复杂困难的安全生活区和需要监控的施工作业场合都必须依赖轮式机器人的视频监控技术。因此&#xff0c;基于嵌入式技术的无线通信视频监控轮式机…

51单片机小车(附源码)

算是做的第一个比较完整的序幕项目&#xff0c;用到的模块不多&#xff0c;更多时间花在调试硬件封方面&#xff0c;比如电池带不动电机导致51单片机重启。 项目框图 项目流程图 通过蓝牙发送指令控制小车的前进、后退、转向、调速等功能。 在小车运动过程中黄灯亮起&#xff…

单片机智能小车

单片机智能小车&#xff1a; 基于单片机控制的自主寻迹电动小车的设计&#xff1a; 摘 要:针对小车在行驶过程中的寻迹要求&#xff0c;设计了以AT89C51单片机为核心的控制电路&#xff0c;采用模块化的设计方案&#xff0c;运用色标传感器、金属探测传感器、超声波传感器、霍尔…

51单片机寻迹小车

# 基于51单片机的循迹小车(详细讲解源码) 作为一名学生&#xff0c;这里我就简单谈谈我们小组做这个循迹小车的一个过程。主要是实现小车寻黑线行驶的这样一个功能&#xff0c;速度可以调节(定时器中断模拟PWM波实现) 一丶硬件部分 小车的主体框架是一个三轮的外在形态 电源…