基于python的信用评分模型

随着金融知识的普及,越来越多的人开始改变了自己的消费观念,以前是“先储蓄后消费”,现在是“先消费后还钱”,不得不说,这种观念的改变使得人们的物质生活开始变得更丰富,但与此同时也带来了一些问题:部分人开始还不起款了。在贷款供应端就涉及到了信用评分的问题。

1.背景

Give me some credit是Kaggle上关于信用评分的项目,通过改进信用评分技术,预测未来两年借款人会遇到财务困境的可能性。银行在市场经济中发挥关键作用。 他们决定谁可以获得融资,以及以何种条件进行投资决策。 为了市场和社会的运作,个人和公司需要获得信贷。信用评分算法可以猜测违约概率,这是银行用于确定是否应授予贷款的方法。目标是建立一个借款人可以用来帮助做出最佳财务决策的模型。

#导入相关包
import pandas as pd 
import numpy as np
import matplotlib.pyplot as plt
data = pd.read_csv("C:\\Users\\Administrator\\Desktop\\Give me some credit data\\cs-training.csv",engine = "python")
data.describe()

 

2.数据获取

 

Give Me Some Credit​www.kaggle.com

变量解释:

3.数据预处理

在对数据进行处理之前,我们首先对整体情况进行概览。

data = pd.read_csv("C:\\Users\\Administrator\\Desktop\\give me some credit\\cs-training.csv",engine = "python")
data.describe()

从上图可知,变量MonthlyIncome和NumberOfDependents存在缺失,变量MonthlyIncome共有缺失值29731个,NumberOfDependents有3924个缺失值。

 

3.1缺失值处理

这种情况在现实问题中非常普遍,这会导致一些不能处理缺失值的分析方法无法应用,因此,在信用风险评级模型开发的第一步我们就要进行缺失值处理。缺失值处理的方法,包括如下几种。
(1) 直接删除含有缺失值的样本。
(2) 根据样本之间的相似性填补缺失值。
(3) 根据变量之间的相关关系填补缺失值。
变量MonthlyIncome缺失率比较大,所以我们根据变量之间的相关关系填补缺失值,我们采用随机森林法:

# 用随机森林对缺失值进行预测
from sklearn.ensemble import RandomForestRegressor
  
# 预测填充函数
def rf_filling(df):
    # 处理数集
    process_miss = df.iloc[:,[5,0,1,2,3,4,6,7,8,9]]
    #分成已知特征与未知特征
    known = process_miss[process_miss.MonthlyIncome.notnull()].as_matrix()
    unknown = process_miss[process_miss.MonthlyIncome.isnull()].as_matrix()
    #X,要训练的特征
    X = known[:,1:]
    #y ,结果标签
    y = known[:,0]
    #训练模型
    rf = RandomForestRegressor(random_state=0,n_estimators=200,max_depth=3,n_jobs=-1)
    rf.fit(X,y)
    #预测缺失值
    pred = rf.predict( unknown[:,1:]).round(0)
    #补缺缺失值
    df.loc[df['MonthlyIncome'].isnull(),'MonthlyIncome'] = pred
    return df
data = rf_filling(data)

NumberOfDependents变量缺失值比较少,对总体模型不会造成太大影响。首先我们先通过统计描述查看家属人数列数据。

data.NumberOfDependents.value_counts()
0.0     86902
1.0     26316
2.0     19522
3.0      9483
4.0      2862
5.0       746
6.0       158
7.0        51
8.0        24
9.0         5
10.0        5
13.0        1
20.0        1
Name: NumberOfDependents, dtype: int64

再通过图像描述

sns.countplot(x = 'NumberOfDependents',data = data)

可以看到这一列的数据主要是0、1、2、3、4,因此这一列额Na值我们从[0,1,2,3,4]中随机抽取数值进行填充。

Dependents = pd.Series([0,1,2,3,4])
for i in data['NumberOfDependents'][data['NumberOfDependents'].isnull()].index:
    data['NumberOfDependents'][i] = Dependents.sample(1)

处理异常值,根据分布情况可以看出,家庭人口数超过8人的非常少,再结合生活常识,将超过8人的全部用8代替

data['NumberOfDependents'][data['NumberOfDependents']>8] = 8

 

对缺失值处理完之后,删除重复项。

data = data.drop_duplicates() #删除重复值

 

3.2异常值处理

处理完缺失值之后我们还需要对异常值进行处理。异常值是明显偏离大多数样本抽样数据的数值。

3.2.1年龄

首先我们发现年龄列age中存在0数值,明显是异常值,将其直接剔除。

