精华内容
下载资源
问答
  • 信用评分模型(R语言)

    万次阅读 多人点赞 2016-04-23 10:45:57
    本文详细的介绍了信用评分卡的开发流程,开发语言为R语言,python版本请见:一行代码搞定信用评分模型(python) python版实例和数据请见我的github:https://github.com/chengsong990020186/CreditScoreModel,如...

    信用评分

    2016年1月10日

    本文详细的介绍了信用评分卡的开发流程,开发语言为R语言,python版本请见:一行代码搞定信用评分模型(python)

    python版实例和数据请见我的github:https://github.com/chengsong990020186/CreditScoreModel,如觉得实用就点下star,欢迎大家一起学习交流进步。

    一、数据准备

    1、 问题的准备

    •            目标:要完成一个评分卡,通过预测某人在未来两年内将会经历财务危机的可能性来提高信用评分的效果,帮助贷款人做出最好的决策。

    •            背景:

    –            银行在市场经济中起到至关重要的作用。他们决定谁在什么条件下可以得到融资,并且可以创造或打破投资决策。而市场、社会,以及个人和企业都需要获得贷款。

    –            信用评分算法,对默认可能性进行猜测,这是银行用来判断贷款是否应该被授予的方法。

    •            准备:

    –            首先是基于个人借贷的场景,确定“违约”的定义: 根据新的Basel II Capital Accord(巴塞尔二资本协议),一般逾期90天算作违约。

    –            在判别指标上,选择使用历史最大违约天数。

     

    2、数据的获取与整合

    •            数据来源:数据来自Kaggle,cs-training.csv是有15万条的样本数据,下图可以看到这份数据的大致情况。下载地址  为:https://www.kaggle.com/c/GiveMeSomeCredit/data 

                 如果下载出现问题,可以在此下载数据:http://download.csdn.net/download/csqazwsxedc/10228999

    •            数据描述:数据属于个人消费类贷款,只考虑评分卡最终实施时能够使用到的数据应从如下一些方面获取数据:

    –            基本属性:包括了借款人当时的年龄。

    –            偿债能力:包括了借款人的月收入、负债比率。

    –            信用往来:两年内35-59天逾期次数、两年内60-89天逾期次数、两年内90天或高于90天逾期的次数。

    –            财产状况:包括了开放式信贷和贷款数量、不动产贷款或额度数量。

    –            贷款属性:暂无。

    –            其他因素:包括了借款人的家属数量(不包括本人在内)。

    •            原始变量:

    变量名

    变量类型

    变量描述

    SeriousDlqin2yrs

    Y/N

    超过90天或更糟的逾期拖欠

    RevolvingUtilizationOf

    UnsecuredLines

    percentage

    无担保放款的循环利用:除了不动产和像车贷那样除以信用额度总和的无分期付款债务的信用卡和个人信用额度总额

    age

    integer

    借款人当时的年龄

    NumberOfTime30-59DaysPastDueNotWorse

    integer

    35-59天逾期但不糟糕次数

    DebtRatio

    percentage

    负债比率

    MonthlyIncome

    real

    月收入

    NumberOf

    OpenCreditLinesAndLoans

    integer

    开放式信贷和贷款数量,开放式贷款(分期付款如汽车贷款或抵押贷款)和信贷(如信用卡)的数量

    NumberOfTimes90DaysLate

    integer

    90天逾期次数:借款者有90天或更高逾期的次数

    NumberRealEstateLoans

    OrLines

    integer

    不动产贷款或额度数量:抵押贷款和不动产放款包括房屋净值信贷额度

    NumberOfTime60-89DaysPastDueNotWorse

    integer

    60-89天逾期但不糟糕次数:借款人在在过去两年内有60-89天逾期还款但不糟糕的次数

    NumberOfDependents

    integer

    家属数量:不包括本人在内的家属数量

    •            时间窗口:自变量的观察窗口为过去两年,因变量表现窗口为未来两年。

    二、数据处理

    首先去掉原数据中的顺序变量,即第一列的id变量。由于要预测的是SeriousDlqin2yrs变量,因此将其设为响应变量y,其他分别设为x1~x10变量。

    1、缺失值分析及处理

    在得到数据集后,我们需要观察数据的分布情况,因为很多的模型对缺失值敏感,因此观察是否有缺失值是其中很重要的一个步骤。在正式分析前,我们先通过图形进行对观测字段的缺失情况有一个直观的感受。

     

      matrixplot(traindata)

     

    
     

     

      md.pattern(traindata)
    ##        y x1 x2 x3 x4 x6 x7 x8 x9  x10    x5      
    ## 120269 1  1  1  1  1  1  1  1  1    1     1     0
    ##  25807 1  1  1  1  1  1  1  1  1    1     0     1
    ##   3924 1  1  1  1  1  1  1  1  1    0     0     2
    ##        0  0  0  0  0  0  0  0  0 3924 29731 33655

     

    
     

    利用matrixplot函数对缺失值部分进行可视化展示,上图中浅色表示值小,深色表示值大,而默认缺失值为红色。因此可以看到x5变量和x10变量,即MonthlyIncome变量和NumberOfDependents两个变量存在缺失值,具体确实情况可以见上表,monthlyincome列共有缺失值29731个,numberofdependents有3924个。

    对于缺失值的处理方法非常多,例如基于聚类的方法,基于回归的方法,基于均值的方法,其中最简单的方法是直接移除,但是在本文中因为缺失值所占比例较高,直接移除会损失大量观测,因此并不是最合适的方法。在这里,我们使用KNN方法对缺失值进行填补。

     

    traindata<-knnImputation(traindata,k=10,meth = "weighAvg")
    

     

    2、异常值分析及处理

    关于异常值的检测,这里简单介绍以下一些检测方法:

    •       单变量异常值检测:在R语言中使用函数boxplot.stats()可以实现单变量检测,该函数根据返回的统计数据生成箱线图。在上述函数的返回结果中,有一个参数out,它是由异常值组成的列表。更明确的说就是里面列出了箱线图中箱须线外面的数据点。比如我们可以查看月收入分布,第一幅图为没有删除异常值的箱线图。第二幅箱线图删除异常值后,可以发现月收入主要集中分布在3000-8000之间。但是在这份分析报告中,因为我们对业务尚不熟悉,不好将大于8000的数据直接归为异常值,因此对该变量未做处理。

    •       使用LOF(局部异常因子)检测异常值:LOF(局部异常因子)是一种基于密度识别异常值的算法。算法实现是:将一个点的局部密度与分布在它周围的点的密度相比较,如果前者明显的比后者小,那么这个点相对于周围的点来说就处于一个相对比较稀疏的区域,这就表明该点事一个异常值。LOF算法的缺点是它只对数值型数据有效。包‘DMwR’和包‘dprep’中的lofactor()可以计算LOF算法中的局部异常因子。

    •       通过聚类检测异常值:检测异常值的另外一种方式就是聚类。先把数据聚成不同的类,选择不属于任何类的数据作为异常值。例如,基于密度的聚类DBSCAN算法的实现就是将与数据稠密区域紧密相连的数据对象划分为一个类,因此与其他对象分离的数据就会作为异常值。也可以使用K均值算法实现异常值的检测。首先通过把数据划分为k组,划分方式是选择距离各自簇中心最近的点为一组;然后计算每个对象和对应的簇中心的距离(或者相似度),并挑出拥有最大的距离的点作为异常值。

    首先对于x2变量,即客户的年龄,我们可以定量分析,发现有以下值:

     

    unique(traindata$x2)
    ##  [1]  45  40  38  30  49  74  57  39  27  51  46  76  64  78  53  43  25
    ## [18]  32  58  50  69  24  28  62  42  75  26  52  41  81  31  68  70  73
    ## [35]  29  55  35  72  60  67  36  56  37  66  83  34  44  48  61  80  47
    ## [52]  59  77  63  54  33  79  65  86  92  23  87  71  22  90  97  84  82
    ## [69]  91  89  85  88  21  93  96  99  94  95 101  98 103 102 107 105   0
    ## [86] 109

     

    
     

     

    可以看到年龄中存在0值,显然是异常值,予以剔除。

     

    traindata<-traindata[-which(traindata$x2==0),] 

     

     

    而对于x3,x7,x9三个变量,由下面的箱线图可以看出,均存在异常值,且由unique函数可以得知均存在96、98两个异常值,因此予以剔除。同时会发现剔除其中一个变量的96、98值,其他变量的96、98两个值也会相应被剔除

     

    ##  [1]  2  0  1  3  4  5  7 10  6 98 12  8  9 96 13 11
    ##  [1]  0  1  3  2  5  4 98 10  9  6  7  8 15 96 11 13 14 17 12
    ##  [1]  0  1  2  5  3 98  4  6  7  8 96 11  9

     

    
     

     

    其它变量占不作处理。

    三、变量分析

    1、单变量分析

    我们可以简单地看下部分变量的分布,比如对于age变量,如下图:

     

    ggplot(traindata, aes(x = x2, y = ..density..)) + geom_histogram(fill = "blue", colour = "grey60", size = 0.2, alpha = 0.2) + geom_density()

     

     

    可以看到年龄变量大致呈正态分布,符合统计分析的假设。再比如月收入变量,也可以做图观察观察,如下:

     

    ggplot(traindata, aes(x = x5, y = ..density..)) + geom_histogram(fill = "blue", colour = "grey60", size = 0.2, alpha = 0.2) + geom_density() + xlim(1, 20000)

     

     

    月收入也大致呈正态分布,符合统计分析的需要。

     

    2、变量之间的相关性

    建模之前首先得检验变量之间的相关性,如果变量之间相关性显著,会影响模型的预测效果。下面通过corrplot函数,画出各变量之间,包括响应变量与自变量的相关性。

     

    cor1<-cor(traindata[,1:11])
    corrplot(cor1)

     

     

     

     

    corrplot(cor1,method = "number")

     

    由上图可以看出,各变量之间的相关性是非常小的。其实Logistic回归同样需要检验多重共线性问题,不过此处由于各变量之间的相关性较小,可以初步判断不存在多重共线性问题,当然我们在建模后还可以用VIF(方差膨胀因子)来检验多重共线性问题。如果存在多重共线性,即有可能存在两个变量高度相关,需要降维或剔除处理。

    四、切分数据集

     

    table(traindata$y)
    ## 
    ##      0      1 
    ## 139851   9879

     

    
     

     

    由上表看出,对于响应变量SeriousDlqin2yrs,存在明显的类失衡问题,SeriousDlqin2yrs等于1的观测为9879,仅为所有观测值的6.6%。因此我们需要对非平衡数据进行处理,在这里可以采用SMOTE算法,用R对稀有事件进行超级采样。

    我们利用caret包中的createDataPartition(数据分割功能)函数将数据随机分成相同的两份。

     

    set.seed(1234) 
    splitIndex<-createDataPartition(traindata$y,time=1,p=0.5,list=FALSE) 
    train<-traindata[splitIndex,] 
    test<-traindata[-splitIndex,] 

     

    
     

     

    对于分割后的训练集和测试集均有74865个数据,分类结果的平衡性如下:

     

    prop.table(table(train$y)) 

     

     

    ## 
    ##          0          1 
    ## 0.93314633 0.06685367

     

     

     

    prop.table(table(test$y)) 
    ## 
    ##          0          1 
    ## 0.93489615 0.06510385

     

     

     

    两者的分类结果是平衡的,仍然有6.6%左右的代表,我们仍然处于良好的水平。因此可以采用这份切割的数据进行建模及预测。

    五、Logistic回归

    Logistic回归在信用评分卡开发中起到核心作用。由于其特点,以及对自变量进行了证据权重转换(WOE),Logistic回归的结果可以直接转换为一个汇总表,即所谓的标准评分卡格式。

    2、建立模型

    首先利用glm函数对所有变量进行Logistic回归建模,模型如下

     

    fit<-glm(y~.,train,family = "binomial")
    summary(fit)
    ## 
    ## Call:
    ## glm(formula = y ~ ., family = "binomial", data = train)
    ## 
    ## Deviance Residuals: 
    ##     Min       1Q   Median       3Q      Max  
    ## -4.6144  -0.3399  -0.2772  -0.2240   3.6997  
    ## 
    ## Coefficients:
    ##               Estimate Std. Error z value Pr(>|z|)    
    ## (Intercept) -1.812e+00  6.411e-02 -28.268  < 2e-16 ***
    ## x1          -1.846e-05  8.972e-05  -0.206 0.836948    
    ## x2          -2.861e-02  1.276e-03 -22.428  < 2e-16 ***
    ## x3           5.767e-01  1.564e-02  36.867  < 2e-16 ***
    ## x4          -2.321e-05  1.538e-05  -1.509 0.131224    
    ## x5          -1.355e-05  3.845e-06  -3.524 0.000425 ***
    ## x6          -2.769e-03  3.798e-03  -0.729 0.466051    
    ## x7           8.468e-01  2.429e-02  34.855  < 2e-16 ***
    ## x8           8.620e-02  1.599e-02   5.393 6.94e-08 ***
    ## x9           8.294e-01  3.338e-02  24.848  < 2e-16 ***
    ## x10          5.126e-02  1.388e-02   3.694 0.000221 ***
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ## 
    ## (Dispersion parameter for binomial family taken to be 1)
    ## 
    ##     Null deviance: 36747  on 74864  degrees of freedom
    ## Residual deviance: 29793  on 74854  degrees of freedom
    ## AIC: 29815
    ## 
    ## Number of Fisher Scoring iterations: 6

     

    
     

              可以看出,利用全变量进行回归,模型拟合效果并不是很好,其中x1,x4,x6三个变量的p值未能通过检验,在此直接剔除这三个变量,利用剩余的变量对y进行回归。

     

    fit2<-glm(y~x2+x3+x5+x7+x8+x9+x10,train,family = "binomial")
    summary(fit2)
    ## 
    ## Call:
    ## glm(formula = y ~ x2 + x3 + x5 + x7 + x8 + x9 + x10, family = "binomial", 
    ##     data = train)
    ## 
    ## Deviance Residuals: 
    ##     Min       1Q   Median       3Q      Max  
    ## -4.6223  -0.3402  -0.2777  -0.2239   3.5868  
    ## 
    ## Coefficients:
    ##               Estimate Std. Error z value Pr(>|z|)    
    ## (Intercept) -1.825e+00  6.320e-02 -28.873  < 2e-16 ***
    ## x2          -2.894e-02  1.252e-03 -23.120  < 2e-16 ***
    ## x3           5.742e-01  1.544e-02  37.187  < 2e-16 ***
    ## x5          -1.185e-05  3.513e-06  -3.373 0.000744 ***
    ## x7           8.500e-01  2.401e-02  35.397  < 2e-16 ***
    ## x8           7.494e-02  1.420e-02   5.276 1.32e-07 ***
    ## x9           8.306e-01  3.338e-02  24.883  < 2e-16 ***
    ## x10          5.169e-02  1.386e-02   3.730 0.000192 ***
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ## 
    ## (Dispersion parameter for binomial family taken to be 1)
    ## 
    ##     Null deviance: 36747  on 74864  degrees of freedom
    ## Residual deviance: 29797  on 74857  degrees of freedom
    ## AIC: 29813
    ## 
    ## Number of Fisher Scoring iterations: 6

     

    
     

          第二个回归模型所有变量都通过了检验,甚至AIC值(赤池信息准则)更小,所有模型的拟合效果更好些。

     

    3、模型评估

    通常一个二值分类器可以通过ROC(Receiver Operating Characteristic)曲线和AUC值来评价优劣。

    很多二元分类器会产生一个概率预测值,而非仅仅是0-1预测值。我们可以使用某个临界点(例如0.5),以划分哪些预测为1,哪些预测为0。得到二元预测值后,可以构建一个混淆矩阵来评价二元分类器的预测效果。所有的训练数据都会落入这个矩阵中,而对角线上的数字代表了预测正确的数目,即true positive + true nagetive。同时可以相应算出TPR(真正率或称为灵敏度)和TNR(真负率或称为特异度)。我们主观上希望这两个指标越大越好,但可惜二者是一个此消彼涨的关系。除了分类器的训练参数,临界点的选择,也会大大的影响TPR和TNR。有时可以根据具体问题和需要,来选择具体的临界点。

    如果我们选择一系列的临界点,就会得到一系列的TPR和TNR,将这些值对应的点连接起来,就构成了ROC曲线。ROC曲线可以帮助我们清楚的了解到这个分类器的性能表现,还能方便比较不同分类器的性能。在绘制ROC曲线的时候,习惯上是使用1-TNR作为横坐标即FPR(false positive rate),TPR作为纵坐标。这是就形成了ROC曲线。

    AUCArea Under Curve)被定义为ROC曲线下的面积,显然这个面积的数值不会大于1。又由于ROC曲线一般都处于y=x这条直线的上方,所以AUC的取值范围在0.51之间。使用AUC值作为评价标准是因为很多时候ROC曲线并不能清晰的说明哪个分类器的效果更好,而作为一个数值,对应AUC更大的分类器效果更好。

     

    下面首先利用模型对test数据进行预测,生成概率预测值

    pre <- predict(fit2,test)
    
     

     

    在R中,可以利用pROC包,它能方便比较两个分类器,还能自动标注出最优的临界点,图看起来也比较漂亮。在下图中最优点FPR=1-TNR=0.845,TPR=0.638,AUC值为0.8102,说明该模型的预测效果还是不错的,正确较高。

     

    modelroc <- roc(test$y,pre)
    plot(modelroc, print.auc=TRUE, auc.polygon=TRUE, grid=c(0.1, 0.2),
         grid.col=c("green", "red"), max.auc.polygon=TRUE,
         auc.polygon.col="skyblue", print.thres=TRUE)

     

    
     

     

    ## 
    ## Call:
    ## roc.default(response = test$y, predictor = pre)
    ## 
    ## Data: pre in 69991 controls (test$y 0) < 4874 cases (test$y 1).
    ## Area under the curve: 0.8102

     

    六、WOE转换

    证据权重(Weight of Evidence,WOE)转换可以将Logistic回归模型转变为标准评分卡格式。引入WOE转换的目的并不是为了提高模型质量,只是一些变量不应该被纳入模型,这或者是因为它们不能增加模型值,或者是因为与其模型相关系数有关的误差较大,其实建立标准信用评分卡也可以不采用WOE转换。这种情况下,Logistic回归模型需要处理更大数量的自变量。尽管这样会增加建模程序的复杂性,但最终得到的评分卡都是一样的。

    用WOE(x)替换变量x。WOE()=ln[(违约/总违约)/(正常/总正常)]。

    通过上述的Logistic回归,剔除x1,x4,x6三个变量,对剩下的变量进行WOE转换。

     

    1、进行分箱

    age变量(x2):

     

       cutx2= c(-Inf,30,35,40,45,50,55,60,65,75,Inf)
       plot(cut(train$x2,cutx2))

     

    
     

    NumberOfTime30-59DaysPastDueNotWorse变量(x3):

     

     

       cutx3 = c(-Inf,0,1,3,5,Inf)
       plot(cut(train$x3,cutx3))

     

     

    
     

     

    MonthlyIncome变量(x5):

     

     

       cutx5 = c(-Inf,1000,2000,3000,4000,5000,6000,7500,9500,12000,Inf)
       plot(cut(train$x5,cutx5))

     

     

    
     

     

     

     

     

    NumberOfTimes90DaysLate变量(x7):

       cutx7 = c(-Inf,0,1,3,5,10,Inf)
       plot(cut(train$x7,cutx7))

    NumberRealEstateLoansOrLines变量(x8):

       cutx8= c(-Inf,0,1,2,3,5,Inf)
       plot(cut(train$x8,cutx8))
    
     

     

     

     

    NumberOfTime60-89DaysPastDueNotWorse变量(x9):

       cutx9 = c(-Inf,0,1,3,5,Inf)
       plot(cut(train$x9,cutx9))
    
     

     

     

     

    NumberOfDependents变量(x10):

       cutx10 = c(-Inf,0,1,2,3,5,Inf)
       plot(cut(train$x10,cutx10))

     

    
     

     

     

     

    2、计算WOE值

     

    计算WOE的函数

       totalgood = as.numeric(table(train$y))[1]
       totalbad = as.numeric(table(train$y))[2]
       getWOE <- function(a,p,q)
       {
          Good <- as.numeric(table(train$y[a > p & a <= q]))[1]
          Bad <- as.numeric(table(train$y[a > p & a <= q]))[2]
          WOE <- log((Bad/totalbad)/(Good/totalgood),base = exp(1))
          return(WOE)
      }

    比如age变量(x2)

       Agelessthan30.WOE=getWOE(train$x2,-Inf,30)
       Age30to35.WOE=getWOE(train$x2,30,35)
       Age35to40.WOE=getWOE(train$x2,35,40)
       Age40to45.WOE=getWOE(train$x2,40,45)
       Age45to50.WOE=getWOE(train$x2,45,50)
       Age50to55.WOE=getWOE(train$x2,50,55)
       Age55to60.WOE=getWOE(train$x2,55,60)
       Age60to65.WOE=getWOE(train$x2,60,65)
       Age65to75.WOE=getWOE(train$x2,65,75)
       Agemorethan.WOE=getWOE(train$x2,75,Inf)
       age.WOE=c(Agelessthan30.WOE,Age30to35.WOE,Age35to40.WOE,Age40to45.WOE,Age45to50.WOE,
                Age50to55.WOE,Age55to60.WOE,Age60to65.WOE,Age65to75.WOE,Agemorethan.WOE)
       age.WOE
    ##  [1]  0.57432879  0.52063157  0.34283924  0.24251193  0.22039521
    ##  [6]  0.07194294 -0.25643603 -0.55868003 -0.94144504 -1.28914527

    NumberOfTime30-59DaysPastDueNotWorse变量(x3)

    ## [1] -0.5324915  0.9106018  1.7645290  2.4432903  2.5682332

    MonthlyIncome变量(x5)

    ##  [1] -1.128862326  0.448960482  0.312423080  0.350846777  0.247782295
    ##  [6]  0.114417168 -0.001808106 -0.237224039 -0.389158800 -0.462438653

    NumberOfTimes90DaysLate变量(x7)

    ## [1] -0.3694044  1.9400973  2.7294448  3.3090003  3.3852925  2.3483738

    NumberRealEstateLoansOrLines变量(x8)

    ## [1]  0.21490691 -0.24386987 -0.15568385  0.02906876  0.41685234  1.12192809

    NumberOfTime60-89DaysPastDueNotWorse变量(x9)

    ## [1] -0.2784605  1.8329078  2.7775343  3.5805174  3.4469860

    NumberOfDependents变量(x10)

    ## [1] -0.15525081  0.08669961  0.19618098  0.33162486  0.40469824  0.76425365
    
     

    3、对变量进行WOE变换

    如age变量(x2)

     

    如age变量(x2)

        tmp.age <- 0
        for(i in 1:nrow(train)) {
          if(train$x2[i] <= 30)
            tmp.age[i] <- Agelessthan30.WOE
          else if(train$x2[i] <= 35)
            tmp.age[i] <- Age30to35.WOE
          else if(train$x2[i] <= 40)
            tmp.age[i] <- Age35to40.WOE
          else if(train$x2[i] <= 45)
            tmp.age[i] <- Age40to45.WOE
          else if(train$x2[i] <= 50)
            tmp.age[i] <- Age45to50.WOE
          else if(train$x2[i] <= 55)
            tmp.age[i] <- Age50to55.WOE
          else if(train$x2[i] <= 60)
            tmp.age[i] <- Age55to60.WOE
          else if(train$x2[i] <= 65)
            tmp.age[i] <- Age60to65.WOE
          else if(train$x2[i] <= 75)
            tmp.age[i] <- Age65to75.WOE
          else
            tmp.age[i] <- Agemorethan.WOE
        }
        
        table(tmp.age)
    ## tmp.age
    ##   -1.2891452711972 -0.941445039519045 -0.558680027962495 
    ##               5063               9196               8180 
    ## -0.256436029353835 0.0719429392949312  0.220395209955515 
    ##               8472               9009               9465 
    ##  0.242511934081286  0.342839240194068   0.52063156705216 
    ##               8008               6784               5390 
    ##  0.574328792863984 
    ##               5298
        tmp.age[1:10]
    ##  [1] 0.34283924 0.57432879 0.34283924 0.57432879 0.07194294 0.22039521
    ##  [7] 0.07194294 0.24251193 0.34283924 0.52063157
        train$x2[1:10]
    ##  [1] 38 30 39 30 51 46 53 43 39 32

    NumberOfTime30-59DaysPastDueNotWorse变量(x3)

    ## tmp.NumberOfTime30.59DaysPastDueNotWorse
    ## -0.53249146131578 0.910601840444591  1.76452904024992  2.44329031065646 
    ##             62948              8077              3160               562 
    ##  2.56823323027274 
    ##               118
    ##  [1]  0.9106018 -0.5324915 -0.5324915 -0.5324915 -0.5324915 -0.5324915
    ##  [7] -0.5324915 -0.5324915 -0.5324915 -0.5324915
    ##  [1] 1 0 0 0 0 0 0 0 0 0

    MonthIncome变量(x5)

    ## tmp.MonthlyIncome
    ##    -1.12886232582259   -0.462438653207328   -0.389158799506996 
    ##                10201                 5490                 5486 
    ##   -0.237224038650003 -0.00180810632297072    0.114417167554772 
    ##                 7048                 8076                 7249 
    ##    0.247782294610166    0.312423079500641    0.350846777249291 
    ##                 9147                 8118                 9680 
    ##    0.448960482499888 
    ##                 4370
    ##  [1]  0.350846777  0.350846777  0.350846777  0.312423080 -0.001808106
    ##  [6] -0.462438653 -0.237224039  0.350846777  0.312423080 -0.237224039
    ##  [1]  3042  3300  3500  2500  6501 12454  8800  3280  2500  7916

    NumberOfTime90DaysPastDueNotWorse变量(x7)

    ## tmp.NumberOfTimes90DaysLate
    ## -0.369404425455224   1.94009728631401   2.34837375415972 
    ##              70793               2669                  7 
    ##   2.72944477623793   3.30900029985393   3.38529247382249 
    ##               1093                222                 81
    ##  [1]  1.9400973 -0.3694044 -0.3694044 -0.3694044 -0.3694044 -0.3694044
    ##  [7] -0.3694044 -0.3694044 -0.3694044 -0.3694044
    ##  [1] 1 0 0 0 0 0 0 0 0 0

    NumberRealEstateLoansOrLines变量(x8)

    ## tmp.NumberRealEstateLoansOrLines
    ## -0.243869874062293 -0.155683851792327 0.0290687559545721 
    ##              26150              15890               3130 
    ##  0.214906905417014   1.12192809398173 
    ##              27901               1794
    ##  [1]  0.2149069  0.2149069  0.2149069  0.2149069 -0.1556839 -0.1556839
    ##  [7]  0.2149069 -0.2438699  0.2149069  0.2149069
    ##  [1] 0 0 0 0 2 2 0 1 0 0

    NumberOfTime60.89DaysPastDueNotWorse变量(x9)

    ## tmp.NumberOfTime60.89DaysPastDueNotWorse
    ## -0.278460464730538   1.83290775083723   2.77753428092856 
    ##              71150               2919                708 
    ##   3.44698604282783   3.58051743545235 
    ##                 13                 75
    ##  [1] -0.2784605 -0.2784605 -0.2784605 -0.2784605 -0.2784605 -0.2784605
    ##  [7] -0.2784605 -0.2784605 -0.2784605 -0.2784605
    ##  [1] 0 0 0 0 0 0 0 0 0 0

    NumberOfDependents变量(x10)

    ## tmp.NumberOfDependents
    ## -0.155250809857344 0.0866996065110081  0.196180980387687 
    ##              43498              14544              10102 
    ##  0.331624863227172  0.404698242905824   0.76425364970991 
    ##               4771               1815                135
    ##  [1] -0.1552508 -0.1552508 -0.1552508 -0.1552508  0.1961810  0.1961810
    ##  [7] -0.1552508  0.1961810 -0.1552508 -0.1552508
    ##  [1] 0 0 0 0 2 2 0 2 0 0

     

    
     

     

    4、WOE DataFrame构建:

     

       trainWOE =cbind.data.frame(tmp.age,tmp.NumberOfTime30.59DaysPastDueNotWorse,tmp.MonthlyIncome,tmp.NumberOfTime60.89DaysPastDueNotWorse,tmp.NumberOfTimes90DaysLate,tmp.NumberRealEstateLoansOrLines,tmp.NumberOfDependents)

     

    七、评分卡的创建和实施

    标准评分卡采用的格式是评分卡中的每一个变量都遵循一系列IF-THEN法则,变量的值决定了该变量所分配的分值,总分就是各变量分值的和。

     

     

     

    知道线性表达式的两个参数A,B后就可以求每条记录(申请人)的分值。为了求得A,B,需要设定两个假设(分数的给定,很主观)。

    以上就是推断,实际代码中,习惯用了q、p来代表A、B.

    通俗来说就是,评分需要自己预设一个阀值,比如:

    这个人预测出来“发生违约”的几率为0.8,设定这个人为600分;

    另一个人预测出来“发生违约”的几率为0.9,设定这个人为500分。

    阀值的设定需根据行业经验不断跟踪调整,下面的分数设定仅代表个人经验。

    下面开始设立评分,假设按坏好比15为600分,每高20分坏好比降一半算出P,Q。如果后期结果不明显,可以高30-50分坏好比降一半。

    另:Score = q + p * log(odds)

    即有方程:

    620 = q + p * log(15/2)

    600 = q + p * log(15)

    逻辑回归建模:

    trainWOE$y = 1-train$y
    glm.fit = glm(y~.,data = trainWOE,family = binomial(link = logit))
    summary(glm.fit)
    coe = (glm.fit$coefficients)
    p <- -20/log(2)
    q <- 600+20*log(15)/log(2)
    Score=q + p*{as.numeric(coe[1])+as.numeric(coe[2])*tmp.age +as.numeric(coe[3])*tmp.NumberOfTime30.59DaysPastDueNotWorse+p*as.numeric(coe[4])*tmp.MonthlyIncome+p*as.numeric(coe[5])*tmp.NumberOfTime60.89DaysPastDueNotWorse+p*as.numeric(coe[6])*tmp.NumberOfTimes90DaysLate+p*as.numeric(coe[7])*tmp.NumberRealEstateLoansOrLines+p*as.numeric(coe[8])*tmp.NumberOfDependents
    个人总评分=基础分+各部分得分
    基础分为:
       base <- q + p*as.numeric(coe[1])
       base
    ## [1] 446.2841

     

    1、对各变量进行打分

     

     

    比如age变量(x2)

        Agelessthan30.SCORE = p*as.numeric(coe[2])*Agelessthan30.WOE
        Age30to35.SCORE = p*as.numeric(coe[2])*Age30to35.WOE
        Age35to40.SCORE = p*as.numeric(coe[2])*Age35to40.WOE
        Age40to45.SCORE = p*as.numeric(coe[2])*Age40to45.WOE
        Age45to50.SCORE = p*as.numeric(coe[2])*Age45to50.WOE
        Age50to55.SCORE = p*as.numeric(coe[2])*Age50to55.WOE
        Age55to60.SCORE = p*as.numeric(coe[2])*Age55to60.WOE
        Age60to65.SCORE = p*as.numeric(coe[2])*Age60to65.WOE
        Age65to75.SCORE = p*as.numeric(coe[2])*Age65to75.WOE
        Agemorethan.SCORE=p*as.numeric(coe[2])*Agemorethan.WOE
        Age.SCORE =c(Age30to35.SCORE,Age35to40.SCORE,Age40to45.SCORE,Age45to50.SCORE,Age50to55.SCORE,Age55to60.SCORE,Age60to65.SCORE,Age65to75.SCORE,Agemorethan.SCORE)
        Age.SCORE
    ## [1]  10.498828   6.913546   4.890389   4.444393   1.450770  -5.171176
    ## [7] -11.266096 -18.984767 -25.996338

    NumberOfTime30-59DaysPastDueNotWorse变量(x3)

    ## [1] -10.29843  17.61112  34.12614  47.25344  49.66985

    MonthlyIncome变量(x5)

    ##  [1] -24.92797904   9.91412083   6.89904854   7.74753565   5.47162546
    ##  [6]   2.52660461  -0.03992731  -5.23847393  -8.59355669 -10.21175106

    NumberOfTimes90DaysLate变量(x7)

    ## [1] -5.19482 27.28299 38.38333 46.53344 47.60632 33.02445

    NumberRealEstateLoansOrLine变量(x8)

    ## [1]  4.022310 -4.564396 -2.913860  0.544066  7.802025 20.998590

    NumberOfTime60-89DaysPastDueNotWorse变量(x9)

    ## [1] -4.820833 31.732126 48.085927 61.987533 59.675778

    NumberOfDependents变量(x10)

    ## [1] -1.5734012  0.8786638  1.9882112  3.3608775  4.1014453  7.7453871

    构造计算分值函数:

       getscore<-function(i,x){
       score = round(p*as.numeric(coe[i])*x,0)
       return(score)
    }

    2、计算各变量分箱得分:

    age变量(x2)

        Agelessthan30.SCORE = getscore(2,Agelessthan30.WOE)
        Age30to35.SCORE = getscore(2,Age30to35.WOE)
        Age35to40.SCORE = getscore(2,Age35to40.WOE)
        Age40to45.SCORE = getscore(2,Age40to45.WOE)
        Age45to50.SCORE = getscore(2,Age45to50.WOE)
        Age50to55.SCORE = getscore(2,Age50to55.WOE)
        Age55to60.SCORE = getscore(2,Age55to60.WOE)
        Age60to65.SCORE = getscore(2,Age60to65.WOE)
        Age65to75.SCORE = getscore(2,Age65to75.WOE)
        Agemorethan.SCORE = getscore(2,Agemorethan.WOE)
        Age.SCORE = c(Agelessthan30.SCORE,Age30to35.SCORE,Age35to40.SCORE,Age40to45.SCORE,Age45to50.SCORE,Age50to55.SCORE,Age55to60.SCORE,Age60to65.SCORE,Age65to75.SCORE,Agemorethan.SCORE)
        Age.SCORE
    ##  [1]  12  10   7   5   4   1  -5 -11 -19 -26

    NumberOfTime30-59DaysPastDueNotWorse变量(x3)

    ## [1] -10  18  34  47  50

    MonthlyIncome变量(x5)

    ##  [1] -25  10   7   8   5   3   0   0  -9 -10

    NumberOfTimes90DaysLate变量(x7)

    ## [1] -5 27 38 47 48 33

    NumberRealEstateLoansOrLine变量(x8)

    ## [1]  4 -5 -3  1  8 21

    NumberOfTime60-89DaysPastDueNotWorse变量(x9)

    ## [1] -5 32 48 62 60

    NumberOfDependents变量(x10)

    ## [1] -2  1  2  3  4  8

     

    3、最终生成的评分卡如下:

     

    age X2 <=30 (30,35] (35,40] (40,45] (45,50] (50,55] (55,60] (60,65] (65,75] (75,100]
    Score 12 10 7 5 4 1 -5 -11 -19 -26
    NumberOfTime30-59DaysPastDueNotWorse X3 <=0 (0,1] (1,3] (3,5] >5
    Score -10 18 34 47 50
    MonthlyIncome X5 <=1000 (1000,2000] (2000,3000] (3000,4000] (4000,5000] (5000,6000] (6000,7500] (7500,9500] (9500,12000] >12000
    Score -25 10 7 8 6 3 0 0 -9 -10
    NumberOfTimes90DaysLate X7 <=0 (0,1] (1,3] (3,5] (5,10] >10
    Score -5 27 38 47 48 33
    NumberRealEstateLoansOrLines X8 <=0 (0,1] (1,2] (2,3] (3,5] >5
    Score 4 -5 -3 1 8 21
    NumberOfTime60-89DaysPastDueNotWorse X9 <=0 (0,1] (1,3] (3,5] >5
    Score -5 32 48 62 60
    NumberOfDependents X10 <=0 (0,1] (1,2] (2,3] (3,5] >5
    Score -2 1 2 3 4 8

     个人评分计算案例:

     

    特征 数据 分数
    Age 38 7
    NumberOfTime30-59DaysPastDueNotWorse 4 47
    MonthlyIncome 1500 10
    NumberOfTimes90DaysLate 2 38
    NumberRealEstateLoansOrLines 1.5 -3
    NumberOfTime60-89DaysPastDueNotWorse 4 62
    NumberOfDependents 1.5 2

    所以这个人的总评分=基础分(base)+各特征分数

    总评分=446.2841+7+47+10+38-3+62+2=609.2841

     

     

     

    展开全文
  • 信用评分模型python实践——简介

    千次阅读 2018-12-20 00:01:03
    由于之前进行过信用评分模型的学习,并且接触并实践了一小段时间,虽然已经过了一年了,但是为了让这一段经历能够保留,在这里记录下点滴。本次将分多篇文章对信用评分模型的python实践进行阐述,包括简介,概念以及...

    由于之前进行过信用评分模型的学习,并且接触并实践了一小段时间,虽然已经过了一年了,但是为了让这一段经历能够保留,在这里记录下点滴。本次将分多篇文章对信用评分模型的python实践进行阐述,包括简介,概念以及最后的代码实现。最后很高兴看这一系列的朋友们,也希望能帮到你,大家一起学习。

    信用评分模型简介

    信用评分模型运用数据挖掘技术和统计分析方法,通过对客户的人口特征、信用历史记录、行为记录、交易记录等大量数据进行系统的分析,挖掘出数据中蕴涵的行为模式、捕捉历史信息和未来信用表现之间的关系,发展出预测性的模型,以一个信用评分(credit score)来综合评估客户未来的信用表现。 

    通过信用评分模型,银行计算出用户的信用评分,而此信用评分表示该用户的风险程度,评分的高低按照风险概率的大小进行排列,分数越高,风险越低;分数越低,风险越高。

    模型类型

    如今在银行,P2P等各种贷款业务机构,普遍使用信用评分,信用评分卡大致可以分A,B,C卡三类。

    • A卡(Application score card)申请评分卡

    • B卡(Behavior score card)行为评分卡

    • C卡(Collection score card)催收评分卡

    三种卡的介绍,请直接看这篇文章:比较全面的说了三种打分机制。梁世栋博士的《行为评分和贷后风险管理研究》http://www.docin.com/p-516772778.html

    本文中将三个阶段进行了进一步的细化,分成以下模型

     第三方征信

    除了人行老大征信,以及各大银行自己的征信策略,随着互联网的如火如荼,各大互联网大佬们也推出了各自的征信系统,大家最熟悉的蚂蚁金服的芝麻征信,已经渗透到生活的方方面面,比如最近背负举债人人喊打的小黄车ofo,当年可以是推出了信用免押金模式(我的押金还在一千多万排名呢?),还有京东的小白信用等。

    芝麻信用

    通过云计算、机器学习等技术客观呈现个人的信用状况,已经在信用卡、消费金融、酒店、租房、出行、婚恋、分类信息、学生服务、公共事业服务等上百个场景为用户、商户提供信用服务。

    京东信用

    主要从身份(社会属性、居住环境、教育情况和稳定性),资产(金融投资、信用能力和收支情况),偏好(登录、浏览、下单等维度),履约(金融产品、电商平台和社会行为中表现出来的履约和违约情况)和关系(社会关系和购物行为)来考量。

    腾讯信用

    是腾讯征信推出的个人征信管理平台,腾讯信用主要基于历史行为信息,通过采集不同维度的信息,运用大数据,机器学习以及传统统计方法相结合的技术手段来客观的反映用户的信用水平,从而得出用户的守信指数,用信用分来反映用户的信用水平,分数的多少是通过专业、先进的信用评分模型评估得出的,分数越高代表信用水平越高。

    模型时间线

    和其他模型非常大不同,信用评分模型是一个漫长的过程,这就需要企业舍得花时间和精力去做了,目前也就是只有银行和互联网大佬有时间精力能力以及数据去做这件事情。我们通常分为观察期和表现期

    • 观察期:收集信用历史信息和行为特征等,可以认为就是特征
    • 表现期:即这次信用评分模型等目标,收集信用表现的信息,比如是否在这几个月内有拖欠,收益高低,卡是否被强停,是否有违约

     具体观察期和表现期的时间要根据模型而定,比如申请风险评分模型时间就较短,其实也很好理解,申请嘛,还没成为你的客户,而且现在正是获客,占领市场的关键时期,所以相对严格度会低一点。但是行为评分模型时间就更长一点,路遥知马力,日久见人心,好好观察后,才能对你进行评判,想想我们办事,处对象也是如此,?。

    模型特征

    这里我就举行为模型常用的客户特征,如下表所示,不同模型的侧重点也不一样。

    消费行为:最近3个月消费金额、消费笔数,最近3期境外消费金额占比,最近3期网银消费笔数 等

    还款行为:最近3个月最大逾期天数、还款率、逾期还款期数,最近6个期账户逾期月数,最近12个月未欠款账单数 等

    账户属性:信用额度,开卡时间,收益率、发卡机构 等

    客户属性:年龄,收入,性别,户籍状态 等

     

    信用评分模型的介绍先到这,可能说的比较简单,先让大家感受感受一下这类模型的特点,从下一节开始,我们将阐述信用模型涉及到的一些数学理论概念等,并进行python实践,大家一起加油吧,?。

     

    对了,推荐一本信用评分模型等入门书籍《信用评分模型技术与应用》。

    展开全文
  • 信用评分模型

    千次阅读 2019-09-29 22:49:05
    本文主要通过kaggle上Give me some credit数据进行数据分析,并根据信用评分建立原理,构建一个简易的信用评分模型。 数据来源 来自kaggle上的数据:https://www.kaggle.com/c/GiveMeSomeCredit/data 项目流程 ...

    信用卡评分模型

    项目简介

    本文主要通过kaggle上Give me some credit数据进行数据分析,并根据信用评分建立原理,构建一个简易的信用评分卡模型。

    数据来源

    来自kaggle上的数据:https://www.kaggle.com/c/GiveMeSomeCredit/data

    项目流程
    1. 理解数据

      • 包括导入数据,查看数据集信息,从整体上了解数据
    2. 探索性数据分析和数据清洗

      • 主要研究各个变量内部结构,自变量和因变量之间的关系

      • 数据清洗

        • 数据预处理(异常值和缺失值的处理)
        • 特征工程(特征衍生,特征提取和特征选择)
    3. 构建模型(逻辑回归建立模型)

    4. 模型评估(ROC和AUC)

    5. 建立评分卡

    项目过程

    理解数据
    • 导入数据/查看数据
    import numpy as np
    import pandas as pd
    train=pd.read_csv('E:/机器学习/04_PythonCase/jupyterCase/02_data/cs-training.csv')
    train.head()
    

    在这里插入图片描述

    #设置ID为索引
    train.set_index('ID',inplace=True)
    #查看数据大小和类型
    print('数据大小:',train.shape)
    print('数据类型:',train.dtypes)
    #查看缺失情况
    train,info()
    

    在这里插入图片描述
    在这里插入图片描述

    可以看到NumberOfDependents和MonthlyIncome存在缺失

    • 为了便于理解各个特征的含义,将特征名称更改为中文

      #英文字段转换为中文字段
      states={'SeriousDlqin2yrs':'好坏客户',
              'RevolvingUtilizationOfUnsecuredLines':'可用额度比值',
              'age':'年龄',
              'NumberOfTime30-59DaysPastDueNotWorse':'逾期30-59天笔数',
              'DebtRatio':'负债率',
              'MonthlyIncome':'月收入',
              'NumberOfOpenCreditLinesAndLoans':'信贷数量',
              'NumberOfTimes90DaysLate':'逾期90天笔数',
              'NumberRealEstateLoansOrLines':'固定资产贷款量',
              'NumberOfTime60-89DaysPastDueNotWorse':'逾期60-89天笔数',
              'NumberOfDependents':'家属数量'}
      
      #使用rename函数列的重列名
      train.rename(columns=states,inplace=True)
      train.head()
      

    在这里插入图片描述

    • 查看各个特征的统计信息
    train.describe()
    #查看缺失比
    print('月收入缺失比值:%.2f%%'%(train[train['月收入'].isnull()].shape[0]/train.shape[0]*100))
    print('家属数量缺失比值:%.2f%%'%(train[train['家属数量'].isnull()].shape[0]/train.shape[0]*100))
    

    在这里插入图片描述

    月收入缺失比比较大,后期需要处理

    • 查看好坏客户分布情况
    import seaborn as sns
    sns.set_style('darkgrid', {'legend.frameon':True})
    plt.rcParams['font.sans-serif']=['SimHei']
    plt.rcParams['axes.unicode_minus']=False
    sns.countplot('好坏客户',data=train)
    plt.ylabel('数量')
    plt.show()
    

    在这里插入图片描述

    好坏客户分布不平衡,后期需要分箱,WOE编码处理

    探索性数据分析和数据清洗
    年龄
    fig,[ax1,ax2]=plt.subplots(1,2,figsize=(20,6))
    sns.distplot(train['年龄'],ax=ax1)
    sns.boxplot(y='年龄',data=train,ax=ax2)
    

    在这里插入图片描述

    由图可知,年龄存在着离群值,用3倍标准差筛选数据

    age_mean=train['年龄'].mean()
    age_std=train['年龄'].std()
    age_low=age_mean-3*age_std
    age_up=age_mean+3*age_std
    print('异常值下限:',age_low,'异常值上限',age_up)
    

    在这里插入图片描述

    ## 筛选异常值
    train=train[train['年龄']<age_up]
    train=train[train['年龄']>age_low]
    train['年龄'].describe()
    

    在这里插入图片描述

    • 查看不同年龄阶段的违约情况,[18,40),[40,60),[60,80),[80,97)
    data_age=train.loc[:,['年龄','好坏客户']]
    data_age.loc[(data_age['年龄']>=18)&(data_age['年龄']<40),'年龄']=1
    data_age.loc[(data_age['年龄']>=40)&(data_age['年龄']<60),'年龄']=2
    data_age.loc[(data_age['年龄']>=60)&(data_age['年龄']<80),'年龄']=3
    data_age.loc[(data_age['年龄']>=80),'年龄']=4
    age_isgb=data_age.groupby('年龄')['好坏客户'].sum()
    age_total=data_age.groupby('年龄')['好坏客户'].count()
    age_ratio=age_isgb/age_total
    age_ratio.index
    sns.barplot(x=age_ratio.index,y=age_ratio.values)
    plt.title("不同年龄段的违约率")
    plt.ylabel('违约率')
    

    在这里插入图片描述

    由图可以知道,年龄在18-40之间的违约情况最严重,随着年龄的增长,违约率逐渐降低

    可用额度比值
    #年龄-可用额度比值的散点图
    figure=plt.figure(figsize=(8,6))
    plt.scatter(train['可用额度比值'],train['年龄'])
    plt.grid()
    plt.title('可用额度比值-年龄散点图')
    plt.xlabel('可用额度比值')
    plt.ylabel('年龄')
    

    在这里插入图片描述

    理论上可用额度比值是小于1的,但是图示可知,可用额度比值超过10000的有多个,所以是否是异常值需要再深入确认

    ##四分位数观察异常值
    sns.boxplot(data=train,y=train['可用额度比值'])
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hUTBCBz2-1569768518872)(E:\机器学习\05_Typora图片\1569764791119.png)]

    ###将数据分为两部分,大于1和小于1
    data1=train.loc[train['可用额度比值']<1,'可用额度比值']
    data2=train.loc[train['可用额度比值']>=1,'可用额度比值']
    fig,[ax1,ax2]=plt.subplots(1,2,figsize=(20,6))
    sns.distplot(data1,ax=ax1,bins=10)
    sns.distplot(data2,ax=ax2,bins=10)
    

    在这里插入图片描述

    由图可以知道,可用额度比值大部分集中在0-1之间,超过1的大部分集中在1-5000之间,可再深入分析,得出划分异常值的临界数据

    #将区间分为(0-1),(1-10),(10-20),(20-100),(100,1000),(1000-10000),(10000,51000)看一下违约率情况
    data_r=train.loc[(train['可用额度比值']>=0)&(train['可用额度比值']<1),:]
    is_1=data_r.loc[data_r['好坏客户']==1,:].shape[0]*100/data_r.shape[0]
    data_r=train.loc[(train['可用额度比值']>=1)&(train['可用额度比值']<10),:]
    is_2=data_r.loc[data_r['好坏客户']==1,:].shape[0]*100/data_r.shape[0]
    data_r=train.loc[(train['可用额度比值']>=10)&(train['可用额度比值']<20),:]
    is_3=data_r.loc[data_r['好坏客户']==1,:].shape[0]*100/data_r.shape[0]
    data_r=train.loc[(train['可用额度比值']>=20)&(train['可用额度比值']<100),:]
    is_4=data_r.loc[data_r['好坏客户']==1,:].shape[0]*100/data_r.shape[0]
    data_r=train.loc[(train['可用额度比值']>=100)&(train['可用额度比值']<1000),:]
    is_5=data_r.loc[data_r['好坏客户']==1,:].shape[0]*100/data_r.shape[0]
    data_r=train.loc[(train['可用额度比值']>=1000)&(train['可用额度比值']<10000),:]
    is_6=data_r.loc[data_r['好坏客户']==1,:].shape[0]*100/data_r.shape[0]
    data_r=train.loc[(train['可用额度比值']>=10000)&(train['可用额度比值']<51000),:]
    is_7=data_r.loc[data_r['好坏客户']==1,:].shape[0]*100/data_r.shape[0]
    print('0-1违约率为:{0}%'.format(is_1),
         '1-10违约率为:{0}%'.format(is_2),
         '10-20违约率为:{0}%'.format(is_3), 
         '20-100违约率为:{0}%'.format(is_4),
         '100-1000违约率为:{0}%'.format(is_5),
         '1000-10000违约率为:{0}%'.format(is_6),
         '10000-51000违约率为:{0}%'.format(is_7))
    

    在这里插入图片描述

    通过观察可知,10-20之间的违约率达到最大,1000-10000之间的违约率趋于正常(与0-1之间的违约率差不多),说明20是分界点,可以以20划分异常值和正常值

    负债率
    ###负债率分析
    sns.set_style('darkgrid', {'legend.frameon':True})
    plt.rcParams['font.sans-serif']=['SimHei']
    plt.rcParams['axes.unicode_minus']=False
    fig,[ax1,ax2]=plt.subplots(1,2,figsize=(20,6))
    sns.kdeplot(train['负债率'],ax=ax1)
    sns.boxplot(y=train['负债率'],ax=ax2)
    

    在这里插入图片描述

    由图可知,负债率大部分集中在0-1之间,存在着离群值,可再细分分析

    #将负债率划分为大于1和小于1
    data1=train.loc[train['负债率']<1,'负债率']
    data2=train.loc[train['负债率']>=1,'负债率']
    fig,[ax1,ax2]=plt.subplots(1,2,figsize=(20,6))
    sns.distplot(data1,ax=ax1)
    sns.distplot(data2,ax=ax2)
    

    在这里插入图片描述

    ###多次细分[1,2),[2,10),[10,200),[200,1000)
    data3=train.loc[(train['负债率']>=1)&(train['负债率']<2),'负债率']
    data4=train.loc[(train['负债率']>=2)&(train['负债率']<10),'负债率']
    data5=train.loc[(train['负债率']>=10)&(train['负债率']<200),'负债率']
    data6=train.loc[(train['负债率']>=200)&(train['负债率']<1000),'负债率']
    fig,[[ax1,ax2],[ax3,ax4]]=plt.subplots(2,2,figsize=(20,6))
    sns.distplot(data3,ax=ax1)
    sns.distplot(data4,ax=ax2)
    sns.distplot(data5,ax=ax3)
    sns.distplot(data6,ax=ax4)
    

    在这里插入图片描述

    可看到负债率数值大小在200-1000之间的数据分布较为平衡

    ###查看各个区间违约率情况
    debt1=train.loc[(train['负债率']>0)&(train['负债率']<1),:]
    DebIs_1=debt1.loc[debt1['好坏客户']==1,:].shape[0]*100/debt1.shape[0]
    debt2=train.loc[(train['负债率']>=1)&(train['负债率']<2),:]
    DebIs_2=debt2.loc[debt2['好坏客户']==1,:].shape[0]*100/debt2.shape[0]
    debt3=train.loc[(train['负债率']>=2)&(train['负债率']<10),:]
    DebIs_3=debt3.loc[debt3['好坏客户']==1,:].shape[0]*100/debt3.shape[0]
    debt4=train.loc[(train['负债率']>=10)&(train['负债率']<200),:]
    DebIs_4=debt4.loc[debt4['好坏客户']==1,:].shape[0]*100/debt4.shape[0]
    debt5=train.loc[(train['负债率']>=200)&(train['负债率']<1000),:]
    DebIs_5=debt5.loc[debt5['好坏客户']==1,:].shape[0]*100/debt5.shape[0]
    print('0-1违约率为:{0}%'.format(DebIs_1),
         '1-2违约率为:{0}%'.format(DebIs_2),
         '2-10违约率为:{0}%'.format(DebIs_3), 
         '10-200违约率为:{0}%'.format(DebIs_4),
         '200-1000违约率为:{0}%'.format(DebIs_5))
       
    

    在这里插入图片描述

    可以看到,1-2之间的违约率达到最大,说明负债率可以以2为分隔界限

    train=train[train['负债率']<2]
    train.describe()
    

    在这里插入图片描述

    信贷数量
    fig,[ax1,ax2]=plt.subplots(1,2,figsize=(20,6))
    sns.distplot(train['信贷数量'],ax=ax1)
    sns.boxplot(y=train['信贷数量'],ax=ax2)
    

    在这里插入图片描述

    fig=plt.figure(figsize=(20,6))
    sns.countplot(train['信贷数量'])
    

    在这里插入图片描述

    train.loc[train['信贷数量']>36,'信贷数量']=36
    d1=train.groupby(['信贷数量'])['好坏客户'].sum()
    total=train.groupby(['信贷数量'])['好坏客户'].count()
    r=d1/total
    r.plot(kind='bar',figsize=(20,6))
    

    在这里插入图片描述

    可以看到信贷数量分布较为均衡,说明可能没有异常值,不需要特殊处理

    家属数量
    ###家属数量分析
    fig,[ax1,ax2]=plt.subplots(1,2,figsize=(20,6))
    sns.countplot(train['家属数量'],ax=ax1)
    sns.boxplot(y=train['家属数量'],ax=ax2)
    

    在这里插入图片描述

    ratio=train[train['家属数量'].isnull()].shape[0]/train.shape[0]
    print('家属数量缺失比值%.2f%%'%(ratio*100))
    print(train[train['家属数量'].isnull()].shape[0])
    

    在这里插入图片描述

    家属数量缺失比为0.4%,缺失数量较少,可以直接删除

    train=train[train['家属数量'].isnull()==False]
    train.info()
    

    月收入
    ###月收入分析
    ratio=train[train['月收入'].isnull()].shape[0]/train.shape[0]
    print('月收入缺失比值%.2f%%'%(ratio*100))
    print('缺失数量',train[train['月收入'].isnull()].shape[0])
    

    在这里插入图片描述

    月收入缺失值较多,用随机森林填充

    ###月收入缺失值比较多,用随机森林预测填充
    from sklearn.ensemble import RandomForestRegressor
    X_df=train[train['月收入'].notnull()]
    X=X_df.iloc[:,[1,2,3,4,6,7,8,9,10]]
    Y=X_df['月收入']
    
    X_test=train[train['月收入'].isnull()].iloc[:,[1,2,3,4,6,7,8,9,10]]
    print(X.shape,Y.shape,X_test.shape)
    ##随机森林预测
    #n_estimators:弱学习器的最大迭代次数
    rfr = RandomForestRegressor(random_state=0, n_estimators=200,max_depth=3,n_jobs=-1)
    rfr.fit(X,Y)
    pre_Y=rfr.predict(X_test)
    
    train.loc[train['月收入'].isnull(),'月收入']=pre_Y
    train.info()
    
    

    在这里插入图片描述

    逾期天数
    fig,[[ax1,ax2,ax3],[ax4,ax5,ax6]]=plt.subplots(2,3,figsize=(20,12))
    sns.countplot(train['逾期30-59天笔数'],ax=ax1)
    sns.countplot(train['逾期60-89天笔数'],ax=ax2)
    sns.countplot(train['逾期90天笔数'],ax=ax3)
    sns.boxplot(y=train['逾期30-59天笔数'],ax=ax4)
    sns.boxplot(y=train['逾期60-89天笔数'],ax=ax5)
    sns.boxplot(y=train['逾期90天笔数'],ax=ax6)
    

    在这里插入图片描述

    可以看出,超过90的为明显异常值,直接删除

    train=train[train['逾期30-59天笔数']<90]
    train=train[train['逾期60-89天笔数']<90]
    train=train[train['逾期90天笔数']<90]
    
    
    特征衍生
    #特征衍生
    train['总逾期数']=train['逾期30-59天笔数']+train['逾期60-89天笔数']+train['逾期90天笔数']
    train['月支出']=train['负债率']*train['月收入']
    train['总人数']=train['家属数量']+1   #原来数据中的家属数量不包括本人
    ##类型转换
    train.dtypes
    train['家属数量']=train['家属数量'].astype('int64')
    train['总人数']=train['总人数'].astype('int64')
    train['月收入']=train['月收入'].astype('int64')
    train['月支出']=train['月支出'].astype('int64')
    
    特征分箱
    #可用额度比值分箱
    train.loc[train['可用额度比值']<=1,'可用额度比值']=0
    train.loc[(train['可用额度比值']>1)&(train['可用额度比值']<=20),'可用额度比值']=1
    train.loc[train['可用额度比值']>20,'可用额度比值']=2
    #负债率分箱
    train.loc[train['负债率']<1,'负债率']=0
    train.loc[(train['负债率']>=1)&(train['负债率']<2),'负债率']=1
    train.loc[train['负债率']>=2,'负债率']=2
    # 逾期天数分箱
    train.loc[train['逾期30-59天笔数']>=8,'逾期30-59天笔数']=8
    train.loc[train['逾期60-89天笔数']>=7,'逾期30-59天笔数']=7
    train.loc[train['逾期90天笔数']>=10,'逾期30-59天笔数']=10
    train.loc[train['总逾期数']>1,'总逾期数']=1
    #家属数量分箱
    train.loc[train['家属数量']>=7,'家属数量']=7
    
    
    特征选择
    ###查看各个特征之间的相关系数
    corr=train.corr()
    plt.figure(figsize=(14,12))
    sns.heatmap(corr,annot=True,linewidth=0.2,cmap='YlGnBu')
    

    在这里插入图片描述

    WOE/IV值计算
    def bin_woe(tar,var,n=None,cat=None):
        """
        连续自变量分箱,woe,iv变换
        tar:target目标变量
        var:进行woe,iv转换的自变量
        n:分组数量
        """
        total_bad=tar.sum()
        total_good=tar.count()-tar.sum()
        totalRate=total_good/total_bad
        if cat=='s':
            msheet=pd.DataFrame({tar.name:tar,var.name:var,'var_bins':pd.qcut(var,n,duplicates='drop')})
            grouped = msheet.groupby(['var_bins'])
        elif (cat == 'd') and (n is None):
            msheet = pd.DataFrame({tar.name:tar,var.name:var})
            grouped = msheet.groupby([var.name])
        groupBad = grouped.sum()[tar.name]
        groupTotal = grouped.count()[tar.name]
        groupGood = groupTotal - groupBad
        groupRate = groupGood/groupBad
        groupBadRate = groupBad/groupTotal
        groupGoodRate = groupGood/groupTotal
        woe = np.log(groupRate/totalRate)
        iv = np.sum((groupGood/total_good-groupBad/total_bad)*woe)
        
        if cat == 's':
            new_var, cut = pd.qcut(var, n, duplicates='drop',retbins=True, labels=woe.tolist())
        elif cat == 'd':
            dictmap = {}
            for x in woe.index:
                dictmap[x] = woe[x]
            new_var, cut = var.map(dictmap), woe.index
        
        return woe.tolist(), iv, cut, new_var
    
    
    # 确定变量类型,连续变量(s)还是离散变量(d)
    dvar = ['可用额度比值','负债率','逾期30-59天笔数', '逾期60-89天笔数','逾期90天笔数','总逾期数','Withdepend',
            '固定资产贷款量','家属数量']
    svar = ['月收入','年龄','月支出','信贷数量']
    
    # 可视化woe得分和iv得分
    def woe_vs(data):
        cutdict = {}
        ivdict = {}
        woe_dict = {}
        woe_var = pd.DataFrame()
        for var in data.columns:
            if var in dvar:
                woe, iv, cut, new = bin_woe(data['好坏客户'], data[var], cat='d')
                woe_dict[var] = woe
                woe_var[var] = new
                ivdict[var] = iv
                cutdict[var] = cut
            elif var in svar:
                woe, iv, cut, new = bin_woe(data['好坏客户'], data[var], n=5, cat='s')
                woe_dict[var] = woe
                woe_var[var] = new
                ivdict[var] = iv
                cutdict[var] = cut
                
        ivdict = sorted(ivdict.items(), key=lambda x:x[1], reverse=False)
        iv_vs = pd.DataFrame([x[1] for x in ivdict],index=[x[0] for x in ivdict],columns=['IV'])
        ax = iv_vs.plot(kind='barh',
                        figsize=(12,12),
                        title='Feature IV',
                        fontsize=10,
                        width=0.8,
                        color='#00688B')
        ax.set_ylabel('Features')
        ax.set_xlabel('IV of Features')
        
        return ivdict, woe_var, woe_dict, cutdict
    
    # woe转化
    ivinfo, woe_data, woe_dict, cut_dict = woe_vs(train)
    

    在这里插入图片描述

    筛选出IV值大于0.1的特征,这些特征具有较大的预测能力,这些特征是年龄,可用额度比值,逾期30-59天笔数,总逾期数,其中总逾期数和逾期30-59天的相关系数为0.7,具有强相关性,因此选择总逾期数输入模型

    #筛选出IV>0.1的特征
    # 年龄,可用额度比值,总逾期数,逾期30-59天笔数
    X=woe_data.loc[:,['年龄','总逾期数','可用额度比值','逾期30-59天笔数']]
    y=train['好坏客户']
    X.head()
    
    

    在这里插入图片描述

    构建模型
    #划分训练集和测试集
    from sklearn.model_selection import train_test_split
    X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=42)
    
    #训练模型(逻辑回归)
    from sklearn.linear_model import LogisticRegression
    model=LogisticRegression(random_state=0,
                               solver="sag",
                               penalty="l2",
                               class_weight="balanced",
                               C=1.0,
                               max_iter=500)
    model.fit(X_train, y_train)
    model_proba = model.predict_proba(X_test)#predict_proba返回的结果是一个数组,包含两个元素,第一个元素是标签为0的概率值,第二个元素是标签为1的概率值
    model_score=model_proba[:,1]
    
    
    模型评估
    #用ROC曲线和AUC来评估模型的预测能力
    from sklearn.metrics import roc_curve, roc_auc_score
    fpr,tpr,thresholds =roc_curve(y_test,model_score)
    auc_score=roc_auc_score(y_test,model_score)
    plt.plot(fpr, tpr, linewidth=2, label='AUC = %0.2f'%auc_score)
    plt.plot([0,1],[0,1], "k--")
    plt.axis([0,1,0,1])
    plt.xlabel('FPR')
    plt.ylabel('TPR')
    plt.legend()
    

    在这里插入图片描述

    可以看到AUC是0.79,说明模型区分能力良好

    制作评分卡

    score=ABlog(odds)=AB(β0+β1x1+β2x2+...+βnxn) score=A-Blog(odds)=A-B(\beta_0+\beta_1x_1+\beta_2x_2+...+\beta_nx_n)
    B=PDOlog(2)B=\frac{PDO}{log(2)}
    A=P0+Blog(β0)A=P_0+Blog(\beta_0)

    IV_info=['年龄','总逾期数','可用额度比值','逾期30-59天笔数']
    intercept=model.intercept_
    coef=model.coef_
    coe=coef[0].tolist()
    coe_df=pd.DataFrame({'feature':IV_info,'coe':coe})
    coe_df
    

    在这里插入图片描述

    import math
    B=20/math.log(2)
    A=600+B*math.log(1/20)
    #基础分
    score=round(A-B*intercept[0],0)
    featurelist = []
    woelist = []
    cutlist = []
    for k,v in woe_dict.items():
        if k in IV_info:
            for n in range(0,len(v)):
                featurelist.append(k)
                woelist.append(v[n])
                cutlist.append(cut_dict[k][n])
    scoreboard = pd.DataFrame({'feature':featurelist,'woe':woelist,'cut':cutlist},
                              columns=['feature','cut','woe'])
    score_df=pd.merge(scoreboard,coe_df)
    score_df['score']=round(-B*score_df['woe']*score_df['coe'],0)
    score_df.drop('coe',axis=1,inplace=True)
    score_df
    

    在这里插入图片描述

    结论

    • 本次建模AUC为0.79,识别能力良好,后期可通过网格搜索超参数获取更好的结果,欢迎指正
    展开全文
  • FICO信用评分模型解析

    万次阅读 2017-03-26 15:19:33
    美国的信用评级基本都会参考FICO信用分,FICO是Fair Isaac Company推出针对用户哥哥方面情况的评分,范围从300-850分之间。分数越高说明客户的信用风险越小。  一般情况下,用户的FICO分值高于680,则被认为是是好...

    美国的信用评级基本都会参考FICO信用分,FICO是Fair Isaac Company推出针对用户哥哥方面情况的评分,范围从300-850分之间。分数越高说明客户的信用风险越小。

      一般情况下,用户的FICO分值高于680,则被认为是是好的,低于620分,则会考虑拒绝,介于620-680之间的用户会被进一步调查。但是一般机构也不会直接用FICO作为判断的唯一风险的唯一依据,还会结合用户的其他行为。
      FICO评分模型中考虑的因素:
      1. 偿还历史
      影响FICO得分的最重要的因素是用户的信用偿还历史,大约占35%。主要显示用户的历史偿还情况,帮助贷方了解用户是否存在历史逾期行为。包括:
      * 各种信用卡的还款记录、零售消费账户记录、分期偿还贷款、金融抵押情况、抵押贷款。
      * 公开记录及支票存款记录,主要记录破产记录、丧失抵押品赎回权记录、法律诉讼事件、留置权记录及判决。
      * 逾期不还的情况,包括,逾期的天数、金额、次数和逾期发生时距现在的时间长度等。
      例如:最近几个月的违约情况。
      2. 信用账户数
      影响度仅次于历史信用,约占30%。该因素主要用来分析对一个用户而言多少个账户算“多”,因为不是账户数越多风险越高,需要考虑一个用户的可用的信用度。
      例如:总余额在循环账户总限额比
      3. 使用信用的年限
      约占15%的影响度。该因素主要考虑信用账户的账龄,既要考虑较早开立账户的账龄,也要考虑晚的,以及平均账龄。
      4. 正在使用的信用类型
      约占10%,包括持有的信用账户类型和每种类型的信用账户数
      5. 新开立的信用账户
      

    展开全文
  • 信用统计评级模型是指通过观察和分析企业财务报表中的一些感性的财务来判别企业财务状况以及采取适当防范施的数学模型对于投资和债权,可以根据财务预测指数的动态分析进行正确的投资选择;而对于上市公司经营者来说...
  • 信用评分模型

    2021-06-19 22:54:31
    信用评分模型是最常见的金融风控手段之一,它是指根据客户的各种属性和行为数据,利用一定的信用评分模型,对客户进行信用评分,据此决定是否给予授信以及授信的额度和利率,从而识别和减少在金融交易中存在的交易...
  • 全面的信贷评分模型开发流程介绍
  • 不同机构的信用评分模型不尽相同,主要有两类: 金融机构: (1)申请风险评分 (2)行为风险评分/行为收益评分/行为流失倾向评分 (3)申请欺诈评分/交易欺诈评分 征信局: (1)征信局风险/破产/收益评分 (2)市场反应...
  • 信用风险评级模型的开发过程

    千次阅读 2019-02-18 10:44:25
    一、信用风险评级模型类型 信用风险计量体系包括主体评级模型和债项评级两部分。主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用“四张卡”来表示,分别是A卡、B卡、C卡和F卡;债项评级模型通常...
  • 信用评分模型开发

    2020-06-18 11:06:15
    信用风险计量体系包括主体评级模型和债项评级两个部分。主体评级包含以下四个内容: 1.申请者评级 2.行为评级模型3.催收评级模型4.欺诈评级模型 数据来源: 本项目来源kaggle竞赛 Give Me Some Credit 2.1.1获取数据...
  • 基于python的信用评分模型

    万次阅读 多人点赞 2018-05-24 01:25:32
    项目背景介绍1.1 信用风险和评分模型的基本概念 信用风险指的是交易对手未能履行约定合同中的义务造成经济损失的风险,即受信人不能履行还本付息的责任而使授信人的预期收益与实际收益发生偏离的可能性,它是金融...
  • 信用标准评分模型开发及实现

    万次阅读 多人点赞 2017-08-03 02:07:58
    一、信用风险评级模型类型信用风险计量体系包括主体评级模型和债项评级两部分。主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用“四张卡”来表示,分别是A卡、B卡、C卡和F卡;债项评级模型通常...
  • 欢迎大家,上一篇博客【信用评分模型(R语言)】详细的讲解了如何开发评分卡,这片博客就不再详细介绍,为方便开发评分卡,本人根据自己经验写了一个python包,导入此包后仅需一行代码即可成功制作评分卡,默认...
  • 在信贷市场中,筛选算法区分好类型和坏类型的借款人。 这是他们存在的理由。 然而,通过这样做,他们也经常区分共享受保护属性(例如性别、年龄、种族)的个人和其他人口。 在本文中,我们展示了如何测试 (1) 在拒绝...
  • 信用评级模型实例分析(以消费金融为例)-中 原创 2016-10-13 单良 亚联大数据 点击“亚联大数据”可关注我们!     第五章 自变量的初步分析与处理   模型变量有两种类型,分别是连续型变量 。连续型变数系...
  • 信用评分模型诊断指数

    千次阅读 2018-01-02 13:41:01
    按照模型LR,使用score均值作为变量,带入模型LR中,计算出预测的tag均值 11. WOE WOE 计算公式如下: 其中Bi为每组坏样本数,BT为总的坏样本数。Gi为每组好样本数据,GT为总坏样本数。 IV的计算公式如下: 其中 Bi...
  • 玩转大数据风控-利用评分模型实现信用评级一、算法介绍1. 什么是逻辑回归?2. 逻辑回归为何逻辑 ?3. 逻辑回归怎么回归?二、构建流程1.数据处理1.1 数据清理1.2 缺失值处理2. 特征选择3.特征变量分箱 本篇文章...
  • 第五章 信用评分模型的开发流程

    千次阅读 2017-03-25 17:29:38
    目录 一、模型的样本 二、模型的变量一、模型的样本 好的样本是开发模型的首要环节。预测模型的前提是客户的未来行为要与过去相似,所以在选取样本时要考虑样本的代表性,是否能够...例如,某银行有100万个信用卡账
  • 2) 信贷情况,主要是信贷和信用卡相关数据; 3)公共数据,包括税务、工商、法院、电信、水电煤气等部门的数据; 4) 个人信用报告查询记录。 如今随着大数据时代的到来和发展,可用于评估人们的...
  • 引进全球领先的信用评级模型,有利网风控值得放心吗?   有利网是网贷行业的知名平台,用户规模达千万级,在多个第三方评级中也长居前十位置左右。比较有意思的是,有利网上的借款人审核中,引进了跟全球领先的个人...
  • 信用评分模型开发及评估指标

    千次阅读 2019-12-23 22:54:50
    一、信用风险评级模型类型 信用风险计量体系包括主体评级模型和债项评级两部分。主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用“四张卡”来表示,分别是A卡、B卡、C卡和F卡;债项评级模型通常...
  • 摘要:面向小微商户以及个人消费的小微信贷是当前互联网...本文将通过一个真实的案例出发,进行分析和探讨,针对用户历史行为数据建立信用评分模型,并通过该模型改进信用评估的预测效果。 关键词: 小微信贷;互联...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,188
精华内容 1,275
关键字:

信用评分模型的类型