信用卡評分模型 —— 基於Lending Club數據

1、前言

Lending Club是全球最大的撮合借款人和投資人的線上金融平臺,它利用互聯網模式建立了一種比傳統銀行系統更有效率的、能夠在借款人和投資人之間自由配置資本的機制。本次分析的源數據基於Lending Club 2017年全年和2018年一二季度的公開數據,目的是建立一個貸前評分卡。數據原址:https://www.lendingclub.com/info/download-data.action

2、數據清洗

2.1 導入分析模塊和源數據

import numpy as np
import pandas as pd
from scipy.stats import mode
import statsmodels.api as sm
import matplotlib.pyplot as plt
import seaborn as sns
import chisqbin
import warnings
from sklearn.preprocessing import LabelEncoder
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve,auc
from imblearn.over_sampling import SMOTE

pd.set_option('display.max_columns',200)
warnings.filterwarnings('ignore')
%matplotlib inline

names=['2017Q1','2017Q2','2017Q3','2017Q4','2018Q1','2018Q2']
data_list=[]
for name in names:
    data=pd.read_table('C:/Users/H/Desktop/lending_club/LoanStats_'+name+'.csv',sep=',',low_memory=False)
    data_list.append(data)
loan=pd.concat(data_list,ignore_index=True)

接下來,我們看一下目標變量loan_status

loan_status=pd.DataFrame({'意思':['還款中','審覈通過','全額結清','寬限期','逾期(31-120天)','逾期(16-30天)','壞賬','違約'],'數量':loan['loan_status'].value_counts().values},
                        index=loan['loan_status'].value_counts().index)
print(loan_status)

在這裏插入圖片描述
因爲主要目的是貸前評分,這裏只考慮全額結清未按時還款的的情況,還款中暫時不考慮。

loan['loan_status']=loan['loan_status'].replace(['Fully Paid','In Grace Period','Late (31-120 days)','Late (16-30 days)','Charged Off','Default'],
                                               ['0','1','1','1','1','1'])
loan=loan[loan['loan_status'].isin(['0','1'])]
loan['loan_status']=loan['loan_status'].astype('int')

2.2 缺失值處理

數據集的變量雖然有100多個,但其中不少變量包含大量缺失值,缺失比例在50%以上,還有部分變量與我們的目標變量關係不大,這些變量一併剔除。

null_cols=loan.isna().sum().sort_values(ascending=False)/float(loan.shape[0])
null_cols[null_cols > .3]

loan=loan.dropna(thresh=loan.shape[0]*.7,axis=1)
names=['sub_grade','emp_title','pymnt_plan','title','zip_code','total_rec_late_fee','recoveries','collection_recovery_fee','last_pymnt_d','last_pymnt_amnt',
      'last_credit_pull_d','collections_12_mths_ex_med','policy_code','acc_now_delinq','num_tl_120dpd_2m','num_tl_30dpd','hardship_flag','debt_settlement_flag',
      'funded_amnt','funded_amnt_inv','out_prncp_inv','total_pymnt','total_pymnt_inv','out_prncp','total_rec_prncp','total_rec_int']
loan=loan.drop(names,axis=1)
loan=loan.drop(loan[loan['earliest_cr_line'] == '1900/1/0'].index)

在這裏插入圖片描述
接下來進行進行數據的清洗,把數據轉成需要的形式,同時對缺失值進行插補。

loan['int_rate']=loan['int_rate'].str.replace('%','').astype('float')
loan['revol_util']=loan['revol_util'].str.replace('%','').astype('float')

loan['issue_d']=pd.to_datetime(loan['issue_d'],format='%Y/%m/%d')
loan['earliest_cr_line']=pd.to_datetime(loan['earliest_cr_line'],format='%Y/%m/%d')
loan['mth_interval']=loan['issue_d']-loan['earliest_cr_line']
loan['mth_interval']=loan['mth_interval'].apply(lambda x: round(x.days/30,0))
loan['issue_m']=loan['issue_d'].apply(lambda x: x.month)
loan=loan.drop(['issue_d','earliest_cr_line'],axis=1)

