一 :项目目的

       研究Lending Club 贷款的风险特征,并提出建模方案。

二:数据获取

       数据集来自Lending Club平台发生借贷的业务数据,2017年第一季度,具体数据集可以从Lending Club官网下载,需要先用邮箱注册一个账号。

三:数据探索

     1.导入需要用到的工具

         

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')  #风格设置
import seaborn as sns
sns.set_style('whitegrid')
%matplotlib inline
import warnings
warnings.filterwarnings('ignore') 
plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题

      2.导入数据

      

data=pd.read_csv("LoanStats3d_securev1.csv",encoding='latin-1',skiprows = 1)
data.head()

Lending Club 数据做数据分析&评分卡

           各个变量的解释也可以在Lending Club 官网找到,直接下载是EXCEL格式的

#看一下目标特征
data.loan_status.value_counts()
Fully Paid            307831
Charged Off            77884
Current                33584
Late (31-120 days)      1006
In Grace Period          436
Late (16-30 days)        287
Default                   67
Name: loan_status, dtype: int64

Fully Paid:已结清 ,Charged Off:坏账 ,Current:当前已还款 , Late (31-120 days):预期30-120天

#In Grace Period :已逾期但在宽限期类 , Default:逾期超过90天

#参考:https://help.bitbond.com/article/20-the-10-loan-status-variants-explained·

  从结果看出 我们的正反案列存在严重的正反案列不均衡问题,后续建模需要处理以下

   3.先把标签处理一下    

#封装一个替换函数
def coding(col, codeDict):
  colCoded
= pd.Series(col, copy=True)#创建一个和loan_status一样的 Series for key, value in codeDict.items():#返回可遍历的(键, 值) 元组数组 colCoded.replace(key, value, inplace=True)#替换原有数据 return colCoded
##把贷款状态LoanStatus编码为违约=1, 正常=0:
dict1={'Current':0,'Fully Paid':0,'Charged Off':1,'Late (31-120 days)':1,'Late (16-30 days)':1,'In Grace Period':1,"Default":1}data["loan_status_class"]=coding(data["loan_status"],dict1)
data.loan_status_class.value_counts()
0    341415
1     79680
Name: loan_status_class, dtype: int64
3.处理缺失值
#查看缺失值
for i in data.columns:
    miss=data[i].isnull().sum()
    print(i,"\t",miss)

Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡

 截图不完整

发现很多变量全为空,这种数据对我们没有任何价值,先处理掉这些无用数据。

#删除缺失别列0.8以上的列
half_count = len(data)*0.8 # 设定阀值
data = data.dropna(thresh = half_count, axis = 1 ) #若某一列数据缺失的数量超过阀值就会被删除
data.shape
#data = data.drop(['desc', 'url','id'], axis = 1) #删除了一些无用列
(421095, 93)  
#剩下93 列
4.数据描述
由于数据特征较多,这里筛选一些变量做描述
col=["loan_amnt","term","int_rate","grade","emp_length","annual_inc","verification_status","loan_status","purpose","dti","delinq_2yrs","inq_last_6mths",'open_acc',"pub_rec","revol_bal","total_acc","total_rev_hi_lim","addr_state","home_ownership","emp_title","loan_status_class"]
df=data[col]   
df.columns=["申请额度","借款期限","利率","评级","工作年限","年收入","收入来源是否核实","借款状态","借款目的","负债率","近两年逾期30天以上的次数","近6个月征信查询次数","未结清借款数","负面记录","未结清借款总额","剩余信用额度","总授信额度","所在地","住房状态","职位","分类"]
#描述分类属性依据好坏样本的分布情况
cla=["借款期限","评级","工作年限","收入来源是否核实","借款目的","住房状态"]
for i in cla:
    pvt=pd.pivot_table(df[["分类",i]],index=i,columns="分类",aggfunc=len) 
    pvt.plot(kind="bar")

Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡

 

 Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡

由图可知:
1.(左上)大部分人选择36期贷款,少部分选择60期,但是60期逾期百分比明显高于36期,并且高达37%,借款时间越长,风险越大。
2.(右上)LC自评等级,这个评级与利息相关的,随着评级下降风险越来越高,利息越来越高,我们可以认为相应的逾期率较大,本图也反馈了LC 评级的优异性能
3.(左下)值得注意的是,工作年限10年以上的借款人相对较多,这与我们的一般认知不符合,除此以外工作1-9年的人群随着工作年限加长,借款需求相对减少,可能是收入相对稳定了,
(题外话:如果这个假设成立,为什么我自己工作年限越长,就越穷呢,是因为能力么,我太难了=。=)
4.(右下)收入来源是否经过核实,大部分是经过核实的,并且经过核实的违约概率相对较低
Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡

