Give me a card 信用卡評分模型
來自專欄 猴子聊數據分析
首先聲明一下:由於個人業務水平有限,期間有理解錯誤的地方和不合理的地方,歡迎大家來點評和討論,相互促進學習。
前言
上一篇文章信用評分模型的理解和學習中,了解什麼是信用風險及信用評分,信用評分建模流程,本篇將通過對kaggle上的Give Me Some Credit 項目進行數據分析,根據信用評分的建立原理,基於python創建一個簡單的信用評分系統,以一種分數的手段來衡量風險概率,更好幫助貸款方做好決策。
目錄
- 概述和說明
- 數據預處理
- 數據探索分析
- 特徵選擇
- 模型建立
- 信用評分卡
- 總結
1.概述和說明
1.1、概述
從kaggles上Give Me Some Credit獲取相應的數據,開發一個申請的評分卡模型,對未來一段時間(如12個月)融資人出現違約(如至少一次90天或90天以上逾期)的概率進行預測,對客戶信用進行評估打分。
1.2、說明
樣本總共有150000條數據,11個變數,其中SeriousDlqin2yrs為目標變數
結合業務知識,理解好變數是做好數據分析的關鍵,下面是對變數進行分類有助於更好理解變數。
基本屬性:包括了借款人當時的年齡
償債能力:包括了借款人的月收入、負債比率
信用往來:兩年內35-59天逾期次數、兩年內60-89天逾期次數、兩年內90天或高於90天逾期的次數。
財產狀況:包括了開放式信貸和貸款數量、不動產貸款或額度數量 ,
其他因素:包括了借款人的家屬數量(不包括本人在內)
2.數據預處理
2.1 數據導入
#導入包import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as sns#導入數據data = pd.read_csv(cs_training.csv)#刪除無關的數據data.drop(data.iloc[:,:1],inplace = True,axis=1)
2.2數據初步探索
查看數據集的基本情況,對缺失值和異常值進行初步的判斷
data.info()data.describe()
初步觀察發現:MonthlyIncome和NumberOfDependents存在缺失值,觀測到age年齡為0,通常情況下,年齡值不可能為0,因而視為異常值。
2.3數據清洗
2.3.1變數重命名
觀察發現變數比較長,將變數名進行簡化
columns = ({SeriousDlqin2yrs:Bad, RevolvingUtilizationOfUnsecuredLines:Percentage, NumberOfOpenCreditLinesAndLoans:Number_Open, NumberOfTimes90DaysLate:90-, NumberRealEstateLoansOrLines:Number_Estate, NumberOfTime60-89DaysPastDueNotWorse:60-89, NumberOfDependents:Dependents, NumberOfTime30-59DaysPastDueNotWorse:30-59} )data.rename(columns=columns,inplace = True)
2.3.2缺失值處理
對缺失情況進行進一步的查看,對缺失率進行計算,得到MonthlyIncome和Dependents 數據存在缺失,monthlyIncome 缺失數據最多,缺失率最高
missing_df = data.isnull().sum(axis =0).reset_index()missing_df.columns = [columns_name,missing_count]missing_df[filling_factor] = (data.shape[0] - missing_df[missing_count])/data.shape[0] * 100miss_df1 = missing_df.sort_values(filling_factor).reset_index(drop =True)miss_df1
對於缺失值處理,有很多種方法,
包括
- 直接捨棄(一般缺失值佔比較多的)
- 均值,中位數,眾數等填充法(一般缺失值佔比較少的),或者填充0
- 插值法(拉格朗日插值法)
- 根據變數之間的相似關係進行填補,採用機器學習演算法擬合或預測缺失值(如使用隨機森林進行缺失值預測)
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 dfdata = rf_filling(data)
- Dependents 缺失處理
Dependents變數缺失值比較少,直接刪除,對總體模型不會造成太大影響。對缺失值處理完之後,刪除重複項
data = data.dropna()data.info()data = data.drop_duplicates()
2.3.3異常值處理
對於異常值處理,有很多種方法,
包括
- 刪除異常值(少量或無意義的變數);
- 視為缺失值,用缺失值的處理方法處理;
- 不處理。
age為0的異常值
data =data[data[age] > 0]
觀察30-59天,60-89天,90-天三個變數的異常值
從箱線圖中看出,均有異常值,且再90以上
剔除變數0-59天,60-89天,90-三個變數的異常值
data = data[data[30-59]<90]data = data[data[60-89]<90]data = data[data[90-]<90]data = data.reset_index(drop=True)
3.數據探索分析
3.1單變數分析
查看年齡變數分布狀態
sns.distplot(data[age])
3.2相關性分析
因為邏輯回歸對於相關性比較高的變數很敏感,所以要計算相關性研究各變數之間的相關性
corr = data.corr()corrfig = plt.figure(figsize = (12,8))ax1 = fig.add_subplot(1, 1, 1)sns.heatmap(corr, annot=True, cmap=rainbow, ax=ax1, annot_kws={size: 9, weight: bold, color: blue})#繪製相關性係數熱力圖
由上圖可以看出,各變數之間的相關性是非常小的,可以初步判斷不存在多重共線性問題,
5.3數據切分
為了驗證模型的擬合效果,我們需要對數據集進行切分,我們利用sklearn包中的train_test_split(數據分割功能)函數將數據隨機分成訓練集和測試集
from sklearn.cross_validation import train_test_splity = data[Bad]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)
4、特徵選擇
4.1特徵分箱
對連續變數進行離散化,首先嘗試最優分段,定義一個自動分箱函數
import scipy.stats as statsdef monoto_bin(Y, X, n = 20): r = 0 total_bad = Y.sum() total_good =Y.count()-total_bad 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可以進行最優分段
#能用自動優化分箱的變數:age,Percentage,DebtRatiodfx1,ivx1,cutx1,woex1 = monoto_bin(train[Bad],train[Percentage],n = 10)dfx2,ivx2,cutx2,woex2 = monoto_bin(train[Bad],train[age],n = 10)dfx4,ivx4,cutx4,woex4 = monoto_bin(train[Bad],train[DebtRatio],n = 10)
對於不能用最優分段的變數,採用自定義分箱,進行等距分段(需對業務有深刻的了解)
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(train[Bad],train[30-59],cutx3)dfx5, ivx5,woex5 = self_bin(train[Bad],train[MonthlyIncome],cutx5)dfx6, ivx6,woex6 = self_bin(train[Bad],train[Number_Open],cutx6) dfx7, ivx7,woex7 = self_bin(train[Bad],train[90-],cutx7)dfx8, ivx8,woex8 = self_bin(train[Bad],train[Number_Estate],cutx8) dfx9, ivx9,woex9 = self_bin(train[Bad],train[60-89],cutx9)dfx10, ivx10,woex10 = self_bin(train[Bad],train[Dependents],cutx10)
我們完成了所有變數的離散化,接下來,進一步計算每個變數的Infomation Value(IV)。IV指標是一般用來確定自變數的預測能力,對變數進行篩選。 其公式為:
我們需要求出WOE,gooddatrr,badattr值,在前面變數分段中已經求出。
y=[ivx1,ivx2,ivx3,ivx4,ivx5,ivx6,ivx7,ivx8,ivx9,ivx10]index=[x1,x2,x3,x4,x5,x6,x7,x8,x9,x10]fig= plt.figure(figsize = (18,8))ax1 = fig.add_subplot(1, 1, 1)ax1.bar(range(1,11), y, width_=0.4)#生成柱狀圖ax1.set_xticks(range(1,11))ax1.set_xticklabels(index, rotation=0, fontsize=12)ax1.set_ylabel(IV, fontsize=14)#在柱狀圖上添加數字標籤for i, v in enumerate(y): plt.text(i+1, v+0.01, %.4f % v, ha=center, va=bottom, fontsize=12)plt.show()
將IV值可視化,如下圖
根據IV值判斷變數預測能力的標準是
刪除掉小於0.1的變數,月收入,負債率, Number_Estate,Dependents, Number_Open。
5、模型建立
5.1WOE轉化
在建立模型之前,我們需要將篩選後的變數轉換為WoE值,便於信用評分
def change_woe(d,cut,woe): list=[] i=0 while i<len(d): value=d[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(woe[m]) i += 1 return list#訓練集轉化train[Percentage] = pd.Series(change_woe(train[Percentage], cutx1, woex1))train[age] = pd.Series(change_woe(train[age], cutx2, woex2))train[30-59] = pd.Series(change_woe(train[30-59], cutx3, woex3))train[DebtRatio] = pd.Series(change_woe(train[DebtRatio], cutx4, woex4))train[MonthlyIncome] = pd.Series(change_woe(train[MonthlyIncome], cutx5, woex5))train[Number_Open] = pd.Series(change_woe(train[Number_Open], cutx6, woex6))train[90-] = pd.Series(change_woe(train[90-], cutx7, woex7))train[Number_Estate] = pd.Series(change_woe(train[Number_Estate], cutx8, woex8))train[60-89] = pd.Series(change_woe(train[60-89], cutx9, woex9))train[Dependents] = pd.Series(change_woe(train[Dependents], cutx10, woex10))#測試集轉化test[Percentage] = pd.Series(change_woe(test[Percentage], cutx1, woex1))test[age] = pd.Series(change_woe(test[age], cutx2, woex2))test[30-59] = pd.Series(change_woe(test[30-59], cutx3, woex3))test[DebtRatio] = pd.Series(change_woe(test[DebtRatio], cutx4, woex4))test[MonthlyIncome] = pd.Series(change_woe(test[MonthlyIncome], cutx5, woex5))test[Number_Open] = pd.Series(change_woe(test[Number_Open], cutx6, woex6))test[90-] = pd.Series(change_woe(test[90-], cutx7, woex7))test[Number_Estate] = pd.Series(change_woe(test[Number_Estate], cutx8, woex8))test[60-89] = pd.Series(change_woe(test[60-89], cutx9, woex9))test[Dependents] = pd.Series(change_woe(test[Dependents], cutx10, woex10))
刪除對因變數不明顯的變數.剔除五個變數
train_X =train.drop([Number_Estate,Dependents,Number_Open,DebtRatio,MonthlyIncome],axis=1)test_X =test.drop([Number_Estate,Dependents,Number_Open,DebtRatio,MonthlyIncome],axis=1)
5.2Logistc模型建立
在評分卡建模中,我們選擇Logistc回歸模型,對訓練數據進行訓練,對測試數據進行預測
# 統計學計量包#trainfrom sklearn.metrics import roc_curve, aucimport statsmodels.api as smX_train =train_X.drop(Bad,axis =1)y_train =train_X[Bad]y_test = test[Bad]X_train1=sm.add_constant(X_train)logit=sm.Logit(y_train,X_train1)lg=logit.fit()print(lg.summary2())
可知,各變數通過顯著性檢驗,滿足要求。
5.3模型驗證
在Python中,可以利用sklearn.metrics,它能方便比較兩個分類器,自動計算ROC和AUC。
from sklearn.metrics import roc_curve, aucFPR,TPR,threshold =roc_curve(y_test,pre)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])plt.ylim([0, 1])plt.ylabel(TPR)plt.xlabel(FPR)plt.show()
通過ROC曲線和AUC來評估模型的擬合能力,AUC值為0.85,說明該模型的預測效果還是不錯的,正確率較高
6、信用評分卡
接下來,通過評分卡計算公式,導入相應WOE值和係數,計算出評分卡
代碼如下:
個人總分=基礎分+各部分得分B = 20 / math.log(2)A = 600 - B / math.log(20)基礎分base = round(A+B * coe[0], 0)#計算分數函數def compute_score(coe,woe,factor): scores=[] for w in woe: score=round(coe*w*factor,0) scores.append(score) return scoresx1 = compute_score(coe[1], woex1, B)x2 = compute_score(coe[2], woex2, B)x3 = compute_score(coe[3], woex3, B)x7 = compute_score(coe[4], woex7, B)x9 = compute_score(coe[5], woex9, B)
計算示例:計算綜合得分為515
信用卡評分系統:
根據變數來計算分數,實現如下:
def change_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
對測試樣本做之前相同的數據清洗,根據訓練集樣本得到的評分卡,通過自動評分系統可以批量計算信用評分
代碼如下:
test1 = pd.read_csv(origin_test.csv)test2 = pd.DataFrame()test2[x1] = pd.Series(change_score(test1[Percentage], cutx1, x1))test2[x2] = pd.Series(change_score(test1[age], cutx2, x2))test2[x3] = pd.Series(change_score(test1[30-59], cutx3, x3))test2[x7] = pd.Series(change_score(test1[90-], cutx7, x7))test2[x9] = pd.Series(change_score(test1[60-89], cutx9, x9))test2[Score] = test2[x1] + test2[x2] + test2[x3] + test2[x7] +test2[x9] + basetest2.to_csv(ScoreData.csv, index=False)
總結:
本文通過運用python對kaggle上的Give Me Some Credit進行數據分析,根據信用評分卡的建模原理,創建一個簡單的信用評分系統.
本次分析主要精力集中在研究自變數與目標變數的關係,通過ROC與IV從不同角度研究自變數對目標變數的影響力,從而選出對目標變數貢獻最大的變數,不足的地方未對模型穩定性進行評估,不清楚模型穩定性如何。
後續可以進一步優化模型,對模型穩定性進行測試,對模型進行監控
通過評分卡模型訓練及評分卡預測得到了每個用戶的最終信用評分,這個評分可以借鑒到到各種貸款或者金融相關的徵信領域中。
以《大數據時代的商業建模》中一句話作為結束,建模不能脫離商業環境和業務訴求。有時候數學上的最佳答案並不是商業上最佳選擇。
推薦閱讀:
※"微出行"上線 巨量數據下的風控挑戰
※設備風險反欺詐案例介紹
※資產管理中的大數據風控運用
※反欺詐之血緣關係分析和犯罪傳導監測
※基於遷移學習(Transfer learning)的反欺詐(二)