本次做的是一个银行信用评分卡项目,主要就是通过对银行的客户进行区分,根据信用等级划分为“好客户”和“坏客户”两个类别,然后利用机器学习算法进行建模分析,最后建立信用评分卡,为银行做出放贷决策提供依据。
- 数据预处理
第一步:导入数据
#导入科学计算包
import numpy as np
import matplotlib as mlt
import matplotlib.pyplot as plt
plt.rc('font', family='Microsoft YaHei')
%matplotlib inline
import seaborn as sns
import pandas as pd
import os
os.chdir("/Jupyter_working_path/Numpy/data")
df = pd.read_csv(r"cs-training.csv")
df
可以看出,该数据集一共有15万行,12个字段。但是由于英文表示的字段名太长,所以改用中文
df0 = df.rename(columns={"Unnamed:0":"ID",
"SeriousDlqin2yrs":"好坏客户",
"RevolvingUtilizationOfUnsecuredLines":"额度比值",
"age":"年龄",
"NumberOfTime30-59DaysPastDueNotWorse":"逾30-59天",
"DebtRatio":"负债率",
"MonthlyIncome":"月收入",
"NumberOfOpenCreditLinesAndLoans":"信贷数",
"NumberOfTimes90DaysLate":"逾90天",
"NumberRealEstateLoansOrLines":"固定资产",
"NumberOfTime60-89DaysPastDueNotWorse":"逾60-89天",
"NumberOfDependents":"家属"})
df0.head(5)
第二步:数据清洗
- 处理缺失值
#查看缺失值
df0.info()
可以看出“月收入”和“家属”两个字段有缺失值,我把它们找出来
def missing_values_table(df0):
#全部缺失值
mis_val=df0.isnull().sum()
#缺失值比例
mis_val_percent=100*df0.isnull().sum() /len(df0)
#做成一个表
mis_val_table=pd.concat([mis_val,mis_val_percent], axis=1)
#改列名
mis_val_table_ren_columns = mis_val_table.rename(columns={0:'缺失值',1:'缺失比例'})
#对缺失值排序
mis_val_table_ren_columns = mis_val_table_ren_columns[mis_val_table_ren_columns.iloc[:,1] != 0].sort_values('缺失比例',ascending=False).round(1)
#打印表
print("数据有"+str(df0.shape[1])+"列.\n" "其中" + str(mis_val_table_ren_columns.shape[0]) +"列含有缺失值")
#返回缺失行列
return mis_val_table_ren_columns
missing_values_table(df0)
对于缺失值的处理可以有一下几种方法:
df = df.dropna() #去掉缺失的行
df = df.fillna(num) #补充固定的数
df = df.fillna(df.mean()) #补充均值
df = df.fillna(df.median()) #补充中位数
df = df.interpolate()#插值法
#月收入的缺失值有点多,可以考虑用中位数填补
df0['月收入'].fillna(df0['月收入'].median(),inplace=True)
- 异常值的处理
可以采用分享发,将客户年龄进行分箱,观察异常值所在区间
#分箱法
df0['年龄'].hist(bins=50)
plt.savefig("C:\\Jupyter_working_path\\Numpy\\picture")
def blk(floor, root):
def f(x):
if x <floor:
x=floor
elif x >root:
x = root
return x
return f
q1= df0['年龄'].quantile(0.01)
q99 = df0['年龄'].quantile(0.99)
blk_tot=blk(floor=q1,root=q99)
df0['年龄'] = df0['年龄'].map(blk_tot)
df0['年龄'].hist(bins=50)
plt.savefig("C:\\Jupyter_working_path\\Numpy\\picture0")
第三步:探索性分析
- 单变量分析
分析好坏客户比
#单变量:
age_cut = pd.cut(df0['年龄'],5)
age_cut_grouped=df0["好坏客户"].groupby(age_cut).count()
age_cut_grouped1=df0["好坏客户"].groupby(age_cut).sum()
df1=pd.merge(pd.DataFrame(age_cut_grouped), pd.DataFrame(age_cut_grouped1),right_index=True,left_index=True)
df1.rename(columns={"好坏客户_x":"好客户","好坏客户_y":"坏客户"},inplace=True)
df1.insert(2,"坏客户率",df1["坏客户"]/df1["好客户"])
ax1 = df1[["好客户","坏客户"]].plot.bar()
ax1.set_xticklabels(df1.index,rotation=15)
ax1.set_ylabel("客户数")
ax1.set_title("年龄于好坏客户分布图")
plt.savefig("C:\\Jupyter_working_path\\Numpy\\picture1")
可见坏客户主要集中在23-60岁,可能是60岁以上的客户对于接待会更加谨慎,担心还不上款,所以借贷也相对减少
ax11 = df1["坏客户率"].plot()
ax11.set_ylabel("坏客户率")
ax11.set_title("坏客户率随年龄变化趋势图")
plt.savefig("C:\\Jupyter_working_path\\Numpy\\picture2")
可见,坏客户率会随着年龄而逐渐降低
- 多变量分析
#多变量:
plt.rcParams["font.sans-serif"] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
corr = df0.corr() #计算各个变量的相关系数
xticks = list(corr.index) #x轴标签
yticks = list(corr.index) #y轴标签
fig = plt.figure()
axl2 = fig.add_subplot(1,1,1)
sns.heatmap(corr, annot=True, cmap="rainbow", ax=axl2, linewidths=.5, annot_kws={'size':6,'weight':'bold','color':"blue"})
axl2.set_xticklabels(xticks, rotation=35, fontsize=9)
axl2.set_yticklabels(yticks, rotation=0, fontsize=9)
plt.savefig("C:\\Jupyter_working_path\\Numpy\\picture3")
第四步:WOE值替换和LR建模
- 分箱
#分箱
pinf = float('inf') #正无穷
ninf = float('-inf') #负无穷
cut1=pd.qcut(df0["额度比值"],4, labels=False)
cut2=pd.qcut(df0["年龄"],8, labels=False)
bins3=[ninf,0,1,3,5,pinf]
cut3=pd.cut(df0["逾30-59天"],bins3,labels=False)
cut4=pd.qcut(df0["负债率"],3, labels=False)
cut5=pd.qcut(df0["月收入"],4, labels=False)
cut6=pd.qcut(df0["信贷数"],4, labels=False)
bins7=[ninf,0,1,3,5,pinf]
cut7=pd.cut(df0["逾90天"],bins7, labels=False)
bins8=[ninf,0,1,2,3,pinf]
cut8=pd.cut(df0["固定资产"],bins8, labels=False)
bins9=[ninf,0,1,3,pinf]
cut9=pd.cut(df0["逾60-89天"],bins9, labels=False)
bins10=[ninf,0,1,3,5,pinf]
cut10=pd.cut(df0["家属"],bins10, labels=False)
- 计算WOE值 和 IV 值
#好坏客户比率
rate = df0["好坏客户"].sum()/(df0["好坏客户"].count() - df0["好坏客户"].sum())
#定义WOE计算函数
def get_woe_data(cut):
grouped=df0["好坏客户"].groupby(cut, as_index = True).value_counts()
woe=np.log(pd.DataFrame(grouped).unstack().iloc[:,1] / pd.DataFrame(grouped).unstack().iloc[:,0] /rate)
#计算每个分组的woe值
return(woe)
cut1_woe=get_woe_data(cut1)
cut2_woe=get_woe_data(cut2)
cut3_woe=get_woe_data(cut3)
cut4_woe=get_woe_data(cut4)
cut5_woe=get_woe_data(cut5)
cut6_woe=get_woe_data(cut6)
cut7_woe=get_woe_data(cut7)
cut8_woe=get_woe_data(cut8)
cut9_woe=get_woe_data(cut9)
cut10_woe=get_woe_data(cut10)
- 定义IV值计算函数
#定义IV值计算函数
def get_IV_data(cut, cut_woe):
grouped=df0["好坏客户"].groupby(cut, as_index=True).value_counts()
cut_IV=((pd.DataFrame(grouped).unstack().iloc[:,1]/df0["好坏客户"].sum() -pd.DataFrame(grouped).unstack().iloc[:,0] /(df0["好坏客户"].count() - df0["好坏客户"].sum()))* cut_woe).sum()
return(cut_IV)
#计算各分组的IV值
cut1_IV=get_IV_data(cut1, cut1_woe)
cut2_IV=get_IV_data(cut2, cut2_woe)
cut3_IV=get_IV_data(cut3, cut3_woe)
cut4_IV=get_IV_data(cut4, cut4_woe)
cut5_IV=get_IV_data(cut5, cut5_woe)
cut6_IV=get_IV_data(cut6, cut6_woe)
cut7_IV=get_IV_data(cut7, cut7_woe)
cut8_IV=get_IV_data(cut8, cut8_woe)
cut9_IV=get_IV_data(cut9, cut9_woe)
cut10_IV=get_IV_data(cut10, cut10_woe)
- 各组的IV值可视化
#各组的IV值可视化
df_IV=pd.DataFrame([cut1_IV,cut2_IV,cut3_IV,cut4_IV,cut5_IV,cut6_IV,cut7_IV,cut8_IV,cut9_IV,cut10_IV], index=df0.columns[2:])
df_IV.plot(kind="bar")
#for a,b in zip(range(10),df1.values): 报错TypeError: only size-1 arrays can be converted to Python scalars
#plt.text(a,b,'%.2f' % b,ha='center', va='bottom',fontsize=9)
plt.savefig("C:\\Jupyter_working_path\\Numpy\\picture4")
df_new=df0.drop(["负债率","月收入","信贷数","固定资产","家属"],axis=1)
第五步:LR建模
#LR建模(逻辑斯蒂回归模型)
#替换数据
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["可用额度比值"]=replace_data(cut1, cut1_woe)
df_new["年龄"]=replace_data(cut2, cut2_woe)
df_new["逾30-59天"]=replace_data(cut3, cut3_woe)
df_new["逾90天"]=replace_data(cut7, cut7_woe)
df_new["逾60-89天"]=replace_data(cut9, cut9_woe)
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve,auc
x = df_new.iloc[:,1:]
y = df_new.iloc[:,0]
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.4,random_state=0)
#模型训练
model=LogisticRegression()
clf=model.fit(x_train,y_train)
print("测试成绩:{}".format(clf.score(x_test,y_test)))
y_pred=clf.predict(x_test)
y_pred1=clf.decison_function(x_test)
- 绘制ROC曲线以及计算AUC值
#绘制ROC曲线以及计算AUC值
fpr,tpr,threshold = roc_curve(y_test,y_pred1)
roc_auc = auc(fpr,tpr)
plt.plot(fpr,tpr,color='darkorange',
label='ROC curve(area =%0.2f)' % roc_auc)
plt.plot([0,1],[0,1],color='navy',linestype='--')
plt.xlim([0.0,1.0])
plt.ylim([0.0,1.0])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC_curve')
plt.legend(loc='lower right')
plt.show()
- 将模型转化成信用分
coe = model.coef_
import numpy as np
factor = 20/np.log(2)
offset = 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 scores
- 计算每个变量得分
x1 = get_score(coe[0][0],cut1_woe,factor)
x2 = get_score(coe[0][1],cut2_woe,factor)
x3 = get_score(coe[0][2],cut3_woe,factor)
x7 = get_score(coe[0][3],cut7_woe,factor)
x9 = get_score(coe[0][4],cut9_woe,factor)
- 打印输出每个特征对应的分数
print("可用额度对应的分数:{}".format(x1))
print("年龄对应的分数:{}".format(x2))
print("逾期30-59天笔数对应的分数:{}".format(x3))
print("逾期90天笔数对应的分数:{}".format(x7))
print("逾期60-89天笔数对应的分数:{}".format(x9))
好了,大功告成,还有很多地方需要完善,继续加油!