申請(qǐng)?jiān)u分卡(3)——建模(R)

理論說(shuō)完了,來(lái)次實(shí)踐。

數(shù)據(jù)理解與預(yù)處理

數(shù)據(jù)來(lái)自kaggle的Give Me Some Credit項(xiàng)目,有15萬(wàn)條的樣本數(shù)據(jù)。
要求根據(jù)歷史數(shù)據(jù),預(yù)測(cè)申請(qǐng)人違約的可能性,以此作為放貸依據(jù)。

#數(shù)據(jù)加載,也可以直接放含有絕對(duì)路徑的文件名
setwd('D:\\anajpnotebook\\GiveMeSomeCredit\\data')
train <- read.csv('cs-training.csv')
View(train)

首先我們看下業(yè)務(wù)含義。
數(shù)據(jù)集一共11列,分別是:
SeriousDlqin2yrs:貸款人會(huì)逾期90天以上,取值Y/N,對(duì)應(yīng)1和0,這個(gè)是目標(biāo)變量,也就是貸款人會(huì)不會(huì)違約。
age:年齡,一般具備獨(dú)立經(jīng)濟(jì)能力的申請(qǐng)人償還債務(wù)的可能性較高,而且因?yàn)槠渌蛩氐挠绊?,原則上不會(huì)發(fā)放貸款給未成年人,一般國(guó)內(nèi)是這樣,國(guó)外還要看具體數(shù)據(jù)。
DebtRatio:負(fù)債率,如果本身要償還的債務(wù)比較高,在月收入一定的情況下,違約的可能性就可能就比較高。
MonthlyIncome:月收入,還貸的直接來(lái)源之一,是衡量還貸能力的指標(biāo),影響是否違約的重要因素。
RevolvingUtilizationOfUnsecuredLines:信用總余額(剔除房產(chǎn)貸款和分期付款)/信用額度,和DebtRatio異曲同工,這個(gè)指標(biāo)越低,違約的風(fēng)險(xiǎn)可能就越大。
NumberOfOpenCreditLinesAndLoans:開放式貸款數(shù)量(和信用額度),歷史貸款記錄,可能有正相關(guān)關(guān)系,如果歷史有很多次貸款且沒(méi)有違約記錄,一定程度上說(shuō)明該申請(qǐng)人信用習(xí)慣良好,申請(qǐng)人騙貸的情形除外。
NumberRealEstateLoansOrLines:抵押貸款和房地產(chǎn)貸款數(shù)量(包括房屋信貸額度)。NumberOfTime30.59DaysPastDueNotWorse:逾期30-59天的次數(shù),近2年內(nèi)沒(méi)有更糟的記錄,這個(gè)系列的指標(biāo)對(duì)應(yīng)國(guó)內(nèi)的M1,M2,M3,基本類似。也就是不同程度的不良記錄數(shù)。
NumberOfTime60.89DaysPastDueNotWorse:逾期60-89天的次數(shù),近2年內(nèi)沒(méi)有更糟的記錄。
NumberOfTimes90DaysLate:逾期90天以上的次數(shù)。
NumberOfDependents:家屬個(gè)數(shù),不含自身。

接下來(lái)看看數(shù)據(jù)的取值和分布情況:



先看缺失值情況,MonthlyIncome:29731 NumberOfDependents:3924。
以上2項(xiàng)含有缺失值,NumberOfDependents缺失值較少,占比約2.6%,可以考慮去掉含缺失值的樣本,也可以用中位數(shù)填充。MonthlyIncome缺失值占比中等,需要填充或剔除該項(xiàng)屬性,具體要看數(shù)據(jù)的分布和數(shù)據(jù)間的關(guān)系。
我們先來(lái)看看變量之間的相關(guān)關(guān)系:

> cor(train)

變量之間沒(méi)有顯著的相關(guān)關(guān)系。所以不能憑借相關(guān)關(guān)系來(lái)填充數(shù)據(jù)了。
缺失值處理比較省事的一種方式是用中位數(shù)或平均數(shù)來(lái)填充,由2個(gè)變量的統(tǒng)計(jì)描述指標(biāo)看,用中位數(shù)比較合適。當(dāng)然也可以建模來(lái)填充,比如聚類、回歸或者隨機(jī)森林。這里我們先用中位數(shù)填充。
由于缺乏相關(guān)業(yè)務(wù)信息,這里只對(duì)明顯不符合常識(shí)的幾個(gè)變量中的異常值做處理。
首先是年齡,年齡的最小值是0,樣本數(shù)量只有1個(gè),予以剔除。

> train<-train[-which(train$age==0),] 

接著是信用往來(lái)相關(guān)的變量,取值98、96的樣本共269個(gè),其中好樣本和壞樣本比例基本持平,且占總樣本比例較小,予以剔除。

> train<-train[-which(train$NumberOfTime30.59DaysPastDueNotWorse==98),] 
> train<-train[-which(train$NumberOfTime30.59DaysPastDueNotWorse==96),] 

其他變量雖也存在異常值,不過(guò)信息不夠,暫時(shí)不能剔除。
下面看看目標(biāo)變量的分布情況。

