风控

网贷平台Prosper2005~2014贷款数据分析(一)

2017-12-25  本文已影响440人  YoLean

一、摘要

本文详述了如何通过数据预览,基本数据分析、探索式数据分析,缺失数据填补等方法,实现对kaggle上Prosper借贷平台贷款者还款与否这一分类问题如何进行数据分析的具体探索式实践。并分别对2009.07.01前后的模型进行建模分析对比,得出两个模型的预测准确率和变量对模型的重要性对比分析,明确看出2009.07.01前后平台的模型明显有很大的不同。

二、项目内容介绍

Prosper LoanData是由Joshua Schnessl从Udacity Data Analyst Nanodegree上把数据放到kaggle的上供感兴趣的人分析的一个实例项目,这并非一个竞赛项目。本人试图通过训练数据集分析出什么类型的借款人更可能不违约,并预测出测试数据集中的每笔贷款是否违约。

三、数据变量

数据集包含81个变量,113,937条数据,由于数据变量太多,本文只对重要变量作解释说明,若需详细了解变量含义,请点击变量词典

四、数据预处理

在加载数据之前,先通过如下代码加载之后会用到的相应的库。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import random
from sklearn.cross_validation import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_curve, auc

通过如下代码导入数据:

    #导入数据
    loanData=pd.read_csv('prosperLoanData.csv')

4.1 LoanStatus数据变换

平台把借款状态分为12种:Cancelled(取消)、Chargedoff(冲销,投资人有损失)、Completed(正常完成,投资人无损失)、Current(贷款还款中)、Defaulted(坏账,投资人有损失)、FinalPaymentInProgress(最后还款中,投资人无损失)、Past Due(逾期还款,投资人无损失),Cancelled的只有5笔,所以直接去掉。
本文依据交易是在进行正常还款期内还是已关闭将LoanStatus分成两组,并根据投资人有无损失将已关闭的交易分成Completed和Defaulted:Current(贷款还款中)、Defaulted(包含Defaulted、Chargedoff)、Completed(包含Completed、FinalPaymentInProgress、Past Due)三组。代码定义函数如下:

#贷款状态变换
def loan_status(s):
    if s=='Chargedoff':
        a='Defaulted'
    elif s=='Defaulted':
        a = 'Defaulted'
    elif s=='Cancelled':
        a='Cancelled'
    elif s == 'Current':
        a = 'Current'
    else:
        a='Completed'
    return a

将数据进行转换,设置为Status变量:

    #LoanStatus数据转换
    loanData['Status']=loanData['LoanStatus'].apply(loan_status)
    #'Cancelled状态只有5个,直接删除
    loanData=loanData[loanData['Status']!='Cancelled']

4.2 BankcardUtilization数据变换

信用资料提交时借款人信用卡使用额度和信用卡总透支额度的百分比,本文将这个数据分成五组(mild use、medium use、heavy use、super use、no use)。对之前未在prosper的客户建立库,即其0或NA是未使用过prosper的客户,用no use代替。使用的定义函数如下:

#BankcardUtilization数据转换
def bank_card_use(s,oneForth = 0.31,twoForth = 0.6):
    if s<=oneForth:
        b='Mild Use'
    elif (s>oneForth) & (s<=twoForth):
        b='Medium Use'
    elif (s>twoForth) & (s<=1):
        b='Heavy Use'
    elif s>1:
        b='Super Use'
    else:
        b='No Use'
    return b

将数据进行转换,设置为BankCardUse变量:

    #BankcardUtilization的数据转换
    oneFourth=loanData['BankcardUtilization'].quantile(0.25)
    twoForth=loanData['BankcardUtilization'].quantile(0.5)
    loanData['BankCardUse']=loanData['BankcardUtilization'].apply(bank_card_use)

4.3 LoanOriginationDate数据转换

因为2009年7月1日是一个数据截点,因此将数据分成两段处理,定义函数如下:

#将时间分段
def date_phase(s):
    if s>='2009-07-01':
        c='After Jul.2009'
    else:
        c='Before Jul.2009'
    return c

将数据进行转换,设置为DatePhase变量:

    #LoanOriginationDate的数据转换
    loanData['DatePhase']=loanData['LoanOriginationDate'].apply(date_phase)

4.4 信用分数转换

在变量中有信用分数两个变量CreditScoreRangeUpper和CreditScoreRangeLower,可以通过信用分数高低范围,将这两个数值取平均值做计算:

    #对客户的消费信用分数,数据中有高低范围,将这两个数值取平均值做计算
    loanData['CreditScore']=((loanData.CreditScoreRangeUpper+loanData.CreditScoreRangeLower)/2).round(0)

4.5 TotalProsperLoans

