Home Credit Default Risk EDA

这部分主要对kaggle的比赛Home Credit Default Risk做的一些探索性数据分析(EDA)

读入数据

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns

application_train = pd.read_csv('application_train.csv')
application_test = pd.read_csv('application_test.csv')
bureau = pd.read_csv('bureau.csv')
bureau_balance = pd.read_csv('bureau_balance.csv')
credit_card_balance = pd.read_csv('credit_card_balance.csv')
installments_payments = pd.read_csv('installments_payments.csv')
previous_application = pd.read_csv('previous_application.csv')
POS_CASH_balance = pd.read_csv('POS_CASH_balance.csv')

print('application_train.shape:', application_train.shape)
print('application_test.shape:', application_test.shape)
print('bureau.shape:', bureau.shape)
print('bureau_balance.shape:', bureau_balance.shape)
print('credit_card_balance.shape:', credit_card_balance.shape)
print('installments_payments.shape:', installments_payments.shape)
print('POS_CASH_balance.shape:', POS_CASH_balance.shape)
application_train.shape: (307511, 122)
application_test.shape: (48744, 121)
bureau.shape: (1716428, 17)
bureau_balance.shape: (27299925, 3)
credit_card_balance.shape: (3840312, 23)
installments_payments.shape: (13605401, 8)
POS_CASH_balance.shape: (10001358, 8)

查看数据

查看缺失的数据

def missing_data(data):
    total = data.isnull().sum().sort_values(ascending=False)
    percent = (data.isnull().sum()/data.isnull().count()).sort_values(ascending=False)
    return pd.concat([total, percent], axis=1, keys=['total', 'percent'])
print('missing_data(application_train).head(10):')
print(missing_data(application_train).head(10))
missing_data(application_train).head(10):
                           total   percent
COMMONAREA_MEDI           214865  0.698723
COMMONAREA_AVG            214865  0.698723
COMMONAREA_MODE           214865  0.698723
NONLIVINGAPARTMENTS_MODE  213514  0.694330
NONLIVINGAPARTMENTS_MEDI  213514  0.694330
NONLIVINGAPARTMENTS_AVG   213514  0.694330
FONDKAPREMONT_MODE        210295  0.683862
LIVINGAPARTMENTS_MEDI     210199  0.683550
LIVINGAPARTMENTS_MODE     210199  0.683550
LIVINGAPARTMENTS_AVG      210199  0.683550
print('missing_data(application_test).head(10):')
print(missing_data(application_test).head(10))
missing_data(application_test).head(10):
                          total   percent
COMMONAREA_MEDI           33495  0.687161
COMMONAREA_AVG            33495  0.687161
COMMONAREA_MODE           33495  0.687161
NONLIVINGAPARTMENTS_MODE  33347  0.684125
NONLIVINGAPARTMENTS_MEDI  33347  0.684125
NONLIVINGAPARTMENTS_AVG   33347  0.684125
FONDKAPREMONT_MODE        32797  0.672842
LIVINGAPARTMENTS_AVG      32780  0.672493
LIVINGAPARTMENTS_MEDI     32780  0.672493
LIVINGAPARTMENTS_MODE     32780  0.672493
print('missing_data(bureau).head(10):')
print(missing_data(bureau).head(10))
missing_data(bureau).head(10):
                          total   percent
AMT_ANNUITY             1226791  0.714735
AMT_CREDIT_MAX_OVERDUE  1124488  0.655133
DAYS_ENDDATE_FACT        633653  0.369170
AMT_CREDIT_SUM_LIMIT     591780  0.344774
AMT_CREDIT_SUM_DEBT      257669  0.150119
DAYS_CREDIT_ENDDATE      105553  0.061496
AMT_CREDIT_SUM               13  0.000008
CREDIT_TYPE                   0  0.000000
AMT_CREDIT_SUM_OVERDUE        0  0.000000
CNT_CREDIT_PROLONG            0  0.000000
print('missing_data(bureau_balance).head(10):')
print(missing_data(bureau_balance).head(10))
missing_data(bureau_balance).head(10):
                total  percent
STATUS              0      0.0
MONTHS_BALANCE      0      0.0
SK_ID_BUREAU        0      0.0
print('missing_data(credit_card_balance).head(10):')
print(missing_data(credit_card_balance).head(10))
missing_data(credit_card_balance).head(10):
                             total   percent
