個人貸款違約預測模型練習
重點為分類模型的資料理解與資料準備
資料介紹
-
賬戶表(Accounts):每條記錄描述一個賬戶的靜態資訊
-
顧客資訊表(Clients):每條記錄描述一個客戶的特徵資訊
-
許可權分配表(Disp):每條記錄描述顧客和賬戶之間的關係,以及客戶操作賬戶的許可權
-
支付訂單表(Orders):每條記錄代表一個支付命令
-
交易表(Trans):每條記錄代表每個賬戶上的一條交易
-
貸款表(Loans):每條記錄代表某個賬戶上的一條貸款資訊
-
信用卡(Cards):每條記錄描述一個顧客號的信用卡資訊
-
人口地區統計表(District):每條記錄描述一個地區的人口統計資訊
關係實體圖(E-R圖)可以直觀描述表間關係:
業務分析
在貸款審批方面,可以通過構建量化模型對客戶的信用等級進行一定的區分。在信貸資金管理方面,得知了每個賬戶的違約概率後,可以預估未來的壞賬比例,及時做好資金安排。在這個量化模型中,被解釋變數為二分類變數,因此需要構建一個排序類分類模型。而排序類分類模型中最常用的演算法是邏輯迴歸。
資料理解
建模分析中,需要根據建模的主題進行變數的提取。
第一步維度分析
(1)屬性表徵資訊:在分析個人客戶時,又稱人口統計資訊。這類指標對客戶的行為預測不具有因果關係,只是根據歷史資料統計可得到的一些規律。
(2)行為資訊:行為是內部需求在外部特定環境下的一種表現。
(3)狀態資訊:指客戶的社會經濟狀態和社會網路關係。
(4)利益資訊:如果可以知道客戶的內在需求,當然是最理想的,而這類資料獲取方式匱乏。傳統方式只是通過市場調研、客戶呼入或客戶投訴得到相關資料。現在利用社交媒體的留言資訊可以便捷地獲取客戶評價資訊。
資料整理
在貸款表中還款狀態(status)變數記錄了客戶的貸款償還情況,其中A代表合同終止且正常還款,B代表合同終止但未還款,C代表合同未結束且正常還款,D代表合同未結束但是已經拖欠貸款了。以此構造一個客戶信用評級模型,預測其他客戶貸款違約的概率。
-
資料提取中的取數視窗
取數視窗的長短和模型易用性是一對矛盾體:視窗期越短,缺失值越少,可分析的樣本就越多,越便於使用。但是單個變數的觀測期越短,資料越不穩定;視窗越長,新的客戶就會因為變數缺失而無法納入研究樣本。
同樣,預測視窗取決於構建什麼樣的模型以及目標變數是什麼。比如營銷響應預測視窗取三天到一週,信用卡違約須要一年。 -
匯入資料
import pandas as pd
import numpy as np
import os
os.chdir(r’’)
os.getcwd()
loanfile = os.listdir()
createVar = locals()
for i in loanfile:
if i.endswith("csv"):
createVar[i.split('.')[0]]=pd.read_csv(i,encoding = 'gbk')
print(i.split('.')[0])
bad_good = {'B':1,'D':1,'A':0,'C':2}
loans['bad_good']=loans.status.map(bad_good)
loans.head()
- 表徵資訊
將所有維度的資訊歸結到貸款表上,每個貸款賬戶只有一條記錄。
data2 =pd.merge(loans,disp,on = 'account_id',how ='left')
data2 = pd.merge(data2,clients,on = 'client_id',how = 'left')
- 狀態資訊
提取借款人居住地情況
data3 = pd.merge(data2,district,left_on = 'district_id',right_on = 'A1',how = 'left')
- 行為資訊
根據客戶的賬戶變動的行為資訊,考察借款人還款能力
#將貸款表和交易表按照account_id內連線
data_4temp1=pd.merge(loans[['account_id','date']],
trans[['account_id','type','amount','balance','date']],
on = 'account_id')
data_4temp1.columns = ['account_id','date','type','amount','balance','t_date']
data_4temp1 = data_4temp1.sort_values(by=['account_id','t_date'])
#將來自貸款表和交易表的兩個字串型別日期變數轉換為日期
data_4temp1['date']=pd.to_datetime(data_4temp1['date'])
data_4temp1['t_date']=pd.to_datetime(data_4temp1['t_date'])
#賬戶餘額和交易額度為字元變數,有千分位符,需要進行資料清洗,並轉換為數值型別
data_4temp1['balance2']=data_4temp1['balance'].map(lambda x:int(''.join(x[1:].split(','))))
data_4temp1['amount2']=data_4temp1['amount'].map(lambda x: int(''.join(x[1:].split(','))))
`
#視窗取數,只保留貸款日期前365天至貸款前1天內的交易資料、
import datetime
data_4temp2 = data_4temp1[data_4temp1.date>data_4temp1.t_date][data_4temp1.date<data_4temp1.t_date+datetime.timedelta(days=365)]
#每個貸款賬戶貸款前一年的平均賬戶餘額、賬戶餘額的標準差和變異係數
data_4temp3 = data_4temp2.groupby('account_id')['balance2'].agg([('avg_balance','mean'),('stdev_balance','std')])
data_4temp3['cv_balance'] = data_4temp3[['avg_balance','stdev_balance']].apply(lambda x:x[1]/x[0],axis=1)
type_dict ={'借':'out','貸':'income'}
data_4temp2['type1'] =data_4temp2.type.map(type_dict)
data_4temp4 = data_4temp2.groupby(['account_id','type1'])[['amount2']].sum()
data_4temp5 = pd.pivot_table(data_4temp4,values = 'amount2',index = 'account_id',columns = 'type1')
data_4temp5.fillna(0,inplace = True)
data_4temp5['r_out_in']=data_4temp5[['out','income']].apply(lambda x: x[0]/x[1],axis = 1)
data4 = pd.merge(data3,data_4temp3,left_on = 'account_id',right_index = True,how = 'left')
data4 = pd.merge(data4,data_4temp5,left_on = 'account_id',right_index = True,how = 'left')
data4['r_lb'] = data4[['amount','avg_balance']].apply(lambda x:x[0]/x[1],axis=1)
data4['r_lincome'] = data4[['amount','income']].apply(lambda x: x[0]/x[1],axis=1)
建立分析模型
- 提取狀態為C的樣本用於預測
data_model = data4[data4.status!='C']
for_predict = data4[data4.status=='C']
train = data_model.sample(frac = 0.7, random_state = 1235).copy()
test = data_model[~data_model.index.isin(train.index)].copy()
print("訓練集樣本量:%i \n測試集樣本量:%i" %(len(train),len(test)))
- 使用向前逐步法進行邏輯迴歸建模
import statsmodels.formula.api as smf
import statsmodels.api as sm
def forward_select(data, response):
remaining = set(data.columns)
remaining.remove(response)
selected = []
current_score, best_new_score = float('inf'), float('inf')
while remaining:
aic_with_candidates=[]
for candidate in remaining:
formula = "{} ~ {}".format(
response,' + '.join(selected + [candidate]))
aic = smf.glm(
formula=formula, data=data,
family=sm.families.Binomial(sm.families.links.logit)
).fit().aic
aic_with_candidates.append((aic, candidate))
aic_with_candidates.sort(reverse=True)
best_new_score, best_candidate=aic_with_candidates.pop()
if current_score > best_new_score:
remaining.remove(best_candidate)
selected.append(best_candidate)
current_score = best_new_score
print ('aic is {},continuing!'.format(current_score))
else:
print ('forward selection over!')
break
formula = "{} ~ {} ".format(response,' + '.join(selected))
print('final formula is {}'.format(formula))
model = smf.glm(
formula=formula, data=data,
family=sm.families.Binomial(sm.families.links.logit)
).fit()
return(model)
candidates = ['bad_good','A1','GDP','A4','A10','A11','A12','amount','duration','A13','A14','A15','a16','avg_balance','stdev_balance',
'cv_balance','income','out','r_out_in','r_lb','r_lincome']
data_for_select = train[candidates]
lg_ml= forward_select(data=data_for_select,response='bad_good')
lg_ml.summary().tables[1]
- 模型效果評估
import sklearn.metrics as metrics
import matplotlib.pyplot as plt
fpr , tpr,th = metrics.roc_curve(test.bad_good,lg_ml.predict(test))
plt.figure(figsize=[6,6])
plt.plot(fpr,tpr,'b--')
plt.title('ROC curve')
plt.show()
模型運用
for_predict['prob']=lg_ml.predict(for_predict)
for_predict[['account_id','prob']].head()