zoukankan      html  css  js  c++  java
  • 阿里云-建模比赛

    数据地址:https://tianchi.aliyun.com/competition/entrance/531830/information

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

    2.数据基本认知

    data_train.shape,data_test_a.shape
    ((800000, 47), (200000, 48))

    查看y值的分布

    data_train['isDefault'].value_counts().plot.bar()
    plt.show()
    data_train['isDefault'].value_counts()/len(data_train)# 0    0.800488  ,1    0.199513
    plt.pie([0.800488,0.199513],labels=[0,1],autopct='%1.2f%%')

     

    可以看出y值分布还是比较符合现实的情况

    3.区分数值型和类别性特征

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

    结合一下字段含义,看看二者所属类型是否是符合真实情况

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

    特征变量的值的个数

     1 id :800000    缺失值个数: 0
     2 loanAmnt :1540    缺失值个数: 0
     3 term :2    缺失值个数: 0
     4 interestRate :641    缺失值个数: 0
     5 installment :72360    缺失值个数: 0
     6 grade :7    缺失值个数: 0
     7 subGrade :35    缺失值个数: 0   #删除,相关性太高,如果是评分卡可以使用这个变量
     8 employmentTitle :248683    缺失值个数: 1
     9 employmentLength :11    缺失值个数: 46799   有缺失值
    10 homeOwnership :6    缺失值个数: 0
    11 annualIncome :44926    缺失值个数: 0
    12 verificationStatus :3    缺失值个数: 0
    13 issueDate :139    缺失值个数: 0
    14 isDefault :2    缺失值个数: 0
    15 purpose :14    缺失值个数: 0
    16 postCode :932    缺失值个数: 1
    17 regionCode :51    缺失值个数: 0
    18 dti :6321    缺失值个数: 239
    19 delinquency_2years :30    缺失值个数: 0
    20 ficoRangeLow :39    缺失值个数: 0
    21 ficoRangeHigh :39    缺失值个数: 0
    22 openAcc :75    缺失值个数: 0
    23 pubRec :32    缺失值个数: 0
    24 pubRecBankruptcies :11    缺失值个数: 405
    25 revolBal :71116    缺失值个数: 0
    26 revolUtil :1286    缺失值个数: 531
    27 totalAcc :134    缺失值个数: 0
    28 initialListStatus :2    缺失值个数: 0
    29 applicationType :2    缺失值个数: 0
    30 earliesCreditLine :720    缺失值个数: 0
    31 title :39644    缺失值个数: 1
    32 policyCode :1    缺失值个数: 0
    33 n0 :39    缺失值个数: 40270
    34 n1 :33    缺失值个数: 40270
    35 n2 :50    缺失值个数: 40270
    36 n2.1 :50    缺失值个数: 40270
    37 n4 :46    缺失值个数: 33239
    38 n5 :65    缺失值个数: 40270
    39 n6 :107    缺失值个数: 40270
    40 n7 :70    缺失值个数: 40270
    41 n8 :102    缺失值个数: 40271
    42 n9 :44    缺失值个数: 40270
    43 n10 :76    缺失值个数: 33239
    44 n11 :5    缺失值个数: 69752
    45 n12 :5    缺失值个数: 40270
    46 n13 :28    缺失值个数: 40270
    47 n14 :31    缺失值个数: 40270

    1.贷款金额(loanAmnt)

    # 解决Seaborn中文显示问题并调整字体大小
    sns.set(font='SimHei')
    data_train['loanAmnt'].value_counts()
    #data_train['loanAmnt'].value_counts().plot.hist()
    plt.figure(figsize=(16,12))
    plt.subplot(221)
    sub_plot_1=sns.distplot(data_train['loanAmnt'])
    sub_plot_1.set_title("训练集", fontsize=18)
    plt.subplot(222)
    sub_plot_2=sns.distplot(data_test_a['loanAmnt'])
    sub_plot_2.set_title("测试集", fontsize=18)

    (40000.0, 500.0, count    800000.000000
     mean      14416.818875
     std        8716.086178
     min         500.000000
     25%        8000.000000
     50%       12000.000000
     75%       20000.000000
     max       40000.000000

    最大值是4万最小值是500,没有异常值,但是也不符合正太分布

    2.term 借款期限

    plt.figure(figsize=(16,12))
    plt.subplot(221)
    sub_plot_1=data_train['term'].value_counts().plot.bar()
    sub_plot_1.set_title("训练集", fontsize=18)
    plt.subplot(222)
    sub_plot_2=data_test_a['term'].value_counts().plot.bar()
    sub_plot_2.set_title("测试集", fontsize=18)
    plt.show()
    
    plt.figure(figsize=(16,12))
    plt.subplot(221)
    a=data_train['term'].value_counts()/len(data_train)# 0    0.800488  ,1    0.199513
    plt.pie(a.values,labels=a.index,autopct='%1.2f%%')
    plt.title('训练集')
    
    plt.subplot(222)
    b=data_test_a['term'].value_counts()/len(data_test_a)# 0    0.800488  ,1    0.199513
    plt.pie(b.values,labels=b.index,autopct='%1.2f%%')
    plt.title('测试集')
    plt.show()
    
    a=pd.crosstab(data_train['term'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

     

     

    由此可见训练集和测试集分布基本是一致的,且贷款期限对逾期有着明显的区别

    3.贷款利率  interestRate 看着是连续型数据

    data_train['interestRate'].value_counts().plot.hist()
    plt.show()
    sns.distplot(data_train['interestRate'])
    plt.show()
    sns.distplot(np.log(data_train['interestRate']))

     

    (30.99, 5.31, count    800000.000000
     mean         13.238391
     std           4.765757
     min           5.310000
     25%           9.750000
     50%          12.740000
     75%          15.990000
     max          30.990000
     Name: interestRate, dtype: float64)

    看着后面数据还是的自己处理,可以分箱看看数据的表现如何(后面再处理吧),没有异常值,这个特征还是很有用处的,但是可能会和等分字段的较强的相关性,毕竟等级越好,说明用户越优质,利率就会越低

    4.installment 分期付款金额

    data_train['installment'].value_counts().plot.hist()
    plt.show()
    sns.distplot(data_train['installment'])
    plt.show()
    sns.distplot(np.log(data_train['installment']))

      

     

    (1715.42, 15.69, count    800000.000000
     mean        437.947723
     std         261.460393
     min          15.690000
     25%         248.450000
     50%         375.135000
     75%         580.710000
     max        1715.420000
     Name: installment, dtype: float64)

    这个每期还款金额和贷款金额,以及期限有关,没有异常值

    5.'grade','subGrade' 用户等级之类的

    plt.figure(figsize=(16,12))
    plt.subplot(221)
    sub_plot_1=data_train['grade'].value_counts().sort_index().plot.bar()
    sub_plot_1.set_title("训练集", fontsize=18)
    plt.subplot(222)
    sub_plot_2=data_test_a['grade'].value_counts().sort_index().plot.bar()
    sub_plot_2.set_title("测试集", fontsize=18)
    plt.show()
    
    plt.figure(figsize=(16,12))
    plt.subplot(221)
    a=data_train['grade'].value_counts()/len(data_train)# 0    0.800488  ,1    0.199513
    plt.pie(a.values,labels=a.index,autopct='%1.2f%%')
    plt.title('训练集')
    
    plt.subplot(222)
    b=data_test_a['grade'].value_counts()/len(data_test_a)# 0    0.800488  ,1    0.199513
    plt.pie(b.values,labels=b.index,autopct='%1.2f%%')
    plt.title('测试集')
    plt.show()
    
    a=pd.crosstab(data_train['grade'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

     

     

     看的出来等级分布基本符合正态分布,且这个特征的逾期率是单调增的

    plt.figure(figsize=(16,12))
    plt.subplot(221)
    sub_plot_1=data_train['subGrade'].value_counts().sort_index().plot.bar()
    sub_plot_1.set_title("训练集", fontsize=18)
    plt.subplot(222)
    sub_plot_2=data_test_a['subGrade'].value_counts().sort_index().plot.bar()
    sub_plot_2.set_title("测试集", fontsize=18)
    plt.show()
    
    plt.figure(figsize=(16,12))
    plt.subplot(221)
    a=data_train['subGrade'].value_counts()/len(data_train)# 0    0.800488  ,1    0.199513
    plt.pie(a.values,labels=a.index,autopct='%1.2f%%')
    plt.title('训练集')
    
    plt.subplot(222)
    b=data_test_a['subGrade'].value_counts()/len(data_test_a)# 0    0.800488  ,1    0.199513
    plt.pie(b.values,labels=b.index,autopct='%1.2f%%')
    plt.title('测试集')
    plt.show()
    
    a=pd.crosstab(data_train['subGrade'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()
    
    #还需要看一下这二者之间的相关性如何,由于二者都不是数值型特征,故暂时不可以计算相关性
    data_train['subGrade'].corr(data_train['grade'])

     

     

    觉得可以删除小等级的那个特征

    6.就业职称和就业年限 employmentTitle(就一堆数字,已经加密过的),employmentLength,现在只是在简单理解每个特征的分布,还没有到特征构造的时候(后面再进行)

    #data_train['employmentTitle'].value_counts().plot.hist()
    sns.distplot(data_train['employmentTitle'])
    data_train['employmentTitle'].value_counts().sort_values(

    这个数据是连续型,暂时留着吧

    (378351.0, -1.0, count    800000.000000
     mean      72005.261706
     std      106585.603991
     min          -1.000000
     25%         427.000000
     50%        7755.000000
     75%      117663.250000
     max      378351.000000
     Name: employmentTitle, dtype: float64)
    def employmentLength_to_int(s):
        if pd.isnull(s):
            return s
        else:
            return np.int8(s.split()[0])
    for data in [data_train, data_test_a]:
        data['employmentLength'].replace(to_replace='10+ years', value='10 years', inplace=True)
        data['employmentLength'].replace('< 1 year', '0 years', inplace=True)
        data['employmentLength'] = data['employmentLength'].apply(employmentLength_to_int)
    data_train['employmentLength'].value_counts().sort_index().plot.bar()
    plt.show()
    
    a=pd.crosstab(data_train['employmentLength'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

    这个字段已经处理过了,后面主要注意一下,上面图中,缺失值并没有显示出来,需要注意一下

    7.homeOwnership,借款人在登记时提供的房屋所有权状况,verificationStatus:验证状态,purpose:借款人在贷款申请时的贷款用途类别,

    data_train['homeOwnership'].value_counts().plot.bar()
    plt.show()
    a=pd.crosstab(data_train['homeOwnership'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

     修改之后就好多了

    这个分布奇奇怪怪的,应该是0是有房子,1是没有房子,其他的值可以并到一起

    data_train['verificationStatus'].value_counts().sort_index().plot.bar()
    plt.show()
    a=pd.crosstab(data_train['verificationStatus'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

    这个特征看着就舒服多了

    data_train['purpose'].value_counts()
    data_train['purpose'].value_counts().sort_index().plot.bar()
    plt.show()
    a=pd.crosstab(data_train['purpose'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

    乱七八糟的

    贷款时初始状态initialListStatus

    data_train['initialListStatus'].value_counts().sort_index().plot.bar()
    plt.show()
    a=pd.crosstab(data_train['initialListStatus'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

     annualIncome:年收入

     这个值异常值就很多,看看得要怎么处理

     dti:债务收入比,异常值很多,我觉得这个应该是<=100是合理的(323个值异常)

    (999.0, -1.0, count    800000.000000
     mean         18.278796
     std          11.153469
     min          -1.000000
     25%          11.790000
     50%          17.610000
     75%          24.060000
     max         999.000000
     Name: dti, dtype: float64)

     delinquency_2years:借款人过去2年信用档案中逾期30天以上的违约事件数

    离群值特别多,可以划分为0,1-3,4-7,8+

    (39.0, 0.0, count    800000.000000
     mean          0.318239
     std           0.880325
     min           0.000000
     25%           0.000000
     50%           0.000000
     75%           0.000000
     max          39.000000
     Name: delinquency_2years, dtype: float64)

     借款人在贷款发放时的fico所属的下限范围  和借款人在贷款发放时的fico所属的上限范围

     openAcc:借款人信用档案中未结信用额度的数量

    离群点多

    (86.0, 0.0, count    800000.000000
     mean         11.598020
     std           5.475286
     min           0.000000
     25%           8.000000
     50%          11.000000
     75%          14.000000
     max          86.000000
     Name: openAcc, dtype: float64)

     

     applicationType:表明贷款是个人申请还是与两个共同借款人的联合申请,这个字段严重倾斜

    data_train['applicationType'].value_counts().sort_index().plot.bar()
    plt.show()
    a=pd.crosstab(data_train['applicationType'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

    policyCode:公开可用的策略_代码=1新产品不公开可用的策略_代码=2,都是一个值,可以删除了
    data_train['policyCode'].value_counts().sort_index().plot.bar()
    plt.show()
    a=pd.crosstab(data_train['policyCode'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

    n11,和n12 ,数据严重倾斜,我做了一定处理,才能弄成这样

    data=data_train
    data['n11'].replace(2, value=1, inplace=True)
    data['n11'].replace(3, value=1, inplace=True)
    data['n11'].replace(4, value=1, inplace=True)
    data['n11'] = data['n11'].fillna(-1)
    data['n11'].value_counts().sort_index().plot.bar()
    plt.show()
    a=pd.crosstab(data['n11'],data['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()
    
    data['n12'].replace(2, value=1, inplace=True)
    data['n12'].replace(3, value=1, inplace=True)
    data['n12'].replace(4, value=1, inplace=True)
    data['n12'] = data['n12'].fillna(-1)
    data['n12'].value_counts().sort_index().plot.bar()
    plt.show()
    a=pd.crosstab(data['n12'],data['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

     

     

    8.postCode:借款人在贷款申请中提供的邮政编码的前3位数字,regionCode:地区编码

    data_train[['postCode', 'regionCode']].head()  #postCode没有什么信息,加密过的,数值特别多,后面再看看吧
    
    #主要看看地区编码
    data_train['regionCode'].value_counts()
    data_train['regionCode'].value_counts().sort_index().plot.bar()
    plt.show()
    a=pd.crosstab(data_train['regionCode'],data_train['isDefault'])
    a['坏用户占比']=a[1]/(a[0]+a[1])
    a['坏用户占比'].plot()
    plt.show()

     

    划分数值型特征和类别型

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

     数值特征中也有离散特征

    #过滤数值型类别特征
    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)

    将全部的类别特征转化,有一定层次的用标签化,其余的用独热编码['grade', 'subGrade', 'issueDate', 'earliesCreditLine']

    data_train[['grade', 'subGrade', 'issueDate', 'earliesCreditLine']].isnull().sum()
    for data in [data_train, data_test_a]:
        data['grade'] = data['grade'].map({'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7})
        
    #label-encode:subGrade,postCode,title
    # 高维类别特征需要进行转换
    from tqdm import tqdm
    from sklearn.preprocessing import LabelEncoder
    for col in tqdm(['subGrade']):
        le = LabelEncoder()
        le.fit(list(data_train[col].astype(str).values) + list(data_test_a[col].astype(str).values))
        data_train[col] = le.transform(list(data_train[col].astype(str).values))
        data_test_a[col] = le.transform(list(data_test_a[col].astype(str).values))
    print('Label Encoding 完成')

     看一下这二者之间的相关性,删除其中一个

    data_train['subGrade'].corr(data_train['grade'])
    #0.9756093687672992  可以考虑删除一样
    data_train=data_train.drop(['subGrade'],axis=1)
    data_test_a=data_test_a.drop(['subGrade'],axis=1)

    贷款发放日期'issueDate',借款人最早报告的信用额度开立的月份:earliesCreditLine,看了一下,这个这两个字段是有时间的,

    #构造字段
    #转化成时间格式
    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

    年份数,月份数

    for data in [data_train,data_test_a]:
        
        data['year']=data['earliesCreditLine'].apply(lambda x:x[4:])
        data['month']=data['earliesCreditLine'].apply(lambda x:x[:3])
        data['month']=data['month'].map({'Apr':4,
    'Aug':8,
    'Dec':12,
    'Feb':2,
    'Jan':1,
    'Jul':7,
    'Jun':6,
    'Mar':3,
    'May':5,
    'Nov':11,
    'Oct':10,
    'Sep':9})
        data['year_1']=data['issueDate'].dt.year
        data['month_1']=data['issueDate'].dt.month
        data['earliesCreditLine_month']=(data['year_1']-data['year'].astype(int))*12+(data['month_1']-data['month'].astype(int))
        data['earliesCreditLine_year']=data['year_1']-data['year'].astype(int)
        #data=data.drop(['year','month','year_1','month_1'],axis=1)

     离散型数值变量

    ['term',
     'homeOwnership',  分为0和1 吧
     'verificationStatus',
     'isDefault',  这个是label
     'initialListStatus',
     'applicationType',  虽然严重倾斜,但是区分度很好,不知道要不要留着
     'policyCode',  只有一个值,可以删除
     'n11', 也是严重倾斜,可以把>=1合并,空值为-1 
     'n12']  同上
    for data in [data_train,data_test_a]:
        data['n11'].replace(2, value=1, inplace=True)
        data['n11'].replace(3, value=1, inplace=True)
        data['n11'].replace(4, value=1, inplace=True)
        data['n11'] = data['n11'].fillna(-1)
    
    for data in [data_train,data_test_a]:
        data['n12'].replace(2, value=1, inplace=True)
        data['n12'].replace(3, value=1, inplace=True)
        data['n12'].replace(4, value=1, inplace=True)
        data['n12'] = data['n12'].fillna(-1)
    
    for data in [data_train,data_test_a]:
        data['homeOwnership'].replace(2, value=1, inplace=True)
        data['homeOwnership'].replace(3, value=1, inplace=True)
        data['homeOwnership'].replace(4, value=1, inplace=True)
        data['homeOwnership'].replace(5, value=1, inplace=True)

    删除了变量,然后做独热编码

    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)  #(800000, 54) (200000, 55)

     独热编码

    data_train=pd.get_dummies(data_train, columns=['term', 'homeOwnership', 'verificationStatus','initialListStatus','applicationType', 'n11', 'n12'], drop_first=True)
    
    data_test_a=pd.get_dummies(data_test_a, columns=['term', 'homeOwnership', 'verificationStatus','initialListStatus','applicationType', 'n11', 'n12'], drop_first=True)


  • 相关阅读:
    在HTML中使用css3实现雪人动画效果
    不一样的函数防抖和节流
    2020全球C++及系统软件技术大会成功落下帷幕
    详解flex布局做骰子案例
    LeetCode-环形链表|+环形链表||
    03:成绩排序
    02:奇数单增序列
    谁考了第k名
    【23.59%】【hdu 5883】The Best Path
    【30.53%】【hdu 5878】I Count Two Three
  • 原文地址:https://www.cnblogs.com/cgmcoding/p/13667882.html
Copyright © 2011-2022 走看看