我们先使用探索性分析来查看age列的分布情况。

fig = plt.figure()
ax1 = plt.subplot()
ax1.boxplot(data['age'])
ax1.set_xticklabels(['age'])
plt.show()

我们认为年龄应该在0-100之间,之外的数据我们剔除。

data = data[data['age']>0]
data = data[data['age']<100]

 

3.2.2

对于RevolvingUtilizationOfUnsecuredLines(可用额度比值)及DebtRatio(负债率)而言,箱线图表示如下:

fig = plt.figure()
x1 = data['RevolvingUtilizationOfUnsecuredLines']
x2 = data['DebtRatio']
ax = fig.add_subplot(111)
ax.boxplot([x1,x2])
ax.set_xticklabels(['RevolvingUtilizationOfUnsecuredLines','DebtRatio'])

上述两个变量的数值类型均是百分比,故将大于1的值全部删除。

data = data[data['RevolvingUtilizationOfUnsecuredLines']>=0]
data = data[data['RevolvingUtilizationOfUnsecuredLines']<=1]
data = data[data['DebtRatio']>=0]
data = data[data['DebtRatio']<=1]

 

3.2.3

对于变量NumberOfTime30-59DaysPastDueNotWorse(逾期30-59天笔数)、NumberOfTimes90DaysLate(逾期90天笔数)、NumberOfTime60-89DaysPastDueNotWorse(逾期60-89天笔数),箱型图分析如下。

fig = plt.figure()
x1 = data['NumberOfTime30-59DaysPastDueNotWorse']
x2 = data['NumberOfTimes90DaysLate']
x3 = data['NumberOfTime60-89DaysPastDueNotWorse']
ax = fig.add_subplot(111)
ax.boxplot([x1,x2,x3])
ax.set_xticklabels(['NumberOfTime30-59DaysPastDueNotWorse','DebtRatio','NumberOfTime60-89DaysPastDueNotWorse'])

这几个变量有两个异常值96、98,将其删除。

data = data[data['NumberOfTime30-59DaysPastDueNotWorse']!=96]
data = data[data['NumberOfTime30-59DaysPastDueNotWorse']!=98]
data = data[data['NumberOfTimes90DaysLate']!=96]
data = data[data['NumberOfTimes90DaysLate']!=98]
data = data[data['NumberOfTime60-89DaysPastDueNotWorse']!=96]
data = data[data['NumberOfTime60-89DaysPastDueNotWorse']!=98]

 

3.2.4月收入

虽然我们在前面填补了月收入的数据,但是可以看到其中的一些数据还是异常的,我们先通过图形来分析一下。

import seaborn as sns
sns.set_style("whitegrid")
sns.boxplot(x = data.MonthlyIncome)

初步观察,月收入大多集中在500000以内,我们再统计超过500000的个数。

(data['MonthlyIncome']>500000).sum()

结果显示为 12

说明这12个值可能为异常值,我们使用500000来替换它们,不影响分析的结果。

data["MonthlyIncome"][data["MonthlyIncome"]>500000] = 500000

 

4.探索性分析

4.1好坏客户整体情况

考虑到实际分类中,一般正常客户为1,违约客户为0,所以我们需要先转换客户分类列数据。

data['SeriousDlqin2yrs'] = 1-data['SeriousDlqin2yrs']  #转换0、1
grouped = data['SeriousDlqin2yrs'].groupby(data['SeriousDlqin2yrs']).count()
print("不良客户占比:",(grouped[0]/grouped[1])*100,"%")
grouped.plot(kind = 'bar')

结果显示不良客户占比为:6.337390508687819 %

这里明显可以看出这一列存在类失衡的问题,在后续的建模过程中可使用class_weight = 'balanced'来解决这个问题。

 

4.2客户年龄分布

查看年龄变量分布状态

sns.distplot(data['age'])

年龄分布符合正态分布,对后续结果影响不大。

 

4.3相关性分析

corr = data.corr()
corr
fig = plt.figure(figsize=(12,8))
ax1 = fig.add_subplot(1,1,1)
sns.heatmap(corr,annot = True,cmap = 'rainbow',ax = ax1)

由上图可以看出,各变量之间的相关性是非常小的,可以初步判断不存在多重共线性问题。

4.4 数据切分

from sklearn.model_selection import train_test_split
Y = data['SeriousDlqin2yrs']
X=data.iloc[:,1:]
X_train, X_test, Y_train, Y_test = train_test_split(X,Y,train_size = 0.8,random_state=0)
train = pd.concat([Y_train,X_train], axis =1)
test = pd.concat([Y_test,X_test], axis =1)
train = train.reset_index(drop=True)
test = test.reset_index(drop=True)
#保留一份测试数据集,后面生成评分卡
test.to_csv('origin_test.csv', index=False)