5.借款目的   债务整合,家具装修,还信用卡,三类最多(还是比较诚实,这也侧面说明 这个特征可能用处不大)

6.住房状态上      按揭 与租房 最多,相对的 租房违约率较高 (符合一般家庭情况)

对数值型数据进行描述
cel=[i for i in df1.columns if df1[i].dtypes =="float"]
for i ,j in enumerate(cel):
    plt.figure(figsize=(8,5*len(cel)))
    plt.subplot(len(cel),1,i+1)
    sns.distplot(df1[j][df1.分类==0],color="b")
    sns.distplot(df1[j][df1.分类==1],color="r")

Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡

 由图可知:

     1.(左上)借款额度呈正太分布,稍有一点左偏,表明业务多集中在中小额度上面,且额度越高逾期率相对有所增加。

     2.(右上)年收入集中在15万以内,个别极高收入达到400万。

     3.(左下)负债率呈现正太分布,多集中在40%以内

     4.(右下)近两年逾期30天以上的次数,说明即使一次逾期记录也没有,客户也是可能逾期的

Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡

 由图可知:

     1.征信查询,查的越多越容易逾期

      2.没有负面记录也是会逾期的,但是有负面记录的人逾期率要高得多。

用词云图看看,申请地与职位的频率

text=["职位","所在地"]
str_list=["",""]
for i,j in enumerate(text):
    for k in df1[j].values:
        str_list[i]+=str(k) + " "
print(str_list)
#分别设置了背景颜色,宽度,与高度
from wordcloud import WordCloud
wordcloud=WordCloud(background_color="white",width=1000, height=860, margin=2).generate(str_list[0])
plt.imshow(wordcloud)
plt.axis("off")

wordcloud=WordCloud(background_color="white",width=1000, height=860, margin=2).generate(str_list[1])
plt.imshow(wordcloud)
plt.axis("off")
#在制作词云图时,文本之间提前预留空格,作图时间会非常快,相当于自己提前分词

Lending Club 数据做数据分析&评分卡Lending Club 数据做数据分析&评分卡

 1.借款人 职务 大多是公司职员,

 2.借款人主要集中在,加利福利亚,纽约 德克萨斯州(该公司中部在加州)

探索借款用途与利率之间的关系

df['int_rate_num'] = df['int_rate'].str.rstrip("%").astype("float")# 删除 利率后面的百分号,并且转换成 浮点型数据
sns.boxplot(y="purpose",x="int_rate_num",data=df)

Lending Club 数据做数据分析&评分卡

 借款用途为 small_business 的借款利率最高

 探索探索利率 收入 工作年限 和借款状态之间的关系

#替换数据的第二种方法
mapping_dict = {
    "emp_length": {
        "10+ years": 10,
        "9 years": 9,
        "8 years": 8,
        "7 years": 7,
        "6 years": 6,
        "5 years": 5,
        "4 years": 4,
        "3 years": 3,
        "2 years": 2,
        "1 year": 1,
        "< 1 year": 0,
        "n/a": 0
    }
}

df = df.replace(mapping_dict) #转换
df["annual_inc"]=df["annual_inc"].astype("float") #把收入中odjest 转换成float
sns.pairplot(df, vars=["int_rate_num","annual_inc", "emp_length"],hue="loan_status_class", diag_kind="kde" ,kind="reg", size = 3)

Lending Club 数据做数据分析&评分卡

 

 可理解为 工作年限越长,收入越高违约情况相对较低,相应的享受更低的利息

 

 简单看看相关性

sns.heatmap(df1.corr())

Lending Club 数据做数据分析&评分卡

 

 除去对角线以外颜色越浅相关信息越高

 5.建模准备工作

     1.查看缺失值具体情况,并决定填充策略    

#查看缺失值情况并决定哪些需要删除
data_defect=[i for i in data.columns if (data[i].isnull().sum())/data.shape[0] != 0]
for i in data_defect:
defect=data[i].isnull().sum()/data.shape[0]
print( i , defect)

Lending Club 数据做数据分析&评分卡

data=data.drop(["mths_since_recent_bc","mths_since_recent_inq"],axis=1)
#众数填充
fil=["emp_title","emp_length","title","dti","num_rev_accts","num_tl_120dpd_2m","percent_bc_gt_75"]
from scipy.stats import mode # 计算众数模块
for i in fil:
    data[i][data[i].isnull()]=mode(data[i][data[i].notnull()])[0][0]