对于使用Prosper平台使用贷款的次数变量TotalProsperLoans,我们可以根据数量区分新老用户,0或NA是未使用过prosper的客户,反之是使用过的,定义的区分函数如下:

#0或NA是未使用过prosper的客户,反之是使用过的
def customer_clarify(s):
    if s>0:
        d='Previous Borrower'
    else:
        d='New Borrower'
    return d

将数据进行转换,设置为CustomerClarify变量:

    #TotalProsperLoans的数据转换
    loanData['CustomerClarify']=loanData['TotalProsperLoans'].apply(customer_clarify)

五、探索性分析

5.1 年收入越高,违约率越低

通过统计出每个年收入段贷款如期还款笔数和违约笔数,并计算出每段贷款如期还款笔数和违约笔数所占的比例。代码如下:

    # 输出loanStatus和收入的情况分配
    incomeRage = loanData.groupby(['Status', 'IncomeRange'])['Status'].count().unstack(0)
    index = ['Not displayed', 'Not employed', '$0 ', '$1-24,999', '$25,000-49,999', '$50,000-74,999', '$75,000-99,999', '$100,000+']
    incomeRage = incomeRage.reindex(index)
    defaultRate = (incomeRage['Defaulted'] / (incomeRage['Defaulted'] + incomeRage['Completed'])).reindex(index)
    print(defaultRate)
    # 违约率情况
    y = list(defaultRate.values)
    fig1 = plt.figure(1)
    # fig1.set_size_inches(15.5, 7.5)
    ax1 = fig1.add_subplot(2, 1, 2)
    x = np.arange(len(index)) + 1
    ax1.bar(x, y, width=0.4)
    ax1.set_xticks(x)
    ax1.set_xticklabels(index, rotation=0, fontsize=12)
    ax1.set_ylabel('违约百分率(%)', fontsize=14)
    for a, b in zip(x, y):
        plt.text(a, b + 0.001, '%.2f%%' % (b * 100), ha='center', va='bottom', fontsize=10)
    ax2 = fig1.add_subplot(2, 1, 1)
    incomeRage.plot(kind='bar', ax=ax2)
    ax2.set_xticklabels(index, rotation=0, fontsize=12)
    ax2.set_ylabel('数量', fontsize=14)
    plt.show()
如图5-1所示,随着年收入的增加,违约比例在逐渐下降。 图5-1 年收入和违约情况分布图

5.2 负债水平低的借款人违约率低于负债水平高的人

按照常识来说债务收入比(DebtToIncomeRatio)低的人更具备还款能力,违约可能性应该低于债务收入比高的人。

    DefaultedRatio=loanData[loanData['Status']=='Defaulted']['DebtToIncomeRatio']
    CompletedRatio=loanData[loanData['Status']=='Completed']['DebtToIncomeRatio']
    print(DefaultedRatio.shape)
    print(CompletedRatio.shape)
    ax=CompletedRatio.hist(bins=1000,color='g',label='Compeleted')
    ax=DefaultedRatio.hist(bins=1000,color='b',label='Defaulted')
    ax.set_xlim([0,1])
    plt.xlabel('DebtToIncomeRatio',fontsize=14)
    plt.ylabel('数量',fontsize=14)
    plt.legend(loc='best')
    plt.show()
如图5-2所示,DebtToIncomeRatio < 0.6的借款人中,违约笔数小于未违约的笔数。从下图也可看出大部分借款人的债务收入比低于0.25,说明平台违约的整体风险可控。 图5-2 负债收入比和违约情况分布图

5.3 透支比例非常高的人违约概率大

根据BankCardUse与Status,获取信用卡使用情况和违约情况的分布情况,代码实现如下:

    # 输出loanStatus和收入的情况分配
    bankCardUse = loanData.groupby(['Status', 'BankCardUse'])['Status'].count().unstack(0)
    index=['Mild Use','Medium Use', 'Heavy Use', 'Super Use', 'No Use',]
    bankCardUse=bankCardUse.reindex(index)
    defaultRate = (bankCardUse['Defaulted'] / (bankCardUse['Defaulted'] + bankCardUse['Completed'])).reindex(index)
    print(defaultRate)
    # 违约率情况
    y = list(defaultRate.values)
    fig1 = plt.figure(1)
    # fig1.set_size_inches(15.5, 7.5)
    ax1 = fig1.add_subplot(2, 1, 2)
    x = np.arange(len(index)) + 1
    ax1.bar(x, y, width=0.4)
    ax1.set_xticks(x)
    ax1.set_xticklabels(index, rotation=0, fontsize=12)
    ax1.set_ylabel('违约百分率(%)', fontsize=14)
    for a, b in zip(x, y):
        plt.text(a, b + 0.001, '%.2f%%' % (b * 100), ha='center', va='bottom', fontsize=10)
    ax2 = fig1.add_subplot(2, 1, 1)
    bankCardUse.plot(kind='bar', ax=ax2)
    ax2.set_xticklabels(index, rotation=0, fontsize=12)
    ax2.set_xlabel('',fontsize=0)
    ax2.set_ylabel('数量', fontsize=14)
    plt.show()