5.构建模型

5.1特征选择

特征选择对于模型的准确使用至关重要,在建立评分卡的时候我们使用IV值来筛选特征,具体的IV值和WOE计算如下。

5.1.1特征分箱

特征分箱指的是将连续变量离散化或者多状态的离散变量合并成少状态。相对于连续变量,离散特征的增加或者减少相对容易,易于模型的快速迭代,离散化后的特征对于异常数据有很强的鲁棒性(模型结果不受异常数据过多的影响),能够减少未离散化之前异常值对模型的干扰,同时离散化后可以进行特征交叉。本文选取的模型算法为逻辑回归,逻辑回归属于广义线性模型,表达能力有限。而将单变量离散化之后,每个变量有单独的权重,相当于为模型引入非线性,提升了模型的表达能力,同时也降低了模型过拟合的风险。

特征分箱常用的方法有如下:有监督的Best-KS,卡方分箱,无监督的等频,等距,聚类等。根据不同的数据采用不同的分箱方式。

5.1.1.1连续变量特征分箱

假设因变量为优质客户以及不良客户,其中1为优质客户,0为违约客户。

实现连续型变量单调分箱,在等频的基础上:其中DF表示导入的数据,Y是因变量的字段名,X是自变量的字段名。

import scipy.stats as stats

def monoto_bin(Y, X, n):
    r = 0
    total_good = Y.sum()
    total_bad =Y.count()-total_good
    while 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 - 1
    d3 = pd.DataFrame(d2.min().X, columns = ['min_' + X.name])
    d3['min_' + X.name] = d2.min().X
    d3['max_' + X.name] = d2.max().X
    d3[Y.name] = d2.sum().Y
    d3['total'] = d2.count().Y
    #d3[Y.name + '_rate'] = d2.mean().Y
    #好坏比,求woe,证据权重,自变量对目标变量有没有影响,什么影响
    d3['goodattr']=d3[Y.name]/total_good
    d3['badattr']=(d3['total']-d3[Y.name])/total_bad
    d3['woe'] = np.log(d3['goodattr']/d3['badattr'])
    #iv,信息值,自变量对于目标变量的影响程度
    iv = ((d3['goodattr']-d3['badattr'])*d3['woe']).sum()
    d4 = (d3.sort_values(by = 'min_' + X.name)).reset_index(drop = True)
    print ("=" * 80)
    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

能用自动优化分箱的变量:age,Percentage,DebtRatio

dfx1,ivx1,cutx1,woex1 = monoto_bin(data['SeriousDlqin2yrs'],data['RevolvingUtilizationOfUnsecuredLines'],n = 10)
dfx2,ivx2,cutx2,woex2 = monoto_bin(data['SeriousDlqin2yrs'],data['age'],n = 10)
dfx4,ivx4,cutx4,woex4 = monoto_bin(data['SeriousDlqin2yrs'],data['DebtRatio'],n = 10)

对于不能用最优分段的变量,采用自定义分箱,进行等距分段(需对业务有深刻的了解)。一般来说要判断分箱是否合理,可通过尝试最优分段以及自定义分段来判断,判断的依据是iv值大小、分段是否合理、业务上是否能够理解。

import scipy.stats as stats