#再看看缺失值情况

objectcolumns=[i for i in data.columns if data[i].dtype=="object"]
data[objectcolumns].isnull().sum().sort_values(ascending=False)

Lending Club 数据做数据分析&评分卡

data[objectcolumns].head()
#发现 int_rate    与revol_util  实际数数值,但是含有% 被识别为字符,借款周期需要处理,工龄需要处理
data.int_rate= data.int_rate.str.rstrip('%').astype('float')
data.revol_util= data.revol_util.str.rstrip('%').astype('float')#删除末尾指定字符,并转化成数值
data["term"]=data["term"].str.rstrip("months").astype("float")
objectcolumns=[i for i in data.columns if data[i].dtypes=="object"]
data[objectcolumns].isnull().sum().sort_values()
#数据过滤
var = data[objectcolumns].columns for v in var: print('\nFrequency count for variable {0}'.format(v)) print(data[v].value_counts()) data[objectcolumns].shape

Lending Club 数据做数据分析&评分卡

drop_list=["sub_grade","title","zip_code","last_pymnt_d","last_credit_pull_d"]
data.drop(drop_list,axis=1,inplace=True)
#创建一个vacancy 类型,填充缺失值
objectcolumns=[i for i in data.columns if data[i].dtype=="object"]
data[objectcolumns]=data[objectcolumns].fillna("vacanct")
import missingno as msno   # 缺失值可视化
msno.matrix(data[objectcolumns])

Lending Club 数据做数据分析&评分卡

#查看float数据类型缺失情况
floatcolumns=[i for i in data.columns if data[i].dtype=="float"]
data[floatcolumns].isnull().sum().sort_values(ascending=False)

Lending Club 数据做数据分析&评分卡

对于数值型数据我们先采用均值填补

from sklearn.preprocessing import Imputer
imp = Imputer(missing_values=np.nan , strategy='mean',copy=False, axis=0)
imp=imp.fit(data[floatcolumns])
data[floatcolumns]=imp.transform(data[floatcolumns])

Lending Club 数据做数据分析&评分卡

 

 极端值这里暂时不做处理,因为是做评分卡,后续会做分箱操作

对object数据再次进行数据过滤,看看是否需要筛选


objectColumns = [i for i in data.columns if data[i].dtype=="object"]
var = data[objectColumns].columns
for v in var:
    print('\nFrequency count for variable {0}'.format(v))
    print(data[v].value_counts())

Lending Club 数据做数据分析&评分卡

data_drop=data[["sub_grade","pymnt_plan","title","last_pymnt_d","last_pymnt_d","last_credit_pull_d","application_type","hardship_flag",
              "debt_settlement_flag"]]
data=data.drop(data_drop,axis=1)

  2.特征抽象

  这里我们优先使用类别标签,暂时不用哑变量,后续看模型效果也可以尝试哑变量

data_list={
    "grade":{"A":1,"B":2,"C":3,"D":4,"E":5,"F":6,"G":7},
    "emp_length":{"10+ years":11,"2 years":2,"< 1 year":0,"3 years":3,"1 year":1,"5 years":5,"4 years":6,"vacanct":0,
                  "8 years":8,"7 years":7,"6 years":6,"9 years":9 },
    "home_ownership":{"MORTGAGE":1,"RENT":2,"OWN":3,"ANY":4 },
    "verification_status":{"Source Verified":1,"Verified":2,"Not Verified":3},
    "loan_status":{'Current':0,'Fully Paid':0,'Charged Off':1,'Late (31-120 days)':1,'Late (16-30 days)':1,'In Grace Period':1,"Default":1},
    "purpose":{"debt_consolidation":1,"credit_card":2,"home_improvement":3,"other":4,"major_purchase":5,"medical":6,"car":7,
               "small_business":8,"moving":9,"vacation":10,"house":11,"renewable_energy":12,"wedding":13,"educational":14},
    "initial_list_status":{"w":1,"f":2},
    "term":{36.0:1,60.0:2}
}
data=data.replace(data_list)#映射
n_columns = ["home_ownership","verification_status","purpose","application_type"] 
dummy_df = pd.get_dummies(data[n_columns])# 用get_dummies进行one hot编码
loans = pd.concat([data, dummy_df], axis=1) #当axis = 1的时候,concat就是行对齐,然后将不同列名称的两张表合并
data = data.drop(n_columns, axis=1)  #清除原来的分类变量
哑变量编码

相关文章: