Kaggle: Home Credit Default Risk(房屋信貸違約風(fēng)險(xiǎn)預(yù)測(cè))

項(xiàng)目背景:

1顾复、目的:通過數(shù)據(jù)集提供的相關(guān)數(shù)據(jù)班挖,預(yù)測(cè)客戶貸款是否違約
2、數(shù)據(jù)集介紹:一共有8個(gè)數(shù)據(jù)集芯砸,包括1個(gè)主訓(xùn)練集萧芙,1個(gè)測(cè)試集和6個(gè)輔助信息表,主訓(xùn)練集特征主要有用戶的個(gè)人屬性乙嘀,包括用戶的性別末购,職業(yè),是否有車虎谢,是否有房盟榴,房子面積等基本信息,輔助信息表包括用戶的歷史申請(qǐng)信息婴噩,歷史賬戶余額信息擎场,分期付款信息,信用卡信息几莽,信用局信息和在信用局上的額度信息迅办。

一、數(shù)據(jù)集探索

通過項(xiàng)目所提供的數(shù)據(jù)集章蚣,整理出了如下數(shù)據(jù)集的關(guān)系圖站欺,從圖中我們可以看到唯一主鍵是SK_ID_CURR

數(shù)據(jù)集關(guān)系圖.png
1、了解數(shù)據(jù)集中主要指標(biāo)
2、指標(biāo)評(píng)估

本項(xiàng)目對(duì)用戶貸款是否違約進(jìn)行違約的預(yù)測(cè)是一個(gè)二分類問題矾策,因此評(píng)估指標(biāo)為AUC磷账。
ROC曲線體現(xiàn)了“一般情況下”泛化性能的好壞。
若一個(gè)學(xué)習(xí)器的ROC曲線被另一個(gè)學(xué)習(xí)器的曲線完全“包住”贾虽,則可斷言后者的性能優(yōu)于前者逃糟。若兩個(gè)學(xué)習(xí)器的ROC曲線發(fā)生交叉,則比較ROC曲線下的面積蓬豁,即AUC绰咽。


ROC曲線.png

如圖所示,藍(lán)色模型優(yōu)于紅色模型地粪,紅色模型優(yōu)于黑色對(duì)角線取募,黑色對(duì)角線表示幼稚的隨機(jī)猜測(cè)模型。而AUC表示的是在ROC曲線下的面積蟆技,在這個(gè)指標(biāo)中矛辕,更加細(xì)化比較了各個(gè)學(xué)習(xí)器的得分。

二付魔、主訓(xùn)練集探索

主訓(xùn)練集數(shù)據(jù):application_train.csv
探索目的:
1聊品、了解數(shù)據(jù)的缺失情況,異常值情況几苍、以便做對(duì)應(yīng)的數(shù)據(jù)清洗翻屈;
2、了解一下違約貸款和正常貸款用戶畫像的區(qū)別妻坝,加深對(duì)業(yè)務(wù)的理解伸眶,為后續(xù)數(shù)據(jù)分析(特征工程)展開打基礎(chǔ)

首先,導(dǎo)入一些所需要的工具包

import numpy as np # fundamental package for scientific computing with Python
import matplotlib
import matplotlib.pyplot as plt # for plotting
import seaborn as sns # for making plots with seaborn
color = sns.color_palette()
import plotly.offline as py
py.init_notebook_mode(connected=True)
from plotly.offline import init_notebook_mode, iplot
init_notebook_mode(connected=True)
import plotly.graph_objs as go
import pandas as pd
import plotly.offline as offline
offline.init_notebook_mode()

import cufflinks as cf
cf.go_offline()
plt.style.use('fivethirtyeight')
app_train=pd.read_csv('application_train.csv')
app_test=pd.read_csv('application_test.csv')
app_train.head()

1刽宪、異常值檢測(cè)

#定義缺失值檢測(cè)函數(shù)
def missing_values_table(df):
        # 總?cè)笔е?        mis_val = df.isnull().sum()
        
        # 計(jì)算缺失值百分比
        mis_val_percent = 100 * df.isnull().sum() / len(df)
        
        # 合并缺失值數(shù)量和占比
        mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1)
        
        # 重命名列
        mis_val_table_ren_columns = mis_val_table.rename(
        columns = {0 : 'Missing Values', 1 : '% of Total Values'})
        
        # 對(duì)存在缺失值的特征按百分比的大小降序排序
        mis_val_table_ren_columns = mis_val_table_ren_columns[
            mis_val_table_ren_columns.iloc[:,1] != 0].sort_values(
        '% of Total Values', ascending=False).round(1)
        
        # 輸出結(jié)果
        print ("Your selected dataframe has " + str(df.shape[1]) + " columns.\n"      
            "There are " + str(mis_val_table_ren_columns.shape[0]) +
              " columns that have missing values.")
        
        # 返回?cái)?shù)組中的缺失值
        return mis_val_table_ren_columns
