• 阿里云的金融风控-贷款违约预测_数据分析


    一、赛题数据

    数据大家可以到官网去下载:https://tianchi.aliyun.com/competition/entrance/531830/information需要报名后才可以下载数据

    赛题以预测用户贷款是否违约为任务,数据集报名后可见并可下载,该数据来自某信贷平台的贷款记录,总数据量超过120w,包含47列变量信息,其中15列为匿名变量。为了保证比赛的公平性,将会从中抽取80万条作为训练集,20万条作为测试集A,20万条作为测试集B,同时会对employmentTitle、purpose、postCode和title等信息进行脱敏。

    字段表

    FieldDescription
    id 为贷款清单分配的唯一信用证标识
    loanAmnt 贷款金额
    term 贷款期限(year)
    interestRate 贷款利率
    installment 分期付款金额
    grade 贷款等级
    subGrade 贷款等级之子级
    employmentTitle 就业职称
    employmentLength 就业年限(年)
    homeOwnership 借款人在登记时提供的房屋所有权状况
    annualIncome 年收入
    verificationStatus 验证状态
    issueDate 贷款发放的月份
    purpose 借款人在贷款申请时的贷款用途类别
    postCode 借款人在贷款申请中提供的邮政编码的前3位数字
    regionCode 地区编码
    dti 债务收入比
    delinquency_2years 借款人过去2年信用档案中逾期30天以上的违约事件数
    ficoRangeLow 借款人在贷款发放时的fico所属的下限范围
    ficoRangeHigh 借款人在贷款发放时的fico所属的上限范围
    openAcc 借款人信用档案中未结信用额度的数量
    pubRec 贬损公共记录的数量
    pubRecBankruptcies 公开记录清除的数量
    revolBal 信贷周转余额合计
    revolUtil 循环额度利用率,或借款人使用的相对于所有可用循环信贷的信贷金额
    totalAcc 借款人信用档案中当前的信用额度总数
    initialListStatus 贷款的初始列表状态
    applicationType 表明贷款是个人申请还是与两个共同借款人的联合申请
    earliesCreditLine 借款人最早报告的信用额度开立的月份
    title 借款人提供的贷款名称
    policyCode 公开可用的策略_代码=1新产品不公开可用的策略_代码=2
    n系列匿名特征 匿名特征n0-n14,为一些贷款人行为计数特征的处理

    二、数据分析

    2.1主要内容

    • 数据总体了解:
      • 读取数据集并了解数据集大小,原始特征维度;
      • 通过info熟悉数据类型;
      • 粗略查看数据集中各特征基本统计量;
    • 缺失值和唯一值:
      • 查看数据缺失值情况
      • 查看唯一值特征情况
    • 深入数据-查看数据类型
      • 类别型数据
      • 数值型数据
        • 离散数值型数据
        • 连续数值型数据
    • 数据间相关关系
      • 特征和特征之间关系
      • 特征和目标变量之间关系
    • 用pandas_profiling生成数据报告

    2.2 代码示例

    2.2.1 导入数据分析及可视化过程需要的库

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import seaborn as sns
    import datetime
    import warnings
    warnings.filterwarnings('ignore')

    2.2.2 读取文件

    data_train = pd.read_csv('F:/python/阿里云金融风控-贷款违约预测/train.csv')
    data_test_a = pd.read_csv('F:/python/阿里云金融风控-贷款违约预测/testA.csv')

    2.2.3 总体了解

    data_test_a.shape  #(200000, 48)
    data_train.shape  #(800000, 47) 测试集是没有label,也就是y值
    data_train.columns
    data_train.info()
    data_train.describe()
    data_train.head(3).append(data_train.tail(3))

    2.2.4 查看数据集中特征缺失值,唯一值等

    print(f'There are {data_train.isnull().any().sum()} columns in train dataset with missing values.')
    #There are 22 columns in train dataset with missing values.
    # nan可视化
    missing = data_train.isnull().sum()/len(data_train)
    missing = missing[missing > 0]
    missing.sort_values(inplace=True)
    missing.plot.bar()

     查看训练集测试集中特征属性只有一值的特征

    one_value_fea = [col for col in data_train.columns if data_train[col].nunique() <= 1]
    one_value_fea_test = [col for col in data_test_a.columns if data_test_a[col].nunique() <= 1]
    print(one_value_fea,one_value_fea_test )
    #['policyCode'] ['policyCode']
    data_train['policyCode'].value_counts() 
    #1.0    800000
    #Name: policyCode, dtype: int64
    #可以删除  
    data_train=data_train.drop(['policyCode'],axis=1)
    data_test_a=data_test_a.drop(['policyCode'],axis=1)
    print(data_train.shape,data_test_a.shape)
    data_train.columns,data_test_a.columns

    2.2.5 查看特征的数值类型有哪些,对象类型有哪些

    • 特征一般都是由类别型特征和数值型特征组成
    • 类别型特征有时具有非数值关系,有时也具有数值关系。比如‘grade’中的等级A,B,C等,是否只是单纯的分类,还是A优于其他要结合业务判断。
    • 数值型特征本是可以直接入模的,但往往风控人员要对其做分箱,转化为WOE编码进而做标准评分卡等操作。从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定
    data_train.info()
    numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns)
    category_fea = list(filter(lambda x: x not in numerical_fea,list(data_train.columns)))
    ['id',
     'loanAmnt',
     'term',
     'interestRate',
     'installment',
     'employmentTitle',
     'homeOwnership',
     'annualIncome',
     'verificationStatus',
     'isDefault',
     'purpose',
     'postCode',
     'regionCode',
     'dti',
     'delinquency_2years',
     'ficoRangeLow',
     'ficoRangeHigh',
     'openAcc',
     'pubRec',
     'pubRecBankruptcies',
     'revolBal',
     'revolUtil',
     'totalAcc',
     'initialListStatus',
     'applicationType',
     'title',
     'n0',
     'n1',
     'n2',
     'n2.1',
     'n4',
     'n5',
     'n6',
     'n7',
     'n8',
     'n9',
     'n10',
     'n11',
     'n12',
     'n13',
     'n14']
    ['grade', 'subGrade', 'employmentLength', 'issueDate', 'earliesCreditLine']

    数值型变量分析,数值型肯定是包括连续型变量和离散型变量的,找出来

    #过滤数值型类别特征
    def get_numerical_serial_fea(data,feas):
        '''
        目的:划分数值型变量中的连续变量和分类变量
        data:需要划分的数据集
        feas:需要区分的特征的名称
        返回:连续变量和分类变量 的list集合
        '''
        numerical_serial_fea = []
        numerical_noserial_fea = []
        for fea in feas:
            temp = data[fea].nunique()
            if temp <= 10:
                numerical_noserial_fea.append(fea)
                continue
            numerical_serial_fea.append(fea)
        return numerical_serial_fea,numerical_noserial_fea
    numerical_serial_fea,numerical_noserial_fea = get_numerical_serial_fea(data_train,numerical_fea)
    numerical_serial_fea,numerical_noserial_fea
    (['id',
      'loanAmnt',
      'interestRate',
      'installment',
      'employmentTitle',
      'annualIncome',
      'purpose',
      'postCode',
      'regionCode',
      'dti',
      'delinquency_2years',
      'ficoRangeLow',
      'ficoRangeHigh',
      'openAcc',
      'pubRec',
      'pubRecBankruptcies',
      'revolBal',
      'revolUtil',
      'totalAcc',
      'title',
      'n0',
      'n1',
      'n2',
      'n2.1',
      'n4',
      'n5',
      'n6',
      'n7',
      'n8',
      'n9',
      'n10',
      'n13',
      'n14'],
     ['term',
      'homeOwnership',
      'verificationStatus',
      'isDefault',
      'initialListStatus',
      'applicationType',
      'n11',
      'n12'])

    在仔细查看每个类别变量

    category_col=category_fea+numerical_noserial_fea
    for i in category_col:
        print(i,data_train[i].value_counts())
    grade B    233690
    C    227118
    A    139661
    D    119453
    E     55661
    F     19053
    G      5364
    Name: grade, dtype: int64
    subGrade C1    50763
    B4    49516
    B5    48965
    B3    48600
    C2    47068
    C3    44751
    C4    44272
    B2    44227
    B1    42382
    C5    40264
    A5    38045
    A4    30928
    D1    30538
    D2    26528
    A1    25909
    D3    23410
    A3    22655
    A2    22124
    D4    21139
    D5    17838
    E1    14064
    E2    12746
    E3    10925
    E4     9273
    E5     8653
    F1     5925
    F2     4340
    F3     3577
    F4     2859
    F5     2352
    G1     1759
    G2     1231
    G3      978
    G4      751
    G5      645
    Name: subGrade, dtype: int64
    employmentLength 10+ years    262753
    2 years       72358
    < 1 year      64237
    3 years       64152
    1 year        52489
    5 years       50102
    4 years       47985
    6 years       37254
    8 years       36192
    7 years       35407
    9 years       30272
    Name: employmentLength, dtype: int64
    issueDate 2016-03-01    29066
    2015-10-01    25525
    2015-07-01    24496
    2015-12-01    23245
    2014-10-01    21461
                  ...  
    2007-08-01       23
    2007-07-01       21
    2008-09-01       19
    2007-09-01        7
    2007-06-01        1
    Name: issueDate, Length: 139, dtype: int64
    earliesCreditLine Aug-2001    5567
    Sep-2003    5403
    Aug-2002    5403
    Oct-2001    5258
    Aug-2000    5246
                ... 
    Nov-1953       1
    Sep-1957       1
    Mar-1958       1
    Mar-1957       1
    Aug-1946       1
    Name: earliesCreditLine, Length: 720, dtype: int64
    term 3    606902
    5    193098
    Name: term, dtype: int64
    homeOwnership 0    395732
    1    317660
    2     86309
    3       185
    5        81
    4        33
    Name: homeOwnership, dtype: int64
    verificationStatus 1    309810
    2    248968
    0    241222
    Name: verificationStatus, dtype: int64
    isDefault 0    640390
    1    159610
    Name: isDefault, dtype: int64
    initialListStatus 0    466438
    1    333562
    Name: initialListStatus, dtype: int64
    applicationType 0    784586
    1     15414
    Name: applicationType, dtype: int64
    n11 0.0    729682
    1.0       540
    2.0        24
    4.0         1
    3.0         1
    Name: n11, dtype: int64
    n12 0.0    757315
    1.0      2281
    2.0       115
    3.0        16
    4.0         3
    Name: n12, dtype: int64

     数值连续型变量分析

    #每个数字特征得分布可视化,这个如果数据量太大,不建议这样处理
    f = pd.melt(data_train, value_vars=numerical_serial_fea)
    g = sns.FacetGrid(f, col="variable",  col_wrap=2, sharex=False, sharey=False)
    g = g.map(sns.distplot, "value")

    • 查看某一个数值型变量的分布,查看变量是否符合正态分布,如果不符合正太分布的变量可以log化后再观察下是否符合正态分布。
    • 如果想统一处理一批数据变标准化 必须把这些之前已经正态化的数据提出
    • 正态化的原因:一些情况下正态非正态可以让模型更快的收敛,一些模型要求数据正态(eg. GMM、KNN),保证数据不要过偏态即可,过于偏态可能会影响模型预测结果。
    #Ploting Transaction Amount Values Distribution
    plt.figure(figsize=(16,12))
    plt.suptitle('Transaction Values Distribution', fontsize=22)
    plt.subplot(221)
    sub_plot_1 = sns.distplot(data_train['loanAmnt'])
    sub_plot_1.set_title("loanAmnt Distribuition", fontsize=18)
    sub_plot_1.set_xlabel("")
    sub_plot_1.set_ylabel("Probability", fontsize=15)
    
    plt.subplot(222)
    sub_plot_2 = sns.distplot(np.log(data_train['loanAmnt']))
    sub_plot_2.set_title("loanAmnt (Log) Distribuition", fontsize=18)
    sub_plot_2.set_xlabel("")
    sub_plot_2.set_ylabel("Probability", fontsize=15)

     

    总结:

    • 上面我们用value_counts()等函数看了特征属性的分布,但是图表是概括原始信息最便捷的方式。
    • 数无形时少直觉。
    • 同一份数据集,在不同的尺度刻画上显示出来的图形反映的规律是不一样的。python将数据转化成图表,但结论是否正确需要由你保证

    2.2.6 变量分布可视化

    单一变量分布可视化

    #拿就业年限出来举例子,取前面20个,也可以这样子表达,data_train["employmentLength"].value_counts(dropna=False).plot.bar()
    plt.figure(figsize=(8, 8))
    sns.barplot(data_train["employmentLength"].value_counts(dropna=False)[:20],
                data_train["employmentLength"].value_counts(dropna=False).keys()[:20])
    plt.show()

     由此可见用户的工作年限还是很长的

    根绝y值不同可视化x某个特征的分布

    • 首先查看类别型变量在不同y值上的分布
    train_loan_fr = data_train.loc[data_train['isDefault'] == 1]
    train_loan_nofr = data_train.loc[data_train['isDefault'] == 0]
    
    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 8))
    train_loan_fr.groupby('grade')['grade'].count().plot(kind='barh', ax=ax1, title='Count of grade fraud')
    train_loan_nofr.groupby('grade')['grade'].count().plot(kind='barh', ax=ax2, title='Count of grade non-fraud')
    train_loan_fr.groupby('employmentLength')['employmentLength'].count().plot(kind='barh', ax=ax3, title='Count of employmentLength fraud')
    train_loan_nofr.groupby('employmentLength')['employmentLength'].count().plot(kind='barh', ax=ax4, title='Count of employmentLength non-fraud')
    plt.show()

     其实我觉得这样子表达反而更加清晰明了

    #使用列联表,然后再画图,对于类别型特征特别有效
    pd.crosstab(data_train['grade'],data_train["isDefault"]).plot(kind="bar")

     

    还可以直接画逾期率

    a=pd.crosstab(data_train['grade'],data_train["isDefault"])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()

    • 其次查看连续型变量在不同y值上的分布
    #首先找到 label=1 的数据,在看这个数据的loanAmnt字段,使用apply将这个字段全部log化,然后在画图
    fig, ((ax1, ax2)) = plt.subplots(1, 2, figsize=(15, 6))
    data_train.loc[data_train['isDefault'] == 1] 
        ['loanAmnt'].apply(np.log) 
        .plot(kind='hist',
              bins=10,
              title='Log Loan Amt - Fraud',
              color='r',
              xlim=(-3, 10),
             ax= ax1)
    data_train.loc[data_train['isDefault'] == 0] 
        ['loanAmnt'].apply(np.log) 
        .plot(kind='hist',
              bins=10,
              title='Log Loan Amt - Not Fraud',
              color='b',
              xlim=(-3, 10),
             ax=ax2)

     

     当然也可以先cut,然后再做列联表

    import pandas as pd 
    data=pd.DataFrame()
    data['loanAmnt']=pd.qcut(data_train['loanAmnt'].apply(np.log),10)
    data['isDefault']=data_train['isDefault']
    pd.crosstab(data['loanAmnt'],data['isDefault']).plot(kind="bar")
    plt.show()
    a=pd.crosstab(data['loanAmnt'],data['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

     

     

    官方给出了另外一种方法

    total = len(data_train)
    total_amt = data_train.groupby(['isDefault'])['loanAmnt'].sum().sum()  #计算全部的金额
    plt.figure(figsize=(12,5))
    plt.subplot(121)##1代表行,2代表列,所以一共有2个图,1代表此时绘制第一个图。
    plot_tr = sns.countplot(x='isDefault',data=data_train)#data_train‘isDefault’这个特征每种类别的数量**  也就是计数
    plot_tr.set_title("Fraud Loan Distribution 
     0: good user | 1: bad user", fontsize=14)
    plot_tr.set_xlabel("Is fraud by count", fontsize=16)
    plot_tr.set_ylabel('Count', fontsize=16)
    for p in plot_tr.patches:
        height = p.get_height()
        plot_tr.text(p.get_x()+p.get_width()/2.,
                height + 3,
                '{:1.2f}%'.format(height/total*100),
                ha="center", fontsize=15) 
     
    #反正上面就是计算label的种类的值和占比
      
    percent_amt = (data_train.groupby(['isDefault'])['loanAmnt'].sum())  #分别计算每个种类的金额之和
    percent_amt = percent_amt.reset_index()
    plt.subplot(122)
    plot_tr_2 = sns.barplot(x='isDefault', y='loanAmnt',  dodge=True, data=percent_amt)
    plot_tr_2.set_title("Total Amount in loanAmnt  
     0: good user | 1: bad user", fontsize=14)
    plot_tr_2.set_xlabel("Is fraud by percent", fontsize=16)
    plot_tr_2.set_ylabel('Total Loan Amount Scalar', fontsize=16)
    for p in plot_tr_2.patches:
        height = p.get_height()
        plot_tr_2.text(p.get_x()+p.get_width()/2.,
                height + 3,
                '{:1.2f}%'.format(height/total_amt * 100),
                ha="center", fontsize=15)     

     

    2.2.7 时间格式数据处理及查看

    #转化成时间格式
    data_train['issueDate'] = pd.to_datetime(data_train['issueDate'],format='%Y-%m-%d')
    startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d')
    data_train['issueDateDT'] = data_train['issueDate'].apply(lambda x: x-startdate).dt.days
    
    
    #转化成时间格式
    data_test_a['issueDate'] = pd.to_datetime(data_train['issueDate'],format='%Y-%m-%d')
    startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d')
    data_test_a['issueDateDT'] = data_test_a['issueDate'].apply(lambda x: x-startdate).dt.days
    
    
    plt.hist(data_train['issueDateDT'], label='train');
    plt.hist(data_test_a['issueDateDT'], label='test');
    plt.legend();
    plt.title('Distribution of issueDateDT dates');
    #train 和 test issueDateDT 日期有重叠 所以使用基于时间的分割进行验证是不明智的

    2.2.8 掌握透视图可以让我们更好的了解数据

    #透视图 索引可以有多个,“columns(列)”是可选的,聚合函数aggfunc最后是被应用到了变量“values”中你所列举的项目上。
    pivot = pd.pivot_table(data_train, index=['grade'], columns=['issueDateDT'], values=['loanAmnt'], aggfunc=np.sum)

    2.3.9 用pandas_profiling生成数据报告

    import pandas_profiling
    pfr = pandas_profiling.ProfileReport(data_train)
    pfr.to_file("./example.html")

     跑这个需要时间有点长,就不跑了

  • 相关阅读:
    算法洗脑系列(8篇)——第五篇 分治思想
    算法洗脑系列(8篇)——第七篇 动态规划
    算法洗脑系列(8篇)——第四篇 枚举思想
    8天学通MongoDB——第二天 细说增删查改
    12篇学通C#网络编程——第一篇 基础之进程线程
    算法系列15天速成——第十五天 图【下】(大结局)
    算法洗脑系列(8篇)——第一篇 递推思想
    8天学通MongoDB——第三天 细说高级操作
    8天学通MongoDB——第四天 索引操作
    算法洗脑系列(8篇)——第八篇 概率思想
  • 原文地址:https://www.cnblogs.com/cgmcoding/p/13651921.html
Copyright © 2020-2023  润新知