> table(train$SeriousDlqin2yrs)

     0      1 
136229   9847 

可以看到,違約樣本約占總樣本的1/15,比例失衡,解決這個(gè)問(wèn)題有2種方法:在“0”這一部分抽取與“1”這邊相當(dāng)?shù)臄?shù)據(jù),也就是下采樣,另外一種就是增加“1”的樣本數(shù)量與“0”基本持平,也就是過(guò)采樣。下采樣之后數(shù)據(jù)實(shí)在有點(diǎn)少,這里我們采用過(guò)采樣中常用的SMOTE算法。

> set.seed(1206) 
> splitIndex<-createDataPartition(train$SeriousDlqin2yrs,time=1, p=0.5,list=FALSE) 
> ttrain<-train[splitIndex,] 
> ttest<-train[-splitIndex,] 
> prop.table(table(ttrain$SeriousDlqin2yrs))

         0          1 
0.93432178 0.06567822 
> prop.table(table(ttest$SeriousDlqin2yrs))

        0         1 
0.9337207 0.0662793 

訓(xùn)練集和測(cè)試集各一半,兩者的分類結(jié)果是平衡的,壞樣本比例保持在6.6%左右,我們可以用這個(gè)來(lái)建模。

邏輯回歸

邏輯回歸是信用評(píng)分卡的核心之一。由于其本身的特點(diǎn),引入WOE后,邏輯回歸的結(jié)果可以直接轉(zhuǎn)換成標(biāo)準(zhǔn)評(píng)分卡格式。
首先我們用全變量來(lái)回歸試試。

> fit<-glm(SeriousDlqin2yrs~.,ttrain,family = "binomial")
> summary(fit)
image.png

有3個(gè)變量未通過(guò)檢驗(yàn)。去掉這3個(gè)變量之后再進(jìn)行一次回歸。

> fit2<-glm(SeriousDlqin2yrs~age+NumberOfTime30.59DaysPastDueNotWorse+MonthlyIncome+NumberOfTimes90DaysLate+NumberRealEstateLoansOrLines+NumberOfTime60.89DaysPastDueNotWorse+NumberOfDependents,ttrain,family = "binomial")
> summary(fit2)

AIC更小了,暫且就先用這些變量來(lái)建模。

模型評(píng)估

評(píng)價(jià)邏輯回歸模型我們主要看ROC和AUC指標(biāo)。


由圖中我們可以看到FPR=0.850,TPR=0.636,AUC=0.812,準(zhǔn)確率還不錯(cuò)?;氐綄?shí)際意義,TPR越高,本金損失風(fēng)險(xiǎn)越低;FPR越高,盈利可能性越高;具體比重要看利率和銷售策略。

WOE轉(zhuǎn)換

引入WOE的目的是為了精簡(jiǎn)特征。用WOE(x)替換變量x。

  1. 變量處理
> bin_age<- c(-Inf,30,35,40,45,50,55,60,65,75,Inf)
> plot(cut(ttrain$age,bin_age))
bin_m2<-c(-Inf,0,1,3,5,Inf)
plot(cut(ttrain$NumberOfTime30.59DaysPastDueNotWorse,bin_m2))
> bin_m3<-c(-Inf,0,1,3,5,Inf)
> plot(cut(ttrain$NumberOfTime60.89DaysPastDueNotWorse,bin_m3))
> bin_m3_<-c(-Inf,0,1,3,5,Inf)
> plot(cut(ttrain$NumberOfTimes90DaysLate,bin_m3_))
> bin_rs<-c(-Inf,0,1,3,5,Inf)
> plot(cut(ttrain$NumberRealEstateLoansOrLines,bin_rs))
> bin_mincome<-c(-Inf,2000,4000,6000,8000,10000,12000)
> plot(cut(ttrain$MonthlyIncome,bin_mincome))
> bin_d<-c(-Inf,0,1,2,3,4,5,Inf)
> plot(cut(ttrain$NumberOfDependents,bin_d))
  1. 變量變換
    首先計(jì)算WOE,WOE()=ln[(違約/總違約)/(正常/總正常)]。
#計(jì)算WOE
totalgood = as.numeric(table(ttrain$SeriousDlqin2yrs))[1]
totalbad = as.numeric(table(ttrain$SeriousDlqin2yrs))[2]
getWOE <- function(a,p,q)
{
  Good <- as.numeric(table(ttrain$SeriousDlqin2yrs[a > p & a <= q]))[1]
  Bad <- as.numeric(table(ttrain$SeriousDlqin2yrs[a > p & a <= q]))[2]
  WOE <- log((Bad/totalbad)/(Good/totalgood),base = exp(1))
  return(WOE)
}

接著進(jìn)行數(shù)值替換

#age
age.WOE=c(getWOE(ttrain$age,-Inf,30),getWOE(ttrain$age,30,35),getWOE(ttrain$age,35,40),getWOE(ttrain$age,40,45),getWOE(ttrain$age,45,50),getWOE(ttrain$age,50,55),
          getWOE(ttrain$age,55,60),getWOE(ttrain$age,60,65),getWOE(ttrain$age,65,75),getWOE(ttrain$age,75,Inf))