如图5-3所示,BankCardUse为Mild Use、Medium Use、Heavy Use时违约率在25%左右,而Super Use、No Use违约率在50%左右,所以,对符合这两种情况的借款人要加强风险管控。 图5-3 信用卡使用程度和违约情况分布图

5.4 消费信用分低的借款人违约概率大

消费信用分(CreditScore)是衡量一个人在消费中的经济能力,分值高的人交易更活跃、交易活动违约率更低,所以消费信用分高的人更具备还款能力,违约可能性应该低于消费信用分低的人。

    creditScore=pd.DataFrame(loanData.groupby(['Status', 'CreditScore'])['Status'].count().unstack(0))
    score=pd.DataFrame(creditScore.values[2:],index=list(creditScore.index)[2:],columns=list(creditScore.columns))
    index=list(creditScore.index)[2:]
    defaultRate = (score['Defaulted'] / (score['Defaulted'] + score['Completed'])).reindex(index)
    print(defaultRate)
    ax=score.plot(kind='line',grid=True)
    ax.set_xlabel('CreditScore',fontsize=14)
    ax.set_ylabel('数量', fontsize=14)
    plt.legend(loc='best')
    plt.show()
如图5-4所示,CreditScore < 560的借款人中,违约笔数大于未违约的笔数。从下图也可看出大部分借款人的消费信用分高于600分,说明消费信用分低的人在平台上不容易借到钱。 图5-4 信用分数和违约情况分布图

5.5 信用评级高(2009.07.01之前)的人违约概率小

根据CreditGrade与Status,获取2009年7月之前信用等级情况和违约情况的分布情况,代码实现如下:

    # 输出loanStatus和收入的情况分配
    creditGrade = loanData.groupby(['Status', 'CreditGrade'])['Status'].count().unstack(0)
    index=[ 'NC','HR','E','D', 'C','B', 'A', 'AA']
    creditGrade=creditGrade.reindex(index)
    defaultRate = (creditGrade['Defaulted'] / (creditGrade['Defaulted'] + creditGrade['Completed'])).reindex(index)
    # 违约率情况
    y = list(defaultRate.values)
    fig1 = plt.figure(1)
    # fig1.set_size_inches(15.5, 7.5)
    ax1 = fig1.add_subplot(2, 1, 2)
    x = np.arange(len(index)) + 1
    ax1.bar(x, y, width=0.4)
    ax1.set_xticks(x)
    ax1.set_xticklabels(index, rotation=0, fontsize=12)
    ax1.set_xlabel('CreditGrade', fontsize=14)
    ax1.set_ylabel('违约百分率(%)', fontsize=14)
    for a, b in zip(x, y):
        plt.text(a, b + 0.001, '%.2f%%' % (b * 100), ha='center', va='bottom', fontsize=10)
    ax2 = fig1.add_subplot(2, 1, 1)
    creditGrade.plot(kind='bar', ax=ax2)
    ax2.set_xticklabels(index, rotation=0, fontsize=12)
    ax2.set_ylabel('数量', fontsize=14)
    ax2.set_xlabel('',fontsize=14)
    plt.show()
如图5-5所示,CreditGrade评级越高的人违约率越低,大部分借款人的评级都在D级以上。 图5-5 2009前信用等级和违约情况分布图

5.6 信用评级高(2009.07.01之后)的人违约概率小

ProsperRating (Alpha)是2009年之后的信用等级变量,根据ProsperRating (Alpha)与Status,获取2009年7月之后信用等级情况和违约情况的分布情况,代码实现如下:

    # 输出CreditGrade','ProsperRating (Alpha)和收入的情况分配
    prosperRating= loanData.groupby(['Status', 'ProsperRating (Alpha)'])['Status'].count().unstack(0)
    index=[ 'HR','E','D','C','B',  'A', 'AA']
    prosperRating=prosperRating.reindex(index)
    defaultRate = (prosperRating['Defaulted'] / (prosperRating['Defaulted'] + prosperRating['Completed'])).reindex(index)
    # 违约率情况
    y = list(defaultRate.values)
    fig1 = plt.figure(1)
    # fig1.set_size_inches(15.5, 7.5)
    ax1 = fig1.add_subplot(2, 1, 2)
    x = np.arange(len(index)) + 1
    ax1.bar(x, y, width=0.4)
    ax1.set_xticks(x)
    ax1.set_xticklabels(index, rotation=0, fontsize=12)
    ax1.set_xlabel('ProsperRating', fontsize=14)
    ax1.set_ylabel('违约百分率(%)', fontsize=14)
    for a, b in zip(x, y):
        plt.text(a, b + 0.001, '%.2f%%' % (b * 100), ha='center', va='bottom', fontsize=10)
    ax2 = fig1.add_subplot(2, 1, 1)
    prosperRating.plot(kind='bar', ax=ax2)
    ax2.set_xticklabels(index, rotation=0, fontsize=12)
    ax2.set_ylabel('数量', fontsize=14)
    ax2.set_xlabel('', fontsize=14)
    plt.show()
