Kaggle: Home Credit Default Risk(房屋信贷违约风险预测)

项目背景:

1、目的:通过数据集提供的相关数据,预测客户贷款是否违约
2、数据集介绍:一共有8个数据集,包括1个主训练集,1个测试集和6个辅助信息表,主训练集特征主要有用户的个人属性,包括用户的性别,职业,是否有车,是否有房,房子面积等基本信息,辅助信息表包括用户的历史申请信息,历史账户余额信息,分期付款信息,信用卡信息,信用局信息和在信用局上的额度信息。

一、数据集探索

通过项目所提供的数据集,整理出了如下数据集的关系图,从图中我们可以看到唯一主键是SK_ID_CURR

数据集关系图.png
1、了解数据集中主要指标
2、指标评估

本项目对用户贷款是否违约进行违约的预测是一个二分类问题,因此评估指标为AUC。
ROC曲线体现了“一般情况下”泛化性能的好坏。
若一个学习器的ROC曲线被另一个学习器的曲线完全“包住”,则可断言后者的性能优于前者。若两个学习器的ROC曲线发生交叉,则比较ROC曲线下的面积,即AUC。


ROC曲线.png

如图所示,蓝色模型优于红色模型,红色模型优于黑色对角线,黑色对角线表示幼稚的随机猜测模型。而AUC表示的是在ROC曲线下的面积,在这个指标中,更加细化比较了各个学习器的得分。

二、主训练集探索

主训练集数据:application_train.csv
探索目的:
1、了解数据的缺失情况,异常值情况、以便做对应的数据清洗;
2、了解一下违约贷款和正常贷款用户画像的区别,加深对业务的理解,为后续数据分析(特征工程)展开打基础

首先,导入一些所需要的工具包

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、异常值检测

#定义缺失值检测函数
def missing_values_table(df):
        # 总缺失值
        mis_val = df.isnull().sum()
        
        # 计算缺失值百分比
        mis_val_percent = 100 * df.isnull().sum() / len(df)
        
        # 合并缺失值数量和占比
        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'})
        
        # 对存在缺失值的特征按百分比的大小降序排序
        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)
        
        # 输出结果
        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.")
        
        # 返回数组中的缺失值
        return mis_val_table_ren_columns
missing_values=missing_values_table(app_train)
missing_values.head(20)

根据以上结果显示:数据一共有122列,有67列存在缺失情况,最高缺失值的列缺失度为69.9%。

2、异常值探索

这里主要采用描述统计方法,即查看特征的均值、极大值、极小值等信息判断是否有异常值

#用户年龄的分布情况
(app_train['DAYS_BIRTH']/-365).describe()

根据用户年龄的数据分布情况,发现数据的分布还是比较正常的,最大年龄69,最小年龄20,没有很异常的数字

#用户工作时间的分布情况
(app_train['DAYS_EMPLOYED']/-365).describe()
app_train['DAYS_EMPLOYED'].plot.hist(title='Days Employment Histogram')

通过用户工作时间的数据分布情况,发现所有的异常值都是365243,对于这个异常值可以理解为缺失值,所以我选择用空值0替换,从而保留这个信息,替换后看一下工作时间的分布情况

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、违约用户画像探索

目的:查看违约用户和非违约用户的特征分布情况

#绘图函数,通过图形可以直观地看到数据的分布情况
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})

    # 计算违约用户(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')

通过上图可以发现:男性用户违约率更高,男性违约率为10%, 女性违约率为7%

3.2 违约用户和正常用户年龄分布情况

由于年龄是连续型变量, 所以用KDE分布图查看年龄的分布情况

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

#及时还款用户的KDE图
sns.kdeplot(app_train.loc[app_train['TARGET']==0, 'DAYS_BIRTH']/-365, label='target==0')

#未及时还款用户的KDE图
sns.kdeplot(app_train.loc[app_train['TARGET']==1, 'DAYS_BIRTH']/-365, label='target==1')

#图的标签
plt.xlabel('Age(years)');
plt.ylabel('Density');
plt.title('Distribution of Ages');

通过数据分布图可以看出:
TARGET=0,蓝色曲线,代表按时还款的年龄集中在30-60之间,平均年龄偏高
TARGET=1,红色曲线,代表违约用户在30之后随着年龄的增长,违约的情况在递减
由此可以推断:用户年龄越小,违约的可能性越大

接下来,对用户的年龄进行分割,进一步分析不同年龄用户的违约概率

#计算不同年龄段,违约用户的概率
age_data=app_train[['TARGET', 'DAYS_BIRTH']]
age_data['YEARS_BIRTH']=age_data['DAYS_BIRTH']/-365

#将年龄进行分割,计算平均值
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]之间的人群违约比例较高,之后随着年龄的增长,客户按时还款的比例有所增加。由此可以发现,用户年龄越小,违约的可能性越高

3.3 不同贷款类型的违约情况
plot_stats('NAME_CONTRACT_TYPE')

从图中可以看出,对于现金贷款和流动资金循环贷款,现金贷款的违约率更高

