項(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
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绰咽。
如圖所示,藍(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ì)特征的一些理解,嘗試做出一些新的特征
CREDIT_INCOME_PERCENT: 貸款金額/客戶收入孩哑,預(yù)期是這個(gè)比值越大栓霜,說明貸款金額大于用戶的收入,用戶違約的可能性就越大
ANNUITY_INCOME_PERCENT: 貸款的每年還款金額/客戶收入横蜒,同上胳蛮,該比值越大用戶還款的壓力越大,違約的可能性越大
CREDIT_TERM: 貸款金額/貸款的每年還款金額丛晌,貸款的還款周期仅炊,猜測(cè)還款周期短的貸款,用戶的短期壓力可能會(huì)比較大澎蛛,違約概率高
DAYS_EMPLOYED_PERCENT: 用戶工作時(shí)間/用戶年齡茂洒,工作時(shí)間占年齡的百分比,預(yù)計(jì)越小違約概率越高
INCOME_PER_CHILD:客戶收入/孩子數(shù)量瓶竭,客戶的收入平均到每個(gè)孩子身上督勺,同樣的收入,如果這個(gè)人的家庭很大斤贰,孩子很多智哀,那么他的負(fù)擔(dān)可能比較重,違約的可能性可能更高
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)