missing_values=missing_values_table(app_train)
missing_values.head(20)

根據(jù)以上結(jié)果顯示:數(shù)據(jù)一共有122列厘贼,有67列存在缺失情況,最高缺失值的列缺失度為69.9%圣拄。

2嘴秸、異常值探索

這里主要采用描述統(tǒng)計(jì)方法,即查看特征的均值庇谆、極大值岳掐、極小值等信息判斷是否有異常值

#用戶年齡的分布情況
(app_train['DAYS_BIRTH']/-365).describe()

根據(jù)用戶年齡的數(shù)據(jù)分布情況,發(fā)現(xiàn)數(shù)據(jù)的分布還是比較正常的饭耳,最大年齡69串述,最小年齡20,沒有很異常的數(shù)字

#用戶工作時(shí)間的分布情況
(app_train['DAYS_EMPLOYED']/-365).describe()
app_train['DAYS_EMPLOYED'].plot.hist(title='Days Employment Histogram')

通過用戶工作時(shí)間的數(shù)據(jù)分布情況寞肖,發(fā)現(xiàn)所有的異常值都是365243纲酗,對(duì)于這個(gè)異常值可以理解為缺失值衰腌,所以我選擇用空值0替換,從而保留這個(gè)信息觅赊,替換后看一下工作時(shí)間的分布情況

app_train['DAYS_EMPLOYED'].replace({365243: np.nan}, inplace=True)
(app_train['DAYS_EMPLOYED']/-365).plot.hist(title='Days Employment Histgram')
plt.xlabel('Days Employment');

3桶唐、違約用戶畫像探索

目的:查看違約用戶和非違約用戶的特征分布情況

#繪圖函數(shù),通過圖形可以直觀地看到數(shù)據(jù)的分布情況
def plot_stats(feature,label_rotation=False,horizontal_layout=True):
    temp = app_train[feature].value_counts()
    df1 = pd.DataFrame({feature: temp.index,'Number of contracts': temp.values})

    # 計(jì)算違約用戶(TRAGRT=1)的百分比
    cat_perc = app_train[[feature, 'TARGET']].groupby([feature],as_index=False).mean()
    cat_perc.sort_values(by='TARGET', ascending=False, inplace=True)
    
    if(horizontal_layout):
        fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12,6))
    else:
        fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(12,14))
    sns.set_color_codes("pastel")
    s = sns.barplot(ax=ax1, x = feature, y="Number of contracts",data=df1)
    if(label_rotation):
        s.set_xticklabels(s.get_xticklabels(),rotation=90)
    
    s = sns.barplot(ax=ax2, x = feature, y='TARGET', order=cat_perc[feature], data=cat_perc)
    if(label_rotation):
        s.set_xticklabels(s.get_xticklabels(),rotation=90)
    plt.ylabel('Percent of target with value 1 [%]', fontsize=10)
    plt.tick_params(axis='both', which='major', labelsize=10)

    plt.show();
    
def plot_distribution(var):
    
    i = 0
    t1 = app_train.loc[app_train['TARGET'] != 0]
    t0 = app_train.loc[app_train['TARGET'] == 0]

    sns.set_style('whitegrid')
    plt.figure()
    fig, ax = plt.subplots(2,2,figsize=(12,12))

    for feature in var:
        i += 1
        plt.subplot(2,2,i)
        sns.kdeplot(t1[feature], bw=0.5,label="TARGET = 1")
        sns.kdeplot(t0[feature], bw=0.5,label="TARGET = 0")
        plt.ylabel('Density plot', fontsize=12)
        plt.xlabel(feature, fontsize=12)
        locs, labels = plt.xticks()
        plt.tick_params(axis='both', which='major', labelsize=12)
    plt.show();
3.1 男性用戶和女性用戶的違約率情況
plot_stats('CODE_GENDER')

通過上圖可以發(fā)現(xiàn):男性用戶違約率更高茉兰,男性違約率為10%, 女性違約率為7%

3.2 違約用戶和正常用戶年齡分布情況

由于年齡是連續(xù)型變量欣簇, 所以用KDE分布圖查看年齡的分布情況

plt.figure(figsize=(10, 8))

#及時(shí)還款用戶的KDE圖
sns.kdeplot(app_train.loc[app_train['TARGET']==0, 'DAYS_BIRTH']/-365, label='target==0')