3.4 用户有无房车对违约率的影响
#有没有车对违约率的影响
plot_stats('FLAG_OWN_CAR')
#有没有房对违约率的影响
plot_stats('FLAG_OWN_REALTY')



从图中可以看出,没有车和房的人违约率更高,但相差不是很大

3.5 用户婚姻情况对违约率的影响
#看家庭结婚情况对违约率的影响
plot_stats('NAME_FAMILY_STATUS', True, True)

从家庭情况来看,申请的用户大多已经结婚,单身和世俗结婚的违约率较高,寡居的违约率最低

3.6 用户子女情况对违约率的影响
#看子女情况
plot_stats('CNT_CHILDREN')

从图中可以看出,大部分申请者没有孩子或孩子在3个以下,孩子越多的家庭违约率越高,发现对于9、11个孩子的家庭违约率达到了100%

3.7 用户收入类型对违约率的影响
#看申请者的收入类型
plot_stats('NAME_INCOME_TYPE', False, False)

根据申请者的收入类型区分,可以发现休产假的和没有工作的人违约率较高,在35%以上,对于这两类人群贷款需要谨慎

3.8 用户职业类型对违约率的影响
#职业类型
plot_stats('OCCUPATION_TYPE', True, False)

从职业来看,收入较低或不稳定的职业的违约率越高,比如低廉劳动力、司机、理发师,而像会计、高科技员工等具有稳定高收入的职业违约率较低。

3.9 用户受教育程度对违约率的影响
#贷款人受教育程度
plot_stats('NAME_EDUCATION_TYPE', True)

贷款申请人受教育程度大多数为中学,学历越低越容易违约

三、特征工程

通过对特征的一些理解,尝试做出一些新的特征

  1. CREDIT_INCOME_PERCENT: 贷款金额/客户收入,预期是这个比值越大,说明贷款金额大于用户的收入,用户违约的可能性就越大

  2. ANNUITY_INCOME_PERCENT: 贷款的每年还款金额/客户收入,同上,该比值越大用户还款的压力越大,违约的可能性越大

  3. CREDIT_TERM: 贷款金额/贷款的每年还款金额,贷款的还款周期,猜测还款周期短的贷款,用户的短期压力可能会比较大,违约概率高

  4. DAYS_EMPLOYED_PERCENT: 用户工作时间/用户年龄,工作时间占年龄的百分比,预计越小违约概率越高

  5. INCOME_PER_CHILD:客户收入/孩子数量,客户的收入平均到每个孩子身上,同样的收入,如果这个人的家庭很大,孩子很多,那么他的负担可能比较重,违约的可能性可能更高

  6. HAS_HOUSE_INFORMATION : 根据客户是否有缺失房屋信息设计一个二分类特征,如果未缺失的话是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))
# 对新特征进行迭代
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)
    # 按时还贷用户直方图
    sns.kdeplot(app_train_domain.loc[app_train_domain['TARGET'] == 0, feature], label = 'target == 0')
    #  未按时还贷用户直方图
    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)

通过设计出来的连续新特征,可以看出它们在违约用户和非违约用户中的分布情况,可以发现出CREDIT_TERM这个特征外,其他的特征区分度都不明显,接下来放到模型中再来看一下效果

plot_stats('HAS_HOUSE_INFORMATION', True)

通过缺失值设计的这个特征,从上图可以看出,缺失房屋信息的用户违约概率要明显高于未缺失用户,在模型的预测中可以算是一个比较有效的特征了

对测试集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)

四、建模预测

最后,利用现有的主数据集先进性一次建模,模型选择的是LGB模型,速度快,效果好

#导入所需工具包
from sklearn.model_selection import KFold
from sklearn.metrics import roc_auc_score
import lightgbm as lgb
import gc
def plot_feature_importances(df):
    
    # 对特征按重要性进行排序
    df = df.sort_values('importance', ascending = False).reset_index()
    
    # 对重要性特征进行标准化处理
    df['importance_normalized'] = df['importance'] / df['importance'].sum()
    plt.figure(figsize = (10, 6))
    ax = plt.subplot()
    
    # 取重要性排名前15名进行展示
    ax.barh(list(reversed(list(df.index[:15]))), 
            df['importance_normalized'].head(15), 
            align = 'center', edgecolor = 'k')
    
    # 设置yticks和labels
    ax.set_yticks(list(reversed(list(df.index[:15]))))
    ax.set_yticklabels(df['feature'].head(15))
    
    # 绘制标签
    plt.xlabel('Normalized Importance'); plt.title('Feature Importances')
    plt.show()
    
    return df
fi_sorted = plot_feature_importances(fi)

五、利用其他数据集信息

信用局信息

首先是信用局信息,数据集中的每一行代表的是主训练集中的申请人曾经在其他金融机构申请的贷款信息,可以看到数据集中同样有一列是“SK_ID_CURR',和主训练集中的列一致,可以通过这一列去把辅助训练集和主训练集做left join,但需要注意的一点是,一个SK_ID_CURR可能会对应多个SK_ID_BUREAU,即一个申请人如果在其他金融机构曾经有多条贷款信息的话,这里就会有多条记录,因为模型训练每个申请人在数据集中只能有一条记录,所以说不能直接把辅助训练集去和主训练集join,一般来说需要去计算一些统计特征(groupby操作)