def self_bin(Y,X,cat):
    good=Y.sum()
    bad=Y.count()-good
    d1=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().X
    d3['max'] = d2.max().X
    d3['sum'] = d2.sum().Y
    d3['total'] = d2.count().Y
    d3['rate'] = d2.mean().Y
    d3['woe'] = np.log((d3['rate'] / (1 - d3['rate'])) / (good / bad))
    d3['goodattribute'] = d3['sum'] / good
    d3['badattribute'] = (d3['total'] - d3['sum']) / bad
    iv = ((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

对剩余变量进行自定义分箱

pinf = float('inf')#正无穷大
ninf = float('-inf')#负无穷大
cutx3 = [ninf, 0, 1, 3, 5, pinf]
cutx5 = [ninf,1000,2000,3000,4000,5000,6000,7500,9500,12000,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)
dfx5, ivx5,woex5 = self_bin(data['SeriousDlqin2yrs'],data['MonthlyIncome'],cutx5)
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)

我们完成了所有变量的离散化,接下来,进一步计算每个变量的Infomation Value(IV)。IV指标是一般用来确定自变量的预测能力,对变量进行筛选。 其公式为:

[公式]

我们需要求出WOE,gooddatrr,badattr值,在前面变量分段中已经求出。

ivlist=[ivx1,ivx2,ivx3,ivx4,ivx5,ivx6,ivx7,ivx8,ivx9,ivx10]#各变量IV
index=['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))+1 #设置x轴柱子的个数
ax1.bar(x, ivlist, width=0.4)#生成柱状图
ax1.set_xticks(x)  #设置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()

'''
首先,前边设置的x、y值其实就代表了不同柱子在图形中的位置(坐标),通过for循环找到每一个x、y值的相应坐标——a、b,再使用plt.text在对应位置添文字说明来生成相应的数字标签,而for循环也保证了每一个柱子都有标签。
其中,a, b+0.01表示在每一柱子对应x值、y值上方0.01处标注文字说明, '%.4f' % b,代表标注的文字,即每个柱子对应的y值, ha='center', va= 'bottom'代表horizontalalignment(水平对齐)、verticalalignment(垂直对齐)的方式,fontsize则是文字大小。
'''

根据iv值的判断标准

我们将iv值小于0.1的变量删去,即DebtRatio、MonthlyIncome、NumberOfOpenCreditLinesAndLoans、NumberRealEstateLoansOrLines和NumberOfDependents。

六、模型分析

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

6.1WOE转换。

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

# 替换成woe
train['RevolvingUtilizationOfUnsecuredLines'] = pd.Series(replace_woe(train['RevolvingUtilizationOfUnsecuredLines'], cutx1, woex1))
train['age'] = pd.Series(replace_woe(train['age'], cutx2, woex2))
train['NumberOfTime30-59DaysPastDueNotWorse'] = pd.Series(replace_woe(train['NumberOfTime30-59DaysPastDueNotWorse'], cutx3, woex3))
train['DebtRatio'] = pd.Series(replace_woe(train['DebtRatio'], cutx4, woex4))
train['MonthlyIncome'] = pd.Series(replace_woe(train['MonthlyIncome'], cutx5, woex5))
train['NumberOfOpenCreditLinesAndLoans'] = pd.Series(replace_woe(train['NumberOfOpenCreditLinesAndLoans'], cutx6, woex6))
train['NumberOfTimes90DaysLate'] = pd.Series(replace_woe(train['NumberOfTimes90DaysLate'], cutx7, woex7))
train['NumberRealEstateLoansOrLines'] = pd.Series(replace_woe(train['NumberRealEstateLoansOrLines'], cutx8, woex8))
train['NumberOfTime60-89DaysPastDueNotWorse'] = pd.Series(replace_woe(train['NumberOfTime60-89DaysPastDueNotWorse'], cutx9, woex9))
train['NumberOfDependents'] = pd.Series(replace_woe(train['NumberOfDependents'], cutx10, woex10))
train.dropna(how = 'any')
train.to_csv('WoeData.csv', index=False)

# 替换成woe
test['RevolvingUtilizationOfUnsecuredLines'] = pd.Series(replace_woe(test['RevolvingUtilizationOfUnsecuredLines'], cutx1, woex1))
test['age'] = pd.Series(replace_woe(test['age'], cutx2, woex2))
test['NumberOfTime30-59DaysPastDueNotWorse'] = pd.Series(replace_woe(test['NumberOfTime30-59DaysPastDueNotWorse'], cutx3, woex3))
test['DebtRatio'] = pd.Series(replace_woe(test['DebtRatio'], cutx4, woex4))
test['MonthlyIncome'] = pd.Series(replace_woe(test['MonthlyIncome'], cutx5, woex5))
test['NumberOfOpenCreditLinesAndLoans'] = pd.Series(replace_woe(test['NumberOfOpenCreditLinesAndLoans'], cutx6, woex6))
test['NumberOfTimes90DaysLate'] = pd.Series(replace_woe(test['NumberOfTimes90DaysLate'], cutx7, woex7))
test['NumberRealEstateLoansOrLines'] = pd.Series(replace_woe(test['NumberRealEstateLoansOrLines'], cutx8, woex8))
test['NumberOfTime60-89DaysPastDueNotWorse'] = pd.Series(replace_woe(test['NumberOfTime60-89DaysPastDueNotWorse'], cutx9, woex9))
test['NumberOfDependents'] = pd.Series(replace_woe(test['NumberOfDependents'], cutx10, woex10))
test.dropna(how = 'any')

删除对因变量不明显的变量.剔除五个变量

train_X =train.drop(['NumberRealEstateLoansOrLines','NumberOfDependents','NumberOfOpenCreditLinesAndLoans','DebtRatio','MonthlyIncome'],axis=1)
test_X =test.drop(['NumberRealEstateLoansOrLines','NumberOfDependents','NumberOfOpenCreditLinesAndLoans','DebtRatio','MonthlyIncome'],axis=1)

 

6.2模型建立

在评分卡建模中,我们选择Logistc回归模型,对训练数据进行训练,对测试数据进行预测。

from sklearn.metrics import roc_curve, auc
import statsmodels.api as sm
X_train =train_X.drop(['SeriousDlqin2yrs'],axis =1)
y_train =train_X['SeriousDlqin2yrs']
y_test = test_X['SeriousDlqin2yrs']
X_test = test_X.drop(['SeriousDlqin2yrs'],axis =1)
X_train = sm.add_constant(X_train)
logit = sm.Logit(y_train,X_train)
result = logit.fit()
print(lg.summary2())

可知,各变量通过显著性检验,满足要求。

 

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

from sklearn.metrics import roc_curve, auc
X2 = sm.add_constant(X_test)
resu = result.predict(X2)
FPR,TPR,threshold = roc_curve(y_test,resu)
ROC_AUC = auc(FPR,TPR)
plt.plot(FPR, TPR, 'b', label='AUC = %0.2f' % ROC_AUC)
plt.legend(loc='lower right')
plt.plot([0, 1], [0, 1], 'r--')
plt.xlim([0, 1])   #限制x轴的取值范围
plt.ylim([0, 1])
plt.ylabel('TPR')
plt.xlabel('FPR')
plt.show()

模型的AUC值为0.85,说明模型的分类效果还是不错的。

七、信用评分卡的建立

7.1评分标准

依据相关论文资料得到:

a=log(p_good/P_bad)
Score = offset + factor * log(odds)

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

个人总评分= 基础分+ 各部分得分。

# 我们取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)
# 因为第一个是常数项
#构建评分卡时候只需要选出那些,IV值高的特征就行,最后相加得到总分

#计算分数函数
def get_score(coe,woe,factor):
    scores=[]
    for w in woe:
        score=round(coe*w*factor,0)
        scores.append(score)
    return scores

# 各项部分分数
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)

[22.0, 19.0, 5.0, -19.0]

[-7.0, -5.0, -4.0, -3.0, -2.0, 2.0, 5.0, 12.0, 15.0]

[14.0, -25.0, -50.0, -70.0, -72.0]

[18.0, -103.0, -143.0, -172.0, -171.0]

[8.0, -58.0, -88.0, -88.0]

#根据变量计算分数
def compute_score(series,cut,score):
    list = []
    i = 0
    while i < len(series):
        value = series[i]
        j = len(cut) - 2
        m = len(cut) - 2
        while j >= 0:
            if value >= cut[j]:
                j = -1
            else:
                j -= 1
                m -= 1
        list.append(score[m])
        i += 1
    return list
# j和m相当于j是移动光标,m是跟着j,确定数的
# list就是在x1里面挑一个值,这个值和series[i]是对应的

from pandas import Series
test['BaseScore']=Series(np.zeros(len(test))) + baseScore
test['x1'] = Series(compute_score(test['RevolvingUtilizationOfUnsecuredLines'], cutx1, x1))
test['x2'] = Series(compute_score(test['age'], cutx2, x2))
test['x3'] = Series(compute_score(test['NumberOfTime30-59DaysPastDueNotWorse'], cutx3, x3))
test['x7'] = Series(compute_score(test['NumberOfTimes90DaysLate'], cutx7, x7))
test['x9'] = Series(compute_score(test['NumberOfTime60-89DaysPastDueNotWorse'], cutx9, x9))
test['Score'] = test['x1'] + test['x2'] + test['x3'] + test['x7'] +test['x9']  + baseScore
test.to_csv('ScoreData.csv', index=False)

基础总得分为795

test.loc[:,['SeriousDlqin2yrs','BaseScore', 'x1', 'x2', 'x3', 'x7', 'x9', 'Score']].head()

八、 总结及展望

本文通过对kaggle上的数据Give Me Some Credit的挖掘分析,结合信用评分卡的建立原理,通过数据预处理、变量选择、建模分析预测等方法,使用了随机森林算法拟合了缺失值,使用pandas包对数据进行了清理,并使用matplotlib、seaborn绘图包将数据可视化,且使用了Logistic回归模型,最后利用模型验证过的部分特征,创建了一个简单的信用评分系统。

posted @ 2020-05-12 21:12  allenysn  阅读(299)  评论(0编辑  收藏  举报