數據分析案例(四)——評分卡模型(四)

  • 拖了好久忘記了我的評分卡模型的擬寫。這一次稍微好好寫一下竿报。本文章主要是寫一下評分卡建模的主要流程

一铅乡、建模思路

信用評分卡.jpg

二、數據集介紹

givemesomecredit --Kaggle數據集
數據來自Kaggle的Give Me Some Credit烈菌,有15萬條的樣本數據阵幸,大致情況如下:
數據屬于個人消費類貸款,只考慮信用評分最終實施時能夠使用到的數據應從如下一些方面獲取數據:
基本屬性:包括了借款人當時的年齡芽世。
償債能力:包括了借款人的月收入挚赊、負債比率。
信用往來:兩年內35-59天逾期次數捂襟、兩年內60-89天逾期次數咬腕、兩年內90天或高于90天逾期的次數欢峰。
財產狀況:包括了開放式信貸和貸款數量葬荷、不動產貸款或額度數量。
貸款屬性:暫無纽帖。
其他因素:包括了借款人的家屬數量(不包括本人在內)。
時間窗口:自變量的觀察窗口為過去兩年,因變量表現(xiàn)窗口為未來兩年碗啄。

三淑履、具體步驟與代碼

3.1 數據描述

df.rename(columns = {'SeriousDlqin2yrs':'y'},inplace = True)
df.drop(columns = 'Unnamed: 0',inplace = True) #因為id沒有什么意義,下面還要直接去重
df.info()
結果.png
  • 發(fā)現(xiàn)有缺失值:MonthlyIncome室囊、NumberofDependents
(df['y'].value_counts()[1])/len(df)

0.06684

  • 說明樣本非常不平衡雕崩。如果兩類樣本的比例在1:5以上,則無需做樣本不平衡處理融撞。這里需要做不平衡處理盼铁。

其他的數據描述,例如繪制條形圖尝偎、相關性檢查就不再這里寫出來了饶火。

3.2 數據清洗

3.2.1 去重鹏控、缺失值處理

# 去重
dfana = df.copy()
dfana.drop_duplicates(inplace = True)
dfana.reset_index(inplace = True)
# 缺失值補充
dfana.isnull().mean()
結果2.png
  • 查看缺失值比例
  • 考慮到后面要進行分箱,如果缺失比例>5%,且好樣本率非極端肤寝,不做缺失值填補当辐,直接變成一個分箱;
  • 缺失值比例<=5%,或者好壞樣本率極端(全好全壞樣本)鲤看,選擇隨機填補缘揪。
dfana[dfana['MonthlyIncome'].isnull()].y.value_counts()
image.png
  • 發(fā)現(xiàn)MonthlyIncome非極端,則無需做缺失值填補
# 對NumberOfDependents進行缺失值填補
dfana['NumberOfDependents']=Fillna(dfana['NumberOfDependents'],repval ='random')
  • 對NumberOfDependents進行缺失值進行隨機填補义桂。這里的Fillna函數自行定義寺晌。思路:可以用np.random.choice(array,n,replace = True),注意不要隨機抽到缺失值的了

3.2.2 異常值處理

  • 對于異常值處理澡刹,首先要根據業(yè)務呻征,繪制box箱線圖發(fā)現(xiàn)異常變量,進行剔除或者代替罢浇。這里具體的發(fā)現(xiàn)方法不做描述陆赋。
dfana = dfana[dfana.age > 0]
dfana = dfana[(dfana['NumberOfTime30-59DaysPastDueNotWorse'] < 90)]
dfana.loc[(dfana.RevolvingUtilizationOfUnsecuredLines > 30),'RevolvingUtilizationOfUnsecuredLines'] = 0.5
dfana.loc[(dfana.DebtRatio > 2),'DebtRatio'] = 0.5
  • 通過探索發(fā)現(xiàn)處理了age、NumberOfTime30-59DaysPastDueNotWorse嚷闭、RevolvingUtilizationOfUnsecuredLines攒岛、DebtRatio這幾個變量的異常值。見仁見智胞锰。

3.3 不平衡樣本處理

注意:不平衡樣本處理應該在分箱之前灾锯,因為分箱之后某些信息會缺失,所以按照badrate來平衡樣本嗅榕。

  • 方法有很多顺饮,這里用的是簡單的下采樣的方法。
  • 無需抽樣成1:1凌那,可以抽樣成1:5兼雄,所以這里從正樣本匯總抽樣5倍的壞樣本數即可。