#读取数据集
bureau=pd.read_csv('bureau.csv')
bureau.head()
#针对每个贷款申请人计算他们在其他金融机构历史上的贷款数量
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()

然后再把计算出来的统计特征和主训练集做left join,可以看到我们的统计特征就出现在了最后一列

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

在做出新特征后,我们还需要检验新特征是否对预测有区分度,因为不是所有的新特征都是有用的,有些没有用的特征加到数据集里反而会降低预测值。

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的统计属性发现,虽然非违约用户的平均贷款申请数量要略多于违约用户,但差异很小,所以其实很难判断这个特征对预测是否是有用的,我们可以尝试再做一些更多的特征
定义一个查看分布的函数,再做一些新特征,我们可以用这个函数快速查看新的特征再违约用户和非违约用户中的分布情况

#定义一个查看分布的函数
def kde_target(var_name, df):
    #计算新变量和TARGET之间的相关性
    corr=df['TARGET'].corr(df[var_name])
    #计算还款和未还款的平均数
    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')
    #分布图的标签
    plt.xlabel(var_name); 
    plt.ylabel('Density'); 
    plt.title('%s Distribution' % var_name)
    plt.legend();
    #输出相关关系
    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)
连续型变量特征提取

对于连续型变量,可以采用计算它们的统计值来作为特征,为了快速计算出大量统计特征,可以结合采用python中groupby和agg函数

# 定义函数完成特征提取
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函数计算出统计特征
    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')
# 提取的特征加入到主训练集中
app_train = app_train.merge(bureau_agg_new , on = 'SK_ID_CURR', how = 'left')
bureau_agg_new.head()

接下来,考察一下这些统计特征对模型预测的能力,在这里,可以通过查看这些特征和Y值的相关性系数来做一个快速的判断,虽然不够准确,但可以做一个大致的参考

#定义一个相关性计算函数
def target_corrs(df):
    # List of correlations
    corrs = []

    # 通过列进行迭代
    for col in df.columns:
        print(col)
        # 跳过target 列
        if col != 'TARGET':
            # 计算与target的相关性
            corr = df['TARGET'].corr(df[col])
            corrs.append((col, corr))
    corrs = sorted(corrs, key = lambda x: abs(x[1]), reverse = True)
    return corrs

通过函数输出可以看到,DAYS_CREDIT_mean(申请人再信用局开户的平均历史天数)与Y值的正相关性最强,在数据集中我们可以发现这个值是负数,因此可以理解为用户的开户时间越长,历史信用纪律的时间越久越不容易违约

离散型变量特征提取

对于连续型变量我们可以计算统计值的方法去做特征,对于离散变量我们不能计算他们的统计特征,但可以计算离散性特征中每个取值的个数
首先先把数据集中的离散特征变成哑变量,然后求总数和均值,sum列代表该类别的计数,mean表示其在整体中的占比

# 定义一个函数来提取离散变量的特征
def count_categorical(df, group_var, df_name):
 
    # 首先先把数据集中的离散特征变成哑变量
    categorical = pd.get_dummies(df.select_dtypes('object'))

    # 通过groupby和agg函数对特征进行统计
    categorical[group_var] = df[group_var]

    #求总数和均值
    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()
整合所有数据集

首先,重新读取一边数据集,把数据集还原到初始状态

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')

把之前对主训练集做的特征重新加入到数据集

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)

两个函数完成之前,对信用局数据中连续变量和离散变量的特征提取

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()

给训练集和测试集增加信用局相关特征

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')

对于历史贷款信息数据,做同样的操作

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)

六、特征筛选

在之前的一系列的特征工程中,我们给训练集和数据集增加了很多新的特征,在之前的一系列的特征工程中,我们给训练集和测试集增加了很多新的特征,特征也膨胀到了600多列,在最后建模之前,还需要对这些加入的特征再做一次筛选,排除一些具有共线性的特征以提高模型的效果,我们可以计算变量与变量之间的相关系数,来快速移除一些相关性过高的变量,这里可以定义一个阈值是0.8,即移除每一对相关性大于0.8的变量中的其中一个变量

orrs = app_train.corr()
# 设置阈值为0.8
threshold = 0.8
# 清空相关变量
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 = []
# 通过列和相关列进行迭代
for key, value in above_threshold_vars.items():
    # 追踪已经测试过的列
    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))

在结果中可以看到一共移除了189列具有高相关性的变量

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)

训练集和测试集都移除对应的列,我们把列数降低到426

七、建模预测

用之前建立好的模型再进行一次建模预测,到此,就完成了这个项目

submission, fi, metrics = model(train_corrs_removed, test_corrs_removed)
print('metrics')
print(metrics)
fi_sorted = plot_feature_importances(fi)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350