AMT_PAYMENT_CURRENT         767988  0.199981
AMT_DRAWINGS_OTHER_CURRENT  749816  0.195249
CNT_DRAWINGS_POS_CURRENT    749816  0.195249
CNT_DRAWINGS_OTHER_CURRENT  749816  0.195249
CNT_DRAWINGS_ATM_CURRENT    749816  0.195249
AMT_DRAWINGS_ATM_CURRENT    749816  0.195249
AMT_DRAWINGS_POS_CURRENT    749816  0.195249
CNT_INSTALMENT_MATURE_CUM   305236  0.079482
AMT_INST_MIN_REGULARITY     305236  0.079482
SK_DPD_DEF                       0  0.000000
print('missing_data(installments_payments).head(10):')
print(missing_data(installments_payments).head(10))
missing_data(installments_payments).head(10):
                        total   percent
AMT_PAYMENT              2905  0.000214
DAYS_ENTRY_PAYMENT       2905  0.000214
AMT_INSTALMENT              0  0.000000
DAYS_INSTALMENT             0  0.000000
NUM_INSTALMENT_NUMBER       0  0.000000
NUM_INSTALMENT_VERSION      0  0.000000
SK_ID_CURR                  0  0.000000
SK_ID_PREV                  0  0.000000
print('missing_data(POS_CASH_balance).head(10):')
print(missing_data(POS_CASH_balance).head(10))
missing_data(POS_CASH_balance).head(10):
                       total   percent
CNT_INSTALMENT_FUTURE  26087  0.002608
CNT_INSTALMENT         26071  0.002607
SK_DPD_DEF                 0  0.000000
SK_DPD                     0  0.000000
NAME_CONTRACT_STATUS       0  0.000000
MONTHS_BALANCE             0  0.000000
SK_ID_CURR                 0  0.000000
SK_ID_PREV                 0  0.000000

检查TARGET列的分布

print(application_train['TARGET'].value_counts())
application_train['TARGET'].astype(int).plot.hist()
plt.show()

数据分析

贷款的类型

让我们看看贷款的类型,以及在另一个的图表上,TARGET目标值为1(贷款未按时偿还)的贷款的百分比(按贷款类型划分)。

def plot_stats(feature, label_rotation=False, horizontal_layout=True):
    temp = application_train[feature].value_counts()
    df1 = pd.DataFrame({feature: temp.index, 'Number of contracts': temp.values})

    cat_perc = application_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()

plot_stats('NAME_CONTRACT_TYPE')

循环贷款只是贷款合同的小部分(10%左右)。与此同时,与循环贷款相比,现金贷款会有更大的几率未偿还。

客户的性别

让我们看看客户的性别,另外,在另一个图上,TARGET目标值为1(贷款未按时偿还)的贷款的百分比(按客户性别划分)

plot_stats('CODE_GENDER')

女性客户的数量几乎是男性客户数量的两倍。从违约信贷的比例来看,男性不偿还贷款的几率(约10%)高于女性(约7%)。

客户有没有车

让我们看看客户有没有自己的车,另外,在另一个图上,TARGET目标值为1(贷款未按时偿还)的贷款的百分比。

plot_stats('FLAG_OWN_CAR')

拥有汽车的客户几乎是没有汽车的客户的一半。拥有汽车的客户不太可能不偿还拥有汽车的客户的汽车。没有汽车的客户违约率稍微高点(8%左右)

客户有没有房

让我们看看客户有没有自己的房子,另外,在另一个图上,TARGET目标值为1(贷款未按时偿还)的贷款的百分比。

plot_stats('FLAG_OWN_REALTY')

拥有房产的客户是不拥有房产的两倍多。这两类人(拥有房产或不拥有房产)的违约率都在8%左右。

客户家庭状况
plot_stats('NAME_FAMILY_STATUS', True, True)

大多数客户已婚,其次是单身/未婚。
在不偿还贷款比例方面,民事婚姻不偿还比例最高(10%),寡妇最低。

儿童的数量
plot_stats('CNT_CHILDREN')

大多数贷款的客户都没有孩子。从图中我们可以看到孩子越少的客户越会去贷款。
在还款方面,无子女、1、2、3、5子女的客户平均有10%左右没有还款。对于有9、11个孩子的客户,未偿还贷款的比例为100%。

客户家庭人数
plot_stats('CNT_FAM_MEMBERS',True)

家庭成员为2人的客户最多,其次为1人(单身人士)、3人(独生子女家庭)和4人。
家庭成员分别为11人和13人的客户有100%的违约率。有10或8个成员的家庭有30%左右的违约率。拥有6名或6名以下家庭成员的违约率为10%的左右。

客户收入类型
plot_stats('NAME_INCOME_TYPE',False,False)

申请贷款的人大多是工作收入,其次是商业伙伴、养老金领取者和国家公务员。
产假的有近40%的违约率,其次是失业(约37%)。其他类型的违约率均低于10%。

客户的职业
plot_stats('OCCUPATION_TYPE',True, False)