如图5-6所示。 图5-6 2009后信用等级和违约情况分布

ProsperRating (Alpha)评级越高的人违约率越低,大部分借款人的评级都在D级以上。与2009.07.01之前的信用评级对比去掉了NC级,且整体违约率比之前更低,说明平台的风控模型进行了非常有成效的调整。

5.7 过去七年违约次数多的借款人违约概率大

过去七年违约次数(DelinquenciesLast7Years)能够衡量一个人在过去七年中征信情况,违约一次或以上的人在借款时违约概率更大。

    yesno=pd.DataFrame(loanData.groupby(['Status', 'Delinquencies'])['Status'].count().unstack(0))[1:]
    delinQuenciest=pd.DataFrame(loanData.groupby(['Status', 'DelinquenciesLast7Years'])['Status'].count().unstack(0))
    rating= pd.DataFrame(delinQuenciest.values[0:26], index=list(delinQuenciest.index)[0:26], columns=list(delinQuenciest.columns))
    fig1 = plt.figure(1)
    ax1 = fig1.add_subplot(2, 1, 1)
    rating.plot(kind='line',ax=ax1, grid=True)
    ax1.set_ylabel('数量', fontsize=14)
    ax1.set_xlabel('7年内的违约次数', fontsize=14)
    ax2 = fig1.add_subplot(2, 1, 2)
    yesno.plot(kind='bar',ax=ax2)
    ax2.set_xticklabels(list(yesno.index),rotation=0,fontsize=12)
    ax2.set_xlabel('7年内有无违约', fontsize=14)
    ax2.set_ylabel('数量', fontsize=14)
    plt.legend(loc='best')
    plt.show()
如图5-7所示,DelinquenciesLast7Years的借款人中,违约笔数与未违约的笔数线贴合。从下图也可看出大部分借款人的DelinquenciesLast7Years 在1次以下,说明整个平台的风险可控。 图5-7 7年内违约次数和违约情况分布

5.8 受雇佣状态持续时间长的借款人违约概率小

受雇佣状态持续时间(EmploymentStatusDuration)可够衡量一个人工作生活的稳定情况,受雇佣状态持续时间长的违约的概率小。

    employmentStatus = pd.DataFrame(loanData.groupby(['Status', 'EmploymentStatusDuration'])['Status'].count().unstack(0))
    employmentStatus = pd.DataFrame(employmentStatus.values[0:120], index=list(employmentStatus.index)[0:120],columns=list(employmentStatus.columns))
    defaultRate=employmentStatus['Defaulted'] / (employmentStatus['Defaulted'] + employmentStatus['Completed'])
    fig1 = plt.figure(1)
    ax1 = fig1.add_subplot(2, 1, 1)
    employmentStatus.plot(kind='line', grid=True,ax=ax1)
    #ax1.set_xlabel('受雇佣状态持续时间(月)', fontsize=14)
    ax1.set_ylabel('数量', fontsize=14)
    ax2 = fig1.add_subplot(2, 1, 2)
    ax2.plot(list(defaultRate.index),list(defaultRate.values),'ko--')
    ax2.set_xlabel('受雇佣状态持续时间(月)', fontsize=14)
    ax2.set_ylabel('违约百分率(%)', fontsize=14)
    plt.legend(loc='best')
    plt.show()
如图5-8所示,随着EmploymentStatusDuration的增长,违约概率逐渐减小。 图5-8 受雇佣状态持续时间和违约情况分布图

小结

本文主要介绍数据的变量,了解主要变量的意义;根据现有数据变量,对其中几个变量进行转换,便于接下来的处理;进行了描述性分析,初步了解相关变量和违约率的分布情况。
接下来,在网贷平台Prosper2005~2014贷款数据分析(二)中,进行缺失值的处理,2009年前后模型中个变量的重要程度,以及基于2009年后模型对处于借贷状态的还款进行预测。

上一篇 下一篇

猜你喜欢

热点阅读