#未及時(shí)還款用戶的KDE圖
sns.kdeplot(app_train.loc[app_train['TARGET']==1, 'DAYS_BIRTH']/-365, label='target==1')

#圖的標(biāo)簽
plt.xlabel('Age(years)');
plt.ylabel('Density');
plt.title('Distribution of Ages');

通過數(shù)據(jù)分布圖可以看出:
TARGET=0规脸,藍(lán)色曲線,代表按時(shí)還款的年齡集中在30-60之間熊咽,平均年齡偏高
TARGET=1莫鸭,紅色曲線,代表違約用戶在30之后隨著年齡的增長横殴,違約的情況在遞減
由此可以推斷:用戶年齡越小被因,違約的可能性越大

接下來,對(duì)用戶的年齡進(jìn)行分割衫仑,進(jìn)一步分析不同年齡用戶的違約概率

#計(jì)算不同年齡段梨与,違約用戶的概率
age_data=app_train[['TARGET', 'DAYS_BIRTH']]
age_data['YEARS_BIRTH']=age_data['DAYS_BIRTH']/-365

#將年齡進(jìn)行分割,計(jì)算平均值
age_data['YEARS_BINNED']=pd.cut(age_data['YEARS_BIRTH'], bins=np.linspace(20, 70, num=11))
age_groups=age_data.groupby('YEARS_BINNED').mean()
plt.figure(figsize=(8,8))

#繪制條形圖
plt.bar(age_groups.index.astype(str), 100*age_groups['TARGET'])
plt.xticks(rotation=75);
plt.xlabel('Age Group (years)');
plt.ylabel('Failure to Repay(%)')
plt.title('Failure to Repay by Age Group')

從圖中可以看出文狱,在(20.0粥鞋,25.0]之間的人群違約比例較高,之后隨著年齡的增長瞄崇,客戶按時(shí)還款的比例有所增加呻粹。由此可以發(fā)現(xiàn),用戶年齡越小苏研,違約的可能性越高

3.3 不同貸款類型的違約情況
plot_stats('NAME_CONTRACT_TYPE')

從圖中可以看出等浊,對(duì)于現(xiàn)金貸款和流動(dòng)資金循環(huán)貸款,現(xiàn)金貸款的違約率更高

3.4 用戶有無房車對(duì)違約率的影響
#有沒有車對(duì)違約率的影響
plot_stats('FLAG_OWN_CAR')
#有沒有房對(duì)違約率的影響
plot_stats('FLAG_OWN_REALTY')



從圖中可以看出摹蘑,沒有車和房的人違約率更高筹燕,但相差不是很大

3.5 用戶婚姻情況對(duì)違約率的影響
#看家庭結(jié)婚情況對(duì)違約率的影響
plot_stats('NAME_FAMILY_STATUS', True, True)

從家庭情況來看,申請(qǐng)的用戶大多已經(jīng)結(jié)婚衅鹿,單身和世俗結(jié)婚的違約率較高庄萎,寡居的違約率最低

3.6 用戶子女情況對(duì)違約率的影響
#看子女情況
plot_stats('CNT_CHILDREN')

從圖中可以看出,大部分申請(qǐng)者沒有孩子或孩子在3個(gè)以下塘安,孩子越多的家庭違約率越高糠涛,發(fā)現(xiàn)對(duì)于9助赞、11個(gè)孩子的家庭違約率達(dá)到了100%

3.7 用戶收入類型對(duì)違約率的影響
#看申請(qǐng)者的收入類型
plot_stats('NAME_INCOME_TYPE', False, False)

根據(jù)申請(qǐng)者的收入類型區(qū)分欧聘,可以發(fā)現(xiàn)休產(chǎn)假的和沒有工作的人違約率較高,在35%以上竖幔,對(duì)于這兩類人群貸款需要謹(jǐn)慎

3.8 用戶職業(yè)類型對(duì)違約率的影響
#職業(yè)類型
plot_stats('OCCUPATION_TYPE', True, False)

從職業(yè)來看,收入較低或不穩(wěn)定的職業(yè)的違約率越高砸脊,比如低廉勞動(dòng)力具篇、司機(jī)、理發(fā)師凌埂,而像會(huì)計(jì)驱显、高科技員工等具有穩(wěn)定高收入的職業(yè)違約率較低。

3.9 用戶受教育程度對(duì)違約率的影響
#貸款人受教育程度
plot_stats('NAME_EDUCATION_TYPE', True)

貸款申請(qǐng)人受教育程度大多數(shù)為中學(xué)瞳抓,學(xué)歷越低越容易違約

三埃疫、特征工程