loan['emp_length']=loan['emp_length'].fillna('0')
loan['emp_length']=loan['emp_length'].str.replace(' year','')
loan['emp_length']=loan['emp_length'].str.replace('s','')
loan['emp_length']=loan['emp_length'].replace(['10+','< 1'],['11','0']).astype('int')

loan['income_vs_loan']=loan['annual_inc']/loan['loan_amnt']
loan['delinq_2yrs']=loan['delinq_2yrs'].astype('int')
loan['revol_bal']=loan['revol_bal'].astype('int')
loan['total_acc']=loan['total_acc'].astype('int')

剩下的含缺失值的變量都是浮點屬性,我們用這些變量的衆數進行查補。

na_cols=loan.isna().sum()[loan.isna().sum() > 0].index.values
print(loan[na_cols].dtypes)

for col in na_cols:
    loan[col][loan[col].isna()]=mode(loan[col][loan[col].notna()])[0][0]

在這裏插入圖片描述
初步的數據清洗已經完畢,所有變量未含有缺失值。

3、特徵工程

最後的模型是一個評分卡,我們需要知道哪些變量對目標變量loan_status最重要,接下來會進行一系列方式對變量進行篩選。

3.1 強相關性變量

首先,剩下的變量中有一些相關性較強,這些變量不利後面的操作,需要剔除。

num_features=[]
obj_features=[]
for i in loan.columns.values:
    if loan[i].dtype == 'object':
        obj_features.append(i)
    else:
        num_features.append(i)
num_features.remove('loan_status')

fig,ax=plt.subplots(figsize=(20,20))
sns.heatmap(loan[num_features].corr().round(2))
cor=(np.abs(loan[num_features].corr()) < 0.6)
l=[]
for i in range(len(num_features)):
    s=(cor.iloc[i+1:,i].sum() == (len(num_features)-i-1))
    l.append(s)
    
num_feats=[]
for i in range(len(num_features)):
    if l[i]:
        num_feats.append(num_features[i])
        
fig,ax=plt.subplots(figsize=(20,20))
sns.heatmap(loan[num_feats].corr().round(2),annot=True)

這是強相關變量刪除後的相關圖,變量間的相關係數不高於0.6
這是強相關變量刪除後的相關圖,變量間的相關係數不高於0.6。

3.2 隨機森林過濾變量

接下來,通過隨機森林對變量進行過濾,按照變量在隨機森林模型中的重要性排序,可以剔除後面重要性很低的變量

loan_1=loan.copy()
loan_1[obj_features]=loan_1[obj_features].apply(LabelEncoder().fit_transform)

data_1=loan_1[loan_1['loan_status'] == 1]
data_0=loan_1[loan_1['loan_status'] == 0]

data_1_train,data_1_test=train_test_split(data_1,test_size=.3,random_state=12)
data_0_train,data_0_test=train_test_split(data_0,test_size=.3,random_state=12)
train=pd.concat([data_1_train,data_0_train])
test=pd.concat([data_1_test,data_0_test])

train_X=train.drop(['loan_status'],axis=1)
train_y=train['loan_status']
test_X=test.drop(['loan_status'],axis=1)
test_y=test['loan_status']

#resampled_X,resampled_y=SMOTE(random_state=12).fit_sample(train_X,train_y)
rf=RandomForestClassifier(n_estimators=500,max_depth=10,random_state=1).fit(train_X,train_y)
importance=pd.DataFrame({'features':train_X.columns.values,'importance':rf.feature_importances_})
importance.sort_values(by='importance',ascending=False).style.bar()

loan=loan.drop(importance[importance['importance'] < 0.01]['features'].values,axis=1)

在這裏插入圖片描述

3.3 卡方分箱