tmp.age <- 0
for(i in 1:nrow(ttrain)) {
  if(ttrain$age[i] <= 30)
    tmp.age[i] <- Agelessthan30.WOE
  else if(ttrain$age[i] <= 35)
    tmp.age[i] <- Age30to35.WOE
  else if(ttrain$age[i] <= 40)
    tmp.age[i] <- Age35to40.WOE
  else if(ttrain$age[i] <= 45)
    tmp.age[i] <- Age40to45.WOE
  else if(ttrain$age[i] <= 50)
    tmp.age[i] <- Age45to50.WOE
  else if(ttrain$age[i] <= 55)
    tmp.age[i] <- Age50to55.WOE
  else if(ttrain$age[i] <= 60)
    tmp.age[i] <- Age55to60.WOE
  else if(ttrain$age[i] <= 65)
    tmp.age[i] <- Age60to65.WOE
  else if(ttrain$age[i] <= 75)
    tmp.age[i] <- Age65to75.WOE
  else
    tmp.age[i] <- Agemorethan.WOE
}

其他變量做同樣的處理。

建模并制作評(píng)分卡

替換完成后進(jìn)行邏輯回歸建模。

#WOE DataFrame構(gòu)建
trainWOE =cbind.data.frame(tmp.age,tmp.m2,tmp.mincome,tmp.m3,tmp.m3_,tmp.rs,tmp.d)
#建模
trainWOE$SeriousDlqin2yrs = ttrain$SeriousDlqin2yrs
glm.fit = glm(SeriousDlqin2yrs~.,data = trainWOE,family = binomial(link = logit))
summary(glm.fit)
coe = (glm.fit$coefficients)

此處我們得到了計(jì)算分?jǐn)?shù)需要用到的系數(shù),下面來(lái)構(gòu)建評(píng)分卡。


#構(gòu)建評(píng)分卡,score=p-q*log(odds),odds=p(bad)/p(godd),比例越高,分?jǐn)?shù)越低。如果是好壞比,則中間變成加號(hào)。
p <- 20/log(2)
q <- 600-20*log(15)/log(2)
getscore<-function(i,x){
  score = round(p*as.numeric(coe[i])*x,0)
  return(score)
}
#基礎(chǔ)分計(jì)算
base <- q + p*as.numeric(coe[1])
base
#[1] 446.0211
#計(jì)算每個(gè)分箱的得分
Age.SCORE = c(getscore(2,age.WOE[1]),getscore(2,age.WOE[2]),getscore(2,age.WOE[3]),getscore(2,age.WOE[4]),getscore(2,age.WOE[5]),
              getscore(2,age.WOE[6]),getscore(2,age.WOE[7]),getscore(2,age.WOE[8]),getscore(2,age.WOE[9]),getscore(2,age.WOE[10]))
Age.SCORE
# [1]  12  11   7   6   5   2  -6 -13 -20 -28
m2.SCORE=c(getscore(3,m2.WOE[1]),getscore(3,m2.WOE[2]),getscore(3,m2.WOE[3]),getscore(3,m2.WOE[4]),getscore(3,m2.WOE[5]))
m2.SCORE
#[1] -10  17  34  45  50
m3.SCORE=c(getscore(5,m3.WOE[1]),getscore(5,m3.WOE[2]),getscore(5,m3.WOE[3]),getscore(5,m3.WOE[4]),getscore(3,m3.WOE[5]))
m3.SCORE
#[1] -4 26 38 40 55
m3_.SCORE=c(getscore(6,m3_.WOE[1]),getscore(6,m3_.WOE[2]),getscore(6,m3_.WOE[3]),getscore(6,m3_.WOE[4]),getscore(3,m3_.WOE[5]))
m3_.SCORE
#[1] -7 38 51 62 63
mincome.SCORE=c(getscore(4,mincome.WOE[1]),getscore(4,mincome.WOE[2]),getscore(4,mincome.WOE[3]),getscore(4,mincome.WOE[4]),
                getscore(4,mincome.WOE[5]),getscore(4,mincome.WOE[6]),getscore(4,mincome.WOE[7]),getscore(4,mincome.WOE[8]),
                getscore(4,mincome.WOE[9]),getscore(4,mincome.WOE[10]),getscore(4,mincome.WOE[11]))
mincome.SCORE
# [1] -6  8  7  5  2 -2 -2 -3 -5 -7 -7
rs.SCORE=c(getscore(7,rs.WOE[1]),getscore(7,rs.WOE[2]),getscore(7,rs.WOE[3]),getscore(7,rs.WOE[4]),getscore(7,rs.WOE[5]))
rs.SCORE
#[1]  5 -6 -3  9 24
d.SCORE=c(getscore(8,d.WOE[1]),getscore(8,d.WOE[2]),getscore(8,d.WOE[3]),getscore(8,d.WOE[4]),
          getscore(8,d.WOE[5]),getscore(8,d.WOE[6]),getscore(8,d.WOE[7]))
d.SCORE
#[1] -2  2  4  3  6  3  8

將這些分?jǐn)?shù)合并成一張?jiān)u分表,初步的評(píng)分卡就做好了。



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容