通過對(duì)特征的一些理解,嘗試做出一些新的特征

  1. CREDIT_INCOME_PERCENT: 貸款金額/客戶收入孩哑,預(yù)期是這個(gè)比值越大栓霜,說明貸款金額大于用戶的收入,用戶違約的可能性就越大

  2. ANNUITY_INCOME_PERCENT: 貸款的每年還款金額/客戶收入横蜒,同上胳蛮,該比值越大用戶還款的壓力越大,違約的可能性越大

  3. CREDIT_TERM: 貸款金額/貸款的每年還款金額丛晌,貸款的還款周期仅炊,猜測(cè)還款周期短的貸款,用戶的短期壓力可能會(huì)比較大澎蛛,違約概率高

  4. DAYS_EMPLOYED_PERCENT: 用戶工作時(shí)間/用戶年齡茂洒,工作時(shí)間占年齡的百分比,預(yù)計(jì)越小違約概率越高

  5. INCOME_PER_CHILD:客戶收入/孩子數(shù)量瓶竭,客戶的收入平均到每個(gè)孩子身上督勺,同樣的收入,如果這個(gè)人的家庭很大斤贰,孩子很多智哀,那么他的負(fù)擔(dān)可能比較重,違約的可能性可能更高

  6. HAS_HOUSE_INFORMATION : 根據(jù)客戶是否有缺失房屋信息設(shè)計(jì)一個(gè)二分類特征荧恍,如果未缺失的話是1瓷叫,缺失的是0

app_train_domain = app_train.copy()
app_test_domain = app_test.copy()

app_train_domain['CREDIT_INCOME_PERCENT'] = app_train_domain['AMT_CREDIT'] / app_train_domain['AMT_INCOME_TOTAL']
app_train_domain['ANNUITY_INCOME_PERCENT'] = app_train_domain['AMT_ANNUITY'] / app_train_domain['AMT_INCOME_TOTAL']
app_train_domain['CREDIT_TERM'] = app_train_domain['AMT_ANNUITY'] / app_train_domain['AMT_CREDIT']
app_train_domain['DAYS_EMPLOYED_PERCENT'] = app_train_domain['DAYS_EMPLOYED'] / app_train_domain['DAYS_BIRTH']
app_train_domain['INCOME_PER_CHILD'] = app_train_domain['AMT_INCOME_TOTAL'] / app_train_domain['CNT_CHILDREN']
app_train_domain['HAS_HOUSE_INFORMATION'] = app_train_domain['COMMONAREA_MEDI'].apply(lambda x:1 if x>0 else 0)
#通過圖形查看以下特征效果
plt.figure(figsize = (12, 20))
# 對(duì)新特征進(jìn)行迭代
for i, feature in enumerate(['CREDIT_INCOME_PERCENT', 'ANNUITY_INCOME_PERCENT', 'CREDIT_TERM', 'DAYS_EMPLOYED_PERCENT','INCOME_PER_CHILD']):
    plt.subplot(5, 1, i + 1)
    # 按時(shí)還貸用戶直方圖
    sns.kdeplot(app_train_domain.loc[app_train_domain['TARGET'] == 0, feature], label = 'target == 0')
    #  未按時(shí)還貸用戶直方圖
    sns.kdeplot(app_train_domain.loc[app_train_domain['TARGET'] == 1, feature], label = 'target == 1')
    
    plt.title('Distribution of %s by Target Value' % feature)
    plt.xlabel('%s' % feature); plt.ylabel('Density'); 
plt.tight_layout(h_pad = 2.5)