在邏輯迴歸建立評分卡的過程中,變量的分箱是很重要的一步。下面將對剩下的連續性變量進行卡方分箱,並計算所有變量的WOE和IV值,並根據IV值進行最後的篩選。

purpose=chisqbin.BadRateEncoding(loan,'purpose','loan_status')
#state=chisqbin.BadRateEncoding(loan,'addr_state','loan_status')
loan['purpose']=purpose['encoding']
#loan['addr_state']=state['encoding']

num_features=[]
obj_features=[]
for i in loan.columns.values:
    if loan[i].dtype == 'object':
        obj_features.append(i)
    else:
        num_features.append(i)
num_features.remove('loan_status')

train,test=train_test_split(loan,test_size=.3,random_state=12)
cuts=[]
for i in num_features:
    cut=chisqbin.ChiMerge(train,i,'loan_status',max_interval=5)
    cut=[float('-inf')]+cut
    cut.append(float('inf'))
    cuts.append(cut)
    train[i]=pd.cut(train[i],cut)

columns=train.columns.values.tolist()
columns.remove('loan_status')
WOEs={}
IVs=[]
for i in columns:
    woe,iv=chisqbin.CalcWOE(train,i,'loan_status')
    WOEs[i]=woe
    IVs.append(iv)

IV=pd.DataFrame({'feature':columns,'IV':IVs})
IV.sort_values(by='IV',ascending=False).style.bar()

在這裏插入圖片描述
選取出IV值大於0.02的變量,用它們對應的WOE對數據進行替換。

features=IV.loc[IV['IV'] > 0.02,'feature'].values
features=features.tolist()
features.append('loan_status')

for i in range(len(columns)):
    col=columns[i]
    train[col]=train[col].replace(WOEs[col])

for i in range(len(num_features)):
    col=num_features[i]
    test[col]=pd.cut(test[col],cuts[i])

for i in range(len(columns)):
    col=columns[i]
    test[col]=test[col].replace(WOEs[col])    

4、建立評分卡

準備工作已經完成,下面開始建立評分卡。
首先先建立邏輯迴歸模型,獲取變量的參數,同時可以發現,變量的顯著性檢驗全部通過。

train=train[features]
test=test[features]

train_X=train.drop('loan_status',axis=1)
train_y=train['loan_status']
test_X=test.drop('loan_status',axis=1)
test_y=test['loan_status']

train_X=sm.add_constant(train_X)
logit=sm.Logit(train_y,train_X).fit()
logit.summary()

在這裏插入圖片描述

計算得出模型的AUC爲0.69,水平一般,後面會進行優化。

test_X=sm.add_constant(test_X)
result=logit.predict(test_X)
fpr,tpr,th=roc_curve(test_y,result)
rocauc=auc(fpr,tpr)

plt.plot(fpr,tpr,'b',label='AUC = %.2f' %rocauc)
plt.legend(loc='lower right')
plt.plot([0,1],[0,1],'r--')
plt.xlim([0,1])
plt.ylim([0,1])
plt.xlabel('fpr')
plt.ylabel('tpr')

在這裏插入圖片描述

最終,我們可以得到如下的評分卡。

B=20/np.log(2)
A=600+20*np.log(1/60)/np.log(2)
basescore=round(A-B*logit.params[0],0)
scorecard=[]
#features.remove('loan_status')
for i in features:
    woe=WOEs[i]
    interval=[]
    scores=[]
    for key,value in woe.items():
        score=round(-(value*logit.params[i]*B))
        scores.append(score)
        interval.append(key)
    data=pd.DataFrame({'interval':interval,'scores':scores}) 
    scorecard.append(data)

在這裏插入圖片描述
在這裏插入圖片描述

5、小結

經過上述步驟,建立了一個基本的評分卡,由於源數據缺失客戶的人口信息數據(當然,lending club也不可能透露),所以評分卡還是有瑕疵,AUC值只有0.69,後續會改進代碼和流程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章