大部分贷款由劳动者承担,其次是销售人员。IT人员的最低。前几天看了一篇推文发现学了IT的罪犯,再犯率为0%。
违约率最高的是低技能劳动者(17%以上),其次是司机、服务员/酒吧服务员、保安、劳动者和厨师。

行业类型
plot_stats('ORGANIZATION_TYPE',True, False)

违约率最高的行业是运输业:第3类(16%)、工业:第13类(13.5%)、工业:第8类(12.5%)和餐饮业(不到12%)。

客户的教育类型
plot_stats('NAME_EDUCATION_TYPE',True)

大部分客户接受中等/中等特殊教育,其次是接受高等教育的客户。只有极少数人拥有学位。
初中毕业的贷款虽然很少见,但不还贷率最高(11%)。拥有学位的人的不偿还率低于2%。

客户房屋类型
plot_stats('NAME_HOUSING_TYPE',True)

25万多名申请贷款的人将他们的住房登记为住房/公寓。

从这些类别来看,租来的公寓和跟父母一起住的高于10%的违约率。

Bureau数据集

让我们将application_train与bureau合并。

application_bureau_train = application_train.merge(bureau, left_on='SK_ID_CURR', right_on='SK_ID_CURR', how='inner')
print("The resulting dataframe `application_bureau_train` has ",application_bureau_train.shape[0]," rows and ", 
      application_bureau_train.shape[1]," columns.")
The resulting dataframe `application_bureau_train` has  1465325  rows and  138  columns.
现在让我们分析application_bureau_train数据。
def plot_b_stats(feature,label_rotation=False,horizontal_layout=True):
    temp = application_bureau_train[feature].value_counts()
    df1 = pd.DataFrame({feature: temp.index,'Number of contracts': temp.values})

    # Calculate the percentage of target=1 per category value
    cat_perc = application_bureau_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()
信用状态

让我们看看信用状况分布。我们首先显示每个类别的信贷数量(可以是关闭的、活跃的、出售的和坏账)。

plot_b_stats('CREDIT_ACTIVE')

我们可以看出大约有900000人的状态是closed,Bad debt的状态是最少的。
但我们可以看到Bad debt的违约率是最高的,是其他状态的两倍多。

信用货币
plot_b_stats('CREDIT_CURRENCY')

从图上我们可以看出,几乎全都是currency_1贷款。
根据货币的不同,客户违约的比例是不同的。从currency_3开始,然后是currency_1和currency_2,违约的百分比分别是11%、8%和5%。currency_4的违约率约为0%。

信用类型
plot_b_stats('CREDIT_TYPE', True, True)

在 Credit Bureau登记的历史信用主要是消费信贷和信用卡。
购买设备的贷款——违约率超过20%;
小额贷款——违约率超过20%;
流动资金补充贷款——违约率超过12%。

Previous application数据集

让我们将application_train与previous_application合并。

application_prev_train = application_train.merge(previous_application, left_on='SK_ID_CURR', right_on='SK_ID_CURR', how='inner')
print("The resulting dataframe `application_prev_train` has ",application_prev_train.shape[0]," rows and ", 
      application_prev_train.shape[1]," columns.")
The resulting dataframe `application_prev_train` has  1413701  rows and  158  columns.
def plot_p_stats(feature,label_rotation=False,horizontal_layout=True):
    temp = application_prev_train[feature].value_counts()
    df1 = pd.DataFrame({feature: temp.index,'Number of contracts': temp.values})

    # Calculate the percentage of target=1 per category value
    cat_perc = application_prev_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()
合同类型
plot_p_stats('NAME_CONTRACT_TYPE_y')

在之前的申请数据中有三种类型的合同:现金贷款、消费贷款、循环贷款。现金贷款和消费贷款几乎相同(约60万),而循环贷款约15万。
之前申请过的客户违约率因之前申请合同的类型而异,循环贷款的违约率为10%,现金贷款的违约率为9.5%,消费贷款的违约率为8%。

现金贷款的目的
plot_p_stats('NAME_CASH_LOAN_PURPOSE', True, True)

除XAP、XNA、修理、其他以外,购买二手车、建造房屋占合同数量最多。

合同状态
plot_p_stats('NAME_CONTRACT_STATUS', True, True)

大部分的合同状态是批准的,其次是取消和拒绝的。
合同状态为拒绝的违约率最高(约12%),其次是取消的(约9%),违约率最低的是批准的(不到8%)。

付款类型
plot_p_stats('NAME_PAYMENT_TYPE', True, True)

大部分的付款类型是通过银行的,其次是XNA。
除了XNA(违约率最高),其他的违约率约为8%。

结论

EDA到此为止,我们可以根据EDA创建一些新的特征、清洗数据等等。

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

推荐阅读更多精彩内容