通過設(shè)計(jì)出來的連續(xù)新特征,可以看出它們?cè)谶`約用戶和非違約用戶中的分布情況送巡,可以發(fā)現(xiàn)出CREDIT_TERM這個(gè)特征外摹菠,其他的特征區(qū)分度都不明顯,接下來放到模型中再來看一下效果

plot_stats('HAS_HOUSE_INFORMATION', True)

通過缺失值設(shè)計(jì)的這個(gè)特征骗爆,從上圖可以看出次氨,缺失房屋信息的用戶違約概率要明顯高于未缺失用戶,在模型的預(yù)測(cè)中可以算是一個(gè)比較有效的特征了

對(duì)測(cè)試集application_test.csv做同樣的處理

app_test_domain['CREDIT_INCOME_PERCENT'] = app_test_domain['AMT_CREDIT'] / app_test_domain['AMT_INCOME_TOTAL']
app_test_domain['ANNUITY_INCOME_PERCENT'] = app_test_domain['AMT_ANNUITY'] / app_test_domain['AMT_INCOME_TOTAL']
app_test_domain['CREDIT_TERM'] = app_test_domain['AMT_ANNUITY'] / app_test_domain['AMT_CREDIT']
app_test_domain['DAYS_EMPLOYED_PERCENT'] = app_test_domain['DAYS_EMPLOYED'] / app_test_domain['DAYS_BIRTH']
app_test_domain['INCOME_PER_CHILD'] = app_test_domain['AMT_INCOME_TOTAL'] / app_test_domain['CNT_CHILDREN']
app_test_domain['HAS_HOUSE_INFORMATION'] = app_test_domain['COMMONAREA_MEDI'].apply(lambda x:1 if x>0 else 0)

四摘投、建模預(yù)測(cè)

最后煮寡,利用現(xiàn)有的主數(shù)據(jù)集先進(jìn)性一次建模虹蓄,模型選擇的是LGB模型,速度快幸撕,效果好

#導(dǎo)入所需工具包
from sklearn.model_selection import KFold
from sklearn.metrics import roc_auc_score
import lightgbm as lgb
import gc
def plot_feature_importances(df):
    
    # 對(duì)特征按重要性進(jìn)行排序
    df = df.sort_values('importance', ascending = False).reset_index()
    
    # 對(duì)重要性特征進(jìn)行標(biāo)準(zhǔn)化處理
    df['importance_normalized'] = df['importance'] / df['importance'].sum()
    plt.figure(figsize = (10, 6))
    ax = plt.subplot()
    
    # 取重要性排名前15名進(jìn)行展示
    ax.barh(list(reversed(list(df.index[:15]))), 
            df['importance_normalized'].head(15), 
            align = 'center', edgecolor = 'k')
    
    # 設(shè)置yticks和labels
    ax.set_yticks(list(reversed(list(df.index[:15]))))
    ax.set_yticklabels(df['feature'].head(15))
    
    # 繪制標(biāo)簽
    plt.xlabel('Normalized Importance'); plt.title('Feature Importances')
    plt.show()
    
    return df
fi_sorted = plot_feature_importances(fi)

五薇组、利用其他數(shù)據(jù)集信息

信用局信息

首先是信用局信息,數(shù)據(jù)集中的每一行代表的是主訓(xùn)練集中的申請(qǐng)人曾經(jīng)在其他金融機(jī)構(gòu)申請(qǐng)的貸款信息坐儿,可以看到數(shù)據(jù)集中同樣有一列是“SK_ID_CURR'律胀,和主訓(xùn)練集中的列一致,可以通過這一列去把輔助訓(xùn)練集和主訓(xùn)練集做left join貌矿,但需要注意的一點(diǎn)是炭菌,一個(gè)SK_ID_CURR可能會(huì)對(duì)應(yīng)多個(gè)SK_ID_BUREAU,即一個(gè)申請(qǐng)人如果在其他金融機(jī)構(gòu)曾經(jīng)有多條貸款信息的話站叼,這里就會(huì)有多條記錄,因?yàn)槟P陀?xùn)練每個(gè)申請(qǐng)人在數(shù)據(jù)集中只能有一條記錄菇民,所以說不能直接把輔助訓(xùn)練集去和主訓(xùn)練集join尽楔,一般來說需要去計(jì)算一些統(tǒng)計(jì)特征(groupby操作)

#讀取數(shù)據(jù)集
bureau=pd.read_csv('bureau.csv')
bureau.head()
#針對(duì)每個(gè)貸款申請(qǐng)人計(jì)算他們?cè)谄渌鹑跈C(jī)構(gòu)歷史上的貸款數(shù)量
previous_loan_counts=bureau.groupby('SK_ID_CURR', as_index=False)['SK_ID_BUREAU'].count().rename(columns = {'SK_ID_BUREAU': 'previous_loan_counts'})
previous_loan_counts.head()

然后再把計(jì)算出來的統(tǒng)計(jì)特征和主訓(xùn)練集做left join,可以看到我們的統(tǒng)計(jì)特征就出現(xiàn)在了最后一列

app_train=app_train.merge(previous_loan_counts, on='SK_ID_CURR', how='left')
#用0填補(bǔ)缺失值
app_train['previous_loan_counts']=app_train['previous_loan_counts'].fillna(0)
app_train.head()

在做出新特征后第练,我們還需要檢驗(yàn)新特征是否對(duì)預(yù)測(cè)有區(qū)分度阔馋,因?yàn)椴皇撬械男绿卣鞫际怯杏玫模行]有用的特征加到數(shù)據(jù)集里反而會(huì)降低預(yù)測(cè)值娇掏。

print(app_train[app_train.TARGET==1]['previous_loan_counts'].describe())
print(app_train[app_train.TARGET==0]['previous_loan_counts'].describe())

通過查看違約和非違約用戶previous_loan_counts的統(tǒng)計(jì)屬性發(fā)現(xiàn)呕寝,雖然非違約用戶的平均貸款申請(qǐng)數(shù)量要略多于違約用戶,但差異很小婴梧,所以其實(shí)很難判斷這個(gè)特征對(duì)預(yù)測(cè)是否是有用的下梢,我們可以嘗試再做一些更多的特征
定義一個(gè)查看分布的函數(shù),再做一些新特征塞蹭,我們可以用這個(gè)函數(shù)快速查看新的特征再違約用戶和非違約用戶中的分布情況

#定義一個(gè)查看分布的函數(shù)
def kde_target(var_name, df):
    #計(jì)算新變量和TARGET之間的相關(guān)性
    corr=df['TARGET'].corr(df[var_name])
    #計(jì)算還款和未還款的平均數(shù)
    avg_repaid = df.ix[df['TARGET'] == 0, var_name].median()
    avg_not_repaid = df.ix[df['TARGET'] == 1, var_name].median()  
    plt.figure(figsize = (12, 6))  
    #TARGET=0和TARGET=1的分布圖
    sns.kdeplot(df.ix[df['TARGET'] == 0, var_name], label = 'TARGET == 0')
    sns.kdeplot(df.ix[df['TARGET'] == 1, var_name], label = 'TARGET == 1')
    #分布圖的標(biāo)簽
    plt.xlabel(var_name); 
    plt.ylabel('Density'); 
    plt.title('%s Distribution' % var_name)
    plt.legend();
    #輸出相關(guān)關(guān)系
    print('The correlation between %s and the TARGET is %0.4f' % (var_name, corr))
    #輸出平均值
    print('Median value for loan that was not repaid =%0.4f' % avg_not_repaid)
    print('Median value for loan that was repaid=%0.4f' % avg_repaid)    
kde_target('previous_loan_counts', app_train)
連續(xù)型變量特征提取

對(duì)于連續(xù)型變量孽江,可以采用計(jì)算它們的統(tǒng)計(jì)值來作為特征,為了快速計(jì)算出大量統(tǒng)計(jì)特征番电,可以結(jié)合采用python中g(shù)roupby和agg函數(shù)

# 定義函數(shù)完成特征提取
def agg_numeric(df, group_var, df_name):
    # 移除id變量
    for col in df:
        if col != group_var and 'SK_ID' in col:
            df = df.drop(columns = col)
            
    group_ids = df[group_var]
    numeric_df = df.select_dtypes('number')
    numeric_df[group_var] = group_ids

    # 通過groupby和agg函數(shù)計(jì)算出統(tǒng)計(jì)特征
    agg = numeric_df.groupby(group_var).agg(['count', 'mean', 'max', 'min', 'sum']).reset_index()
    columns = [group_var]
    
    # 列名重命名
    for var in agg.columns.levels[0]:
        if var != group_var:
            for stat in agg.columns.levels[1][:-1]:
                columns.append('%s_%s_%s' % (df_name, var, stat))

    agg.columns = columns
    return agg

bureau_agg_new = agg_numeric(bureau.drop(columns = ['SK_ID_BUREAU']), group_var = 'SK_ID_CURR', df_name = 'bureau')
# 提取的特征加入到主訓(xùn)練集中
app_train = app_train.merge(bureau_agg_new , on = 'SK_ID_CURR', how = 'left')
bureau_agg_new.head()

接下來岗屏,考察一下這些統(tǒng)計(jì)特征對(duì)模型預(yù)測(cè)的能力,在這里漱办,可以通過查看這些特征和Y值的相關(guān)性系數(shù)來做一個(gè)快速的判斷这刷,雖然不夠準(zhǔn)確,但可以做一個(gè)大致的參考

#定義一個(gè)相關(guān)性計(jì)算函數(shù)
def target_corrs(df):
    # List of correlations
    corrs = []

    # 通過列進(jìn)行迭代
    for col in df.columns:
        print(col)
        # 跳過target 列
        if col != 'TARGET':
            # 計(jì)算與target的相關(guān)性
            corr = df['TARGET'].corr(df[col])
            corrs.append((col, corr))
    corrs = sorted(corrs, key = lambda x: abs(x[1]), reverse = True)
    return corrs

通過函數(shù)輸出可以看到娩井,DAYS_CREDIT_mean(申請(qǐng)人再信用局開戶的平均歷史天數(shù))與Y值的正相關(guān)性最強(qiáng)暇屋,在數(shù)據(jù)集中我們可以發(fā)現(xiàn)這個(gè)值是負(fù)數(shù),因此可以理解為用戶的開戶時(shí)間越長洞辣,歷史信用紀(jì)律的時(shí)間越久越不容易違約

離散型變量特征提取

對(duì)于連續(xù)型變量我們可以計(jì)算統(tǒng)計(jì)值的方法去做特征率碾,對(duì)于離散變量我們不能計(jì)算他們的統(tǒng)計(jì)特征叔营,但可以計(jì)算離散性特征中每個(gè)取值的個(gè)數(shù)
首先先把數(shù)據(jù)集中的離散特征變成啞變量,然后求總數(shù)和均值所宰,sum列代表該類別的計(jì)數(shù)绒尊,mean表示其在整體中的占比

# 定義一個(gè)函數(shù)來提取離散變量的特征
def count_categorical(df, group_var, df_name):
 
    # 首先先把數(shù)據(jù)集中的離散特征變成啞變量
    categorical = pd.get_dummies(df.select_dtypes('object'))

    # 通過groupby和agg函數(shù)對(duì)特征進(jìn)行統(tǒng)計(jì)
    categorical[group_var] = df[group_var]

    #求總數(shù)和均值
    categorical = categorical.groupby(group_var).agg(['sum', 'mean'])
    
    column_names = []
    
    # 重命名列
    for var in categorical.columns.levels[0]:
        for stat in ['count', 'count_norm']:
            column_names.append('%s_%s_%s' % (df_name, var, stat))
    
    categorical.columns = column_names   
    return categorical

bureau_counts = count_categorical(bureau, group_var = 'SK_ID_CURR', df_name = 'bureau')
bureau_counts.head()
整合所有數(shù)據(jù)集

首先,重新讀取一邊數(shù)據(jù)集仔粥,把數(shù)據(jù)集還原到初始狀態(tài)

app_train=pd.read_csv('application_train.csv')
app_test = pd.read_csv('application_test.csv')
bureau = pd.read_csv('bureau.csv')
previous_application = pd.read_csv('previous_application.csv')

把之前對(duì)主訓(xùn)練集做的特征重新加入到數(shù)據(jù)集

app_train['CREDIT_INCOME_PERCENT'] = app_train['AMT_CREDIT'] / app_train['AMT_INCOME_TOTAL']
app_train['ANNUITY_INCOME_PERCENT'] = app_train['AMT_ANNUITY'] / app_train['AMT_INCOME_TOTAL']
app_train['CREDIT_TERM'] = app_train['AMT_ANNUITY'] / app_train['AMT_CREDIT']
app_train['DAYS_EMPLOYED_PERCENT'] = app_train['DAYS_EMPLOYED'] / app_train['DAYS_BIRTH']
app_train['INCOME_PER_CHILD'] = app_train['AMT_INCOME_TOTAL'] / app_train['CNT_CHILDREN']
app_train['HAS_HOUSE_INFORMATION'] = app_train['COMMONAREA_MEDI'].apply(lambda x:1 if x>0 else 0)

app_test['CREDIT_INCOME_PERCENT'] = app_test['AMT_CREDIT'] / app_test['AMT_INCOME_TOTAL']
app_test['ANNUITY_INCOME_PERCENT'] = app_test['AMT_ANNUITY'] / app_test['AMT_INCOME_TOTAL']
app_test['CREDIT_TERM'] = app_test['AMT_ANNUITY'] / app_test['AMT_CREDIT']
app_test['DAYS_EMPLOYED_PERCENT'] = app_test['DAYS_EMPLOYED'] / app_test['DAYS_BIRTH']
app_test['INCOME_PER_CHILD'] = app_test['AMT_INCOME_TOTAL'] / app_test['CNT_CHILDREN']
app_test['HAS_HOUSE_INFORMATION'] = app_test['COMMONAREA_MEDI'].apply(lambda x:1 if x>0 else 0)

兩個(gè)函數(shù)完成之前婴谱,對(duì)信用局?jǐn)?shù)據(jù)中連續(xù)變量和離散變量的特征提取

bureau_counts = count_categorical(bureau, group_var = 'SK_ID_CURR', df_name = 'bureau')
bureau_agg_new = agg_numeric(bureau.drop(columns = ['SK_ID_BUREAU']), group_var = 'SK_ID_CURR', df_name = 'bureau')
bureau_agg_new.head()

給訓(xùn)練集和測(cè)試集增加信用局相關(guān)特征

app_train = app_train.merge(bureau_counts, on = 'SK_ID_CURR', how = 'left')
app_train = app_train.merge(bureau_agg_new, on = 'SK_ID_CURR', how = 'left')

app_test = app_test.merge(bureau_counts, on = 'SK_ID_CURR', how = 'left')
app_test = app_test.merge(bureau_agg_new, on = 'SK_ID_CURR', how = 'left')

對(duì)于歷史貸款信息數(shù)據(jù),做同樣的操作

previous_appication_counts = count_categorical(previous_application, group_var = 'SK_ID_CURR', df_name = 'previous_application')
previous_appication_agg_new = agg_numeric(previous_application, group_var = 'SK_ID_CURR', df_name = 'previous_application')
previous_appication_agg_new.head()
app_train = app_train.merge(previous_appication_counts, on = 'SK_ID_CURR', how = 'left')
app_train = app_train.merge(previous_appication_agg_new, on = 'SK_ID_CURR', how = 'left')

app_test = app_test.merge(previous_appication_counts, on = 'SK_ID_CURR', how = 'left')
app_test = app_test.merge(previous_appication_agg_new, on = 'SK_ID_CURR', how = 'left')

print(app_train.shape)
print(app_test.shape)

六躯泰、特征篩選

在之前的一系列的特征工程中谭羔,我們給訓(xùn)練集和數(shù)據(jù)集增加了很多新的特征,在之前的一系列的特征工程中麦向,我們給訓(xùn)練集和測(cè)試集增加了很多新的特征瘟裸,特征也膨脹到了600多列,在最后建模之前诵竭,還需要對(duì)這些加入的特征再做一次篩選话告,排除一些具有共線性的特征以提高模型的效果,我們可以計(jì)算變量與變量之間的相關(guān)系數(shù)卵慰,來快速移除一些相關(guān)性過高的變量沙郭,這里可以定義一個(gè)閾值是0.8,即移除每一對(duì)相關(guān)性大于0.8的變量中的其中一個(gè)變量

orrs = app_train.corr()
# 設(shè)置閾值為0.8
threshold = 0.8
# 清空相關(guān)變量
above_threshold_vars = {}
# 記錄每列中超過閾值的變量
for col in corrs:
    above_threshold_vars[col] = list(corrs.index[corrs[col] > threshold])
# 跟蹤列
cols_to_remove = []
cols_seen = []
cols_to_remove_pair = []
# 通過列和相關(guān)列進(jìn)行迭代
for key, value in above_threshold_vars.items():
    # 追蹤已經(jīng)測(cè)試過的列
    cols_seen.append(key)
    for x in value:
        if x == key:
            next
        else:
            if x not in cols_seen:
                cols_to_remove.append(x)
                cols_to_remove_pair.append(key)
            
cols_to_remove = list(set(cols_to_remove))
print('Number of columns to remove: ', len(cols_to_remove))

在結(jié)果中可以看到一共移除了189列具有高相關(guān)性的變量

train_corrs_removed = app_train.drop(columns = cols_to_remove)
test_corrs_removed = app_test.drop(columns = cols_to_remove)
print('Training Corrs Removed Shape: ', train_corrs_removed.shape)
print('Testing Corrs Removed Shape: ', test_corrs_removed.shape)

訓(xùn)練集和測(cè)試集都移除對(duì)應(yīng)的列,我們把列數(shù)降低到426

七裳朋、建模預(yù)測(cè)

用之前建立好的模型再進(jìn)行一次建模預(yù)測(cè),到此病线,就完成了這個(gè)項(xiàng)目

submission, fi, metrics = model(train_corrs_removed, test_corrs_removed)
print('metrics')
print(metrics)
fi_sorted = plot_feature_importances(fi)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鲤嫡,隨后出現(xiàn)的幾起案子送挑,更是在濱河造成了極大的恐慌,老刑警劉巖暖眼,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件让虐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡罢荡,警方通過查閱死者的電腦和手機(jī)赡突,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來区赵,“玉大人惭缰,你說我怎么就攤上這事×牛” “怎么了漱受?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我昂羡,道長絮记,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任虐先,我火速辦了婚禮怨愤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蛹批。我一直安慰自己撰洗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布腐芍。 她就那樣靜靜地躺著差导,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猪勇。 梳的紋絲不亂的頭發(fā)上设褐,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音泣刹,去河邊找鬼助析。 笑死,一個(gè)胖子當(dāng)著我的面吹牛项玛,可吹牛的內(nèi)容都是我干的貌笨。 我是一名探鬼主播弱判,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼襟沮,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了昌腰?” 一聲冷哼從身側(cè)響起开伏,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遭商,沒想到半個(gè)月后固灵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡劫流,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年巫玻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祠汇。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仍秤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出可很,到底是詐尸還是另有隱情诗力,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布我抠,位于F島的核電站苇本,受9級(jí)特大地震影響袜茧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瓣窄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一笛厦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧康栈,春花似錦递递、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至悬荣,卻和暖如春菠秒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氯迂。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國打工践叠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嚼蚀。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓禁灼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親轿曙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弄捕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355