# 采用分層抽樣,1:5的比例進行下采樣
G_train = dfana[dfana.y == 0]
B_train = dfana[dfana.y == 1]
### 對好樣本進行抽樣帽蝶,抽樣個數選擇壞樣本個數的5倍
G_train_sample = G_train.sample(n=B_train.shape[0] * 5, frac=None, replace=False, weights=None, random_state=101, axis=0)
dfana_t = []
dfana_t = pd.concat([G_train_sample,B_train])
dfana_t.reset_index(inplace = True,drop = True)
dfana_t.drop(columns = 'index',inplace = True)

3.4 分箱處理

  • 用最小卡方法(具體的方法查看評分卡模型(二)

    具體的代碼這里不寫出來了赦肋。主要的點:
  1. 生成交叉表,計算badrate:
    dftbl = pd.crosstab(dfana[colname], dfana[target])
    dftbl.reset_index(inplace = True)
    dftbl['badrate'] = dftbl[1] / (dftbl[0] + dftbl[1])
  1. 需要提前處理badrate極端的組別励稳,badrate = 0 or 1,這部分提前與上下組別合并佃乘。
  2. 最好定義一個dataframe記錄需要轉變前的colname,與需要轉變后的colname
# 初始化的轉換表格就是自己本身
dfres = pd.DataFrame({colname : dftbl[colname], 'trans' : dftbl[colname]})
# 找到需要轉換的變量特征驹尼,利用dfres將原來的table轉變一下
dfres.loc[i, 'trans'] = dfres.loc[i - 1, 'trans']
dftbl['anacol'] =  dfres.trans
# 轉變了之后利用groupby再次合并一次
dftbl = dftbl.groupby('anacol', as_index = False, 
                              observed = True).agg('sum')
  1. 計算相鄰兩個單元格的卡方值
for i in range(N_levels - 1): 
        dftbl.loc[i, 'chi2'] = ss.chi2_contingency(dftbl.loc[i : i + 1, [0, 1]])[0] 
  1. 找到最小的卡方值趣避,進行向上或者向下合并,合并了之后還需要重新計算其卡方值
dftbl.loc[minindex, 'chi2'] = ss.chi2_contingency(dftbl.loc[minindex : minindex + 1,
                                                                    [0, 1]])[0]

3.5 WOE\IV值

新增一個計算WOE和IV值的函數扶欣,計算分箱之后的woe值鹅巍,和整體變量的iv值千扶,然后進行反復篩選。
主要代碼:

    dftbl = pd.crosstab(colname.fillna('NA'), target, normalize = 'columns') #normalize是計算target中的各項頻率骆捧!數量/列的總和
    # 也就是goodpct,badpct
    dftbl.columns = ['goodpct', 'badpct']
    dftbl['WOE'] = np.log(dftbl.goodpct / dftbl.badpct) * 100
    IV = sum((dftbl.goodpct - dftbl.badpct) * dftbl.WOE) / 100
  • 經過多次嘗試澎羞,需要把分箱盡量單調,同時合并只能合并最近的箱體(已經排序過了)


    image.png

    image.png
image.png
image.png
image.png
image.png
image.png
image.png
  • 某些特殊的分箱不滿足單調性敛苇,只需要能在業(yè)務上解釋就可以了
image.png
image.png

3.6 切分訓練集和測試集

  • 在所有數值變換之后妆绞,就可以切分數據集和測試集了,一定要保證訓練集和測試集的變換是一樣的
from sklearn.model_selection import train_test_split
train_df, test_df = train_test_split(dfana_fit,test_size = 0.3)
train_df.shape, test_df.shape

3.7 特征選擇(選用)

  • 這里選用的是逐步回歸法枫攀,但是數據集本身的特征就不是很多括饶,所以這里可以不需要用這個方法,同時IV值都已經滿足了要求了来涨。
  • 逐步回歸法就是例如有6個特征图焰,一個model
    f1 + model
    f2 + model
    ...
    f6 + model
    選取其中評判標注最好的一個特征加入模型,例如是f2
    selected = f2
    然后
    f1 +model+selected
    f3 + model + selected
    ...
    依次類推
image.png
  • 發(fā)現(xiàn)剔除了'NumberOfOpenCreditLinesAndLoans_WOE
  • 下面計算VIF值蹦掐,這個值是判斷多重共線的技羔,一般大于10就是有多重共線性了,需要剔除變量
  • 我們發(fā)現(xiàn)所有的系數都為負卧抗,這個是因為我們計算WOE的值的時候是goodrate / badrate藤滥,所以與y關系是負相關,同時我們發(fā)現(xiàn)所有的系數都是一個方向社裆,這樣的模型才效果更好拙绊。
from statsmodels.stats.outliers_influence import variance_inflation_factor
colnames = list(model.params.index)
colnames.remove('Intercept')
train_X_M = np.matrix(train_df[colnames])

VIF_list = [variance_inflation_factor(train_X_M, i) for i in range(train_X_M.shape[1])]
VIF_list

[1.345451226908903,
1.2336645504356645,
1.203776578215364,
1.2737738281637017,
1.1334798511412756,
1.0174816425613178,
1.0462200835734954,
1.0972902825775086,
1.0547503741366757]

  • 結果發(fā)現(xiàn)都在小于2,說明沒有共線性泳秀,無需剔除變量

3.8 評價模型

  • 計算AUC
from sklearn.metrics import auc,roc_curve, roc_auc_score
### 計算AUC值
print('訓練集的auc為:{}'.format(roc_auc_score(train_df.y,train_predict)))
print('測試集的auc為:{}'.format(roc_auc_score(test_df.y, predictions)))

訓練集的auc為:0.8320949945565318
測試集的auc為:0.8307774122803272

  • 說明模型沒有過擬合标沪,在訓練集和測試集的auc都不錯

3.9 轉換成分值

利用公式進行求解:評分卡模型(三)
按照公式求出Factor和offset,當然自己要規(guī)定PDO(odds比增加一倍時候要增加的分數)晶默,β是建模的系數谨娜。

    factor = pdo/np.log(2)
    offset = basescore - factor * np.log(baseodds)
    dfres['cardscore'] = round(offset / n - factor * (para.Intercept / n + dfres.beta * dfres.WOE))
  • 注意,這里先把每一個箱體的分數計算出來磺陡,再計算每一個id的總分數,總分數就是每一個箱體相加
分數特征.png
  • 最后計算的分數特征是漠畜,577為分數均值币他,最高分為690,最低分為92.

3.10 KS指標

  • 橫坐標是分數憔狞,縱坐標是壞人/好人的累計占比


    KS指標.png
  • 最后繪制兩條曲線蝴悉,KS為兩條曲線的差值。發(fā)現(xiàn)最大的差值是在572分的時候KS為51分左右瘾敢,說明在572的時候好壞的占比大約占一半拍冠。
  • KS值一般大于40尿这,就是可以用的模型。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末庆杜,一起剝皮案震驚了整個濱河市射众,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晃财,老刑警劉巖叨橱,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異断盛,居然都是意外死亡,警方通過查閱死者的電腦和手機钢猛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門伙菜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人命迈,你說我怎么就攤上這事仇让。” “怎么了躺翻?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵丧叽,是天一觀的道長。 經常有香客問我公你,道長踊淳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任陕靠,我火速辦了婚禮迂尝,結果婚禮上,老公的妹妹穿的比我還像新娘剪芥。我一直安慰自己垄开,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布税肪。 她就那樣靜靜地躺著溉躲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪益兄。 梳的紋絲不亂的頭發(fā)上锻梳,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音净捅,去河邊找鬼疑枯。 笑死,一個胖子當著我的面吹牛蛔六,可吹牛的內容都是我干的荆永。 我是一名探鬼主播废亭,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼具钥!你這毒婦竟也來了豆村?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤氓拼,失蹤者是張志新(化名)和其女友劉穎你画,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體桃漾,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡坏匪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撬统。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片适滓。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恋追,靈堂內的尸體忽然破棺而出凭迹,到底是詐尸還是另有隱情,我是刑警寧澤苦囱,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布嗅绸,位于F島的核電站,受9級特大地震影響撕彤,放射性物質發(fā)生泄漏鱼鸠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一羹铅、第九天 我趴在偏房一處隱蔽的房頂上張望蚀狰。 院中可真熱鬧,春花似錦职员、人聲如沸麻蹋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扮授。三九已至,卻和暖如春蛛蒙,著一層夾襖步出監(jiān)牢的瞬間糙箍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工牵祟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抖格。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓诺苹,卻偏偏與公主長得像咕晋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子收奔,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344