本文是解密大數(shù)據(jù)社群一期課程的結(jié)業(yè)作業(yè)。項(xiàng)目分為數(shù)據(jù)探索,使用sklearn完成回歸和預(yù)測(cè),利用神經(jīng)網(wǎng)絡(luò)進(jìn)行預(yù)測(cè) 三部分。目前編碼工作基本完成,整理后陸續(xù)貼上來(lái)。
數(shù)據(jù)集介紹
采用kaggle平臺(tái)上的債務(wù)違約預(yù)測(cè)數(shù)據(jù)集。其中包含150000個(gè)用戶的信貸信息。
變量名 | 變量類型 | 變量含義 |
---|---|---|
SeriousDlqin2yrs | 取值為0或1 | 兩年內(nèi)是否發(fā)生了90天以上逾期,即是否違約 |
RevolvingUtilizationOfUnsecuredLines | percentage | 無(wú)抵押貸款循環(huán)使用率,除不動(dòng)產(chǎn)和車貸之外的貸款余額與個(gè)人信用總額度之比 |
age | integer | 借款人年齡 |
NumberOfTime30-59DaysPastDueNotWorse | integer | 過(guò)去兩年中發(fā)生30-59天逾期的次數(shù) |
DebtRatio | percentage | 負(fù)債比率 |
MonthlyIncome | real | 月收入 |
NumberOfOpenCreditLinesAndLoans | integer | 開(kāi)放貸款(如汽車貸款或抵押貸款)和信用貸款數(shù)量(從數(shù)值看,應(yīng)該是貸款的筆數(shù)) |
NumberOfTimes90DaysLate | integer | 過(guò)去兩年中發(fā)生90天逾期的次數(shù) |
NumberRealEstateLoansOrLines | integer | 抵押貸款和不動(dòng)產(chǎn)貸款數(shù)量 |
NumberOfTime60-89DaysPastDueNotWors | integer | 過(guò)去兩年中發(fā)生60-89天逾期的次數(shù) |
NumberOfDependents | integer | 家屬數(shù)目 |
SeriousDlqin2yrs 是要預(yù)測(cè)的對(duì)象,又稱因變量,即根據(jù)其他變量判斷用戶會(huì)不會(huì)發(fā)生違約。其他10個(gè)變量為自變量,分為兩類:客戶自身屬性(年齡,月收入,家屬數(shù)目),客戶信貸歷史(負(fù)債比率,循環(huán)貸款使用率,開(kāi)放貸款數(shù)目等)。自變量較少,而且它們對(duì)是否發(fā)生違約都有不同程度的影響,所以這里就不進(jìn)行特征工程。
分析目標(biāo)
1.針對(duì)數(shù)據(jù)集,分析當(dāng)前用戶的信貸信息,各自變量對(duì)因變量的影響。
2.從數(shù)據(jù)中獲得模型,預(yù)測(cè)用戶發(fā)生違約的可能性
%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.gridspec as gridspec
pd.set_option("display.max_columns",101)
pd.set_option('display.float_format', lambda x: '%.5f' % x) #為了直觀的顯示數(shù)字,不采用科學(xué)計(jì)數(shù)法
pd.options.display.max_rows = 15 #最多顯示15行
import warnings
warnings.filterwarnings('ignore') #為了整潔,去除彈出的warnings
數(shù)據(jù)概覽
df = pd.read_csv("cs-training.csv") # 讀入數(shù)據(jù)
df # Unnamed 列是csv文件中的索引列,可以刪除
df.dtypes # 查看每列的數(shù)據(jù)類型
Unnamed: 0 int64
SeriousDlqin2yrs int64
RevolvingUtilizationOfUnsecuredLines float64
age int64
NumberOfTime30-59DaysPastDueNotWorse int64
DebtRatio float64
MonthlyIncome float64
NumberOfOpenCreditLinesAndLoans int64
NumberOfTimes90DaysLate int64
NumberRealEstateLoansOrLines int64
NumberOfTime60-89DaysPastDueNotWorse int64
NumberOfDependents float64
dtype: object
df.describe()
從以上一組數(shù)字特征中可以看出什么:
從count值中看出MonthlyIncome和NumberOfDependents有缺失值(因?yàn)檫@兩列的count值小于150000,表格太長(zhǎng),截圖中顯示不出)。SeriousDlqin2yrs的均值為0.06684,說(shuō)明違約率是6.684%(發(fā)生違約的客戶,該值為1,把所有值加起來(lái)就是違約客戶的個(gè)數(shù),除以客戶總數(shù)也就相當(dāng)于該列的均值)。age的最小值為0,存在異常值,銀行不可能給18歲以下客戶貸款。
df.isnull().sum() # 計(jì)算每個(gè)列的空值數(shù)目,MonthlyIncome和NumberOfDependents的缺失值分別為29731和3924。
Unnamed: 0 0
SeriousDlqin2yrs 0
RevolvingUtilizationOfUnsecuredLines 0
age 0
NumberOfTime30-59DaysPastDueNotWorse 0
DebtRatio 0
MonthlyIncome 29731
NumberOfOpenCreditLinesAndLoans 0
NumberOfTimes90DaysLate 0
NumberRealEstateLoansOrLines 0
NumberOfTime60-89DaysPastDueNotWorse 0
NumberOfDependents 3924
dtype: int64
數(shù)據(jù)清洗
df=df.drop(df.columns[0], axis=1) # 刪除Unnamed列
df[df['age']<18] # 找出年齡小于18的客戶,只有一行
df=df[df.age>=18] #只保留年齡大于18的客戶
初步分析
先查看違約率在每個(gè)自變量上的分布,即生成下列樣式的頻率分布表。第一個(gè)比例是每個(gè)區(qū)間人數(shù)對(duì)總
人數(shù)的占比,第二個(gè)比例是該區(qū)間上違約人數(shù)的占比。
#從RevolvingUtilizationOfUnsecuredLines開(kāi)始嘗試, 讀取RevolvingUtilizationOfUnsecuredLines 和 SeriousDlqin2yrs兩列數(shù)據(jù)
df_tmp=df[['SeriousDlqin2yrs','RevolvingUtilizationOfUnsecuredLines']]
#增加標(biāo)簽列,即給每行數(shù)據(jù)打上一個(gè)標(biāo)簽,標(biāo)示它屬于哪個(gè)區(qū)間。可以利用pandas提供的
cut函數(shù)。該函數(shù)把連續(xù)變量轉(zhuǎn)換成分類變量,例如把取值[1,2,……100]的變量映射為[1-10],[11-20]
def binning(col, cut_points, labels=None):
minval = col.min()
maxval = col.max()
break_points = [minval] + cut_points + [maxval]
if not labels:
labels = range(len(cut_points)+1)
else:
labels=[str(i+1)+":"+labels[i] for i in range(len(cut_points)+1)]
colBin = pd.cut(col,bins=break_points,labels=labels,include_lowest=True)
return colBin
cut_points = [0.25,0.5,0.75,1,2]
labels = ["below 0.25","0.25-0.5","0.5-0.75","0.75-1.0","1.0-2.0","above 2"]
#增加新列,它的值就是生成的標(biāo)簽
df_tmp["Utilization_Bin"] = binning(df_tmp["RevolvingUtilizationOfUnsecuredLines"], cut_points, labels)
#查看標(biāo)簽列,取值范圍前面加上了序號(hào),是便于后面生成表格時(shí)按順序排列
df_tmp
#為了計(jì)算比例,要獲得總?cè)藬?shù),即df_tmp的行數(shù)
total_size=df_tmp.shape[0]
#使用pandas的pivot_table函數(shù)生成匯總表
per_table=pd.pivot_table(df_tmp,index=['Utilization_Bin'], aggfunc={"RevolvingUtilizationOfUnsecuredLines":[len, lambda x:len(x)/total_size*100],"SeriousDlqin2yrs":[np.sum] },values=['RevolvingUtilizationOfUnsecuredLines','SeriousDlqin2yrs'])
#已經(jīng)很接近上面的表格,還要計(jì)算違約人數(shù)所占比例
per_table
#用該區(qū)間違約人數(shù)除以該區(qū)間總?cè)藬?shù)即可。注意表格的表頭有兩行,看看它的columns有什么特殊的地方
per_table.columns
MultiIndex(levels=[['RevolvingUtilizationOfUnsecuredLines', 'SeriousDlqin2yrs'], ['<lambda>', 'len', 'sum']],
labels=[[0, 0, 1], [0, 1, 2]])
#這是一個(gè)有多重索引的dataframe,如果要定位其中某列,可按層次進(jìn)行
per_table['RevolvingUtilizationOfUnsecuredLines','<lambda>']
Utilization_Bin
1:below 0.25 58.43839
2:0.25-0.5 14.03676
3:0.5-0.75 9.17606
4:0.75-1.0 16.13477
5:1.0-2.0 1.96668
6:above 2 0.24733
Name: (RevolvingUtilizationOfUnsecuredLines, <lambda>), dtype: float64
#因此,添加新列時(shí)也要按層次寫(xiě)列名。增加percent列,它的值來(lái)自另外兩列數(shù)值之比
per_table['SeriousDlqin2yrs','percent']=per_table['SeriousDlqin2yrs','sum']/per_table['RevolvingUtilizationOfUnsecuredLines','len']*100
# 函數(shù)自動(dòng)生成的列名不好理解,進(jìn)行重命名
per_table=per_table.rename(columns={'<lambda>':'percent','len': 'number','sum':'number'})
per_table
#把number放在前面,percent放后面更合理,用reindex_axis調(diào)整順序
per_table=per_table.reindex_axis((per_table.columns[1],per_table.columns[0],per_table.columns[2],per_table.columns[3]),axis=1)
per_table
# 把上述生成頻率表的過(guò)程寫(xiě)成函數(shù),用于對(duì)每個(gè)自變量進(jìn)行類似處理
def get_frequency(df,col_x,col_y, cut_points, labels,ifright=True):
df_tmp=df[[col_x,col_y]]
df_tmp['columns_Bin']=binning(df_tmp[col_x], cut_points, labels,ifright)
total_size=df_tmp.shape[0]
per_table=pd.pivot_table(df_tmp,index=['columns_Bin'], aggfunc={col_x:[len, lambda x:len(x)/total_size*100],col_y:[np.sum] },values=[col_x,col_y])
per_table[col_y,'percent']=per_table[col_y,'sum']/per_table[col_x,'len']*100
per_table=per_table.rename(columns={'<lambda>':'percent','len': 'number','sum':'number'})
per_table=per_table.reindex_axis((per_table.columns[1],per_table.columns[0],per_table.columns[2],per_table.columns[3]),axis=1)
return per_table
cut_points=[25,35,45,55,65]
labels=['below 25', '26-35', '36-45','46-55','56-65','above 65']
feq_age=get_frequency(df,'age','SeriousDlqin2yrs', cut_points, labels)
feq_age
和上一個(gè)表格不太一樣,怎么回事?
#換一個(gè)變量試試
cut_points = [0.25,0.5,0.75,1,2]
labels = ["below 0.25","0.25-0.5","0.5-0.75","0.75-1.0","1.0-2.0","above 2"]
feq_ratio=get_frequency(df,'DebtRatio','SeriousDlqin2yrs', cut_points, labels)
feq_ratio
又符合要求了?看來(lái)對(duì)不同的列,生成的表格不太一樣。回到get_frequency函數(shù)里面,一點(diǎn)點(diǎn)運(yùn)行,找到原因。
給dataframe添加列的時(shí)候,是直接往后加的。假如表格是
再添加['SeriousDlqin2yrs','percent']列,只會(huì)加在最后,變成
而不會(huì)和最前面的['SeriousDlqin2yrs','percent']合并在一起。應(yīng)該可以通過(guò)列名來(lái)調(diào)整順序,但這個(gè)multiindex我還沒(méi)完全弄懂……采用折中辦法,先把第一列挪到后面,再添加新列。
# 重新修改函數(shù)
def get_frequency(df,col_x,col_y, cut_points, labels):
df_tmp=df[[col_x,col_y]]
df_tmp['columns_Bin']=binning(df_tmp[col_x], cut_points, labels)
total_size=df_tmp.shape[0]
per_table=pd.pivot_table(df_tmp,index=['columns_Bin'], aggfunc={col_x:[len, lambda x:len(x)/total_size*100],col_y:[np.sum] },values=[col_x,col_y])
if(per_table.columns[0][0]!=col_x): #假如col_x不在第一列,說(shuō)明是在第2、3列,就把它們往前挪
per_table=per_table.reindex_axis((per_table.columns[1],per_table.columns[2],per_table.columns[0]),axis=1)
per_table[col_y,'percent']=per_table[col_y,'sum']/per_table[col_x,'len']*100
per_table=per_table.rename(columns={'<lambda>':'percent','len': 'number','sum':'number'})
per_table=per_table.reindex_axis((per_table.columns[1],per_table.columns[0],per_table.columns[2],per_table.columns[3]),axis=1)
return per_table
cut_points=[25,35,45,55,65]
labels=['below 25', '26-35', '36-45','46-55','56-65','above 65']
feq_age=get_frequency(df,'age','SeriousDlqin2yrs', cut_points, labels)
feq_age
小于25歲的人群和26-35歲的人群,違約率都超過(guò)10%。隨著年齡增加,違約率在下降。
feq_ratio #隨著負(fù)債率的提高,區(qū)間的違約率也不斷增加,負(fù)債率在1-2之間的人群違約率最高。但負(fù)債率大于2的時(shí)候,違約率又下降了。
cut_points=[5,10,15,20,25,30]
labels=['below 5', '6-10', '11-15','16-20','21-25','26-30','above 30']
feq_OpenCredit=get_frequency(df,'NumberOfOpenCreditLinesAndLoans','SeriousDlqin2yrs', cut_points, labels)
feq_OpenCredit
在開(kāi)放性貸款變量上,違約人數(shù)的分布比較均勻。近69%的借貸者有五筆以上貸款(應(yīng)該是把信用卡也算在里面了)。
cut_points=[5,10,15,20]
labels=['below 5', '6-10', '11-15','16-20','above 20']
feq_RealEstate=get_frequency(df,'NumberRealEstateLoansOrLines','SeriousDlqin2yrs', cut_points, labels)
feq_RealEstate
99.47%借貸者的不動(dòng)產(chǎn)和抵押貸款小于5筆,5筆以上的人群違約率明顯增加。
cut_points=[1,2,3,4,5,6,7]
labels=['0', '1','2','3','4','5','6','7 and above']
feq_30days=get_frequency(df,'NumberOfTime30-59DaysPastDueNotWorse','SeriousDlqin2yrs', cut_points, labels,ifright=False)
feq_30days
沒(méi)有發(fā)生過(guò)30-59天逾期的借貸者,違約率只有4%。隨著逾期次數(shù)的增加,違約率不斷升高。
cut_points=[1,2,3,4,5,6,7]
labels=['0', '1','2','3','4','5','6','7 and above ']
feq_60days=get_frequency(df,'NumberOfTime60-89DaysPastDueNotWorse','SeriousDlqin2yrs', cut_points, labels,ifright=False)
feq_60days
cut_points=[1,2,3,4,5,6,7]
labels=['0', '1','2','3','4','5','6','7and above']
feq_90days=get_frequency(df,'NumberOfTimes90DaysLate','SeriousDlqin2yrs', cut_points, labels,ifright=False)
feq_90days
60-89天逾期數(shù)和90天逾期數(shù)這兩個(gè)變量上的違約率也有同樣趨勢(shì),因此是否發(fā)生過(guò)違約,是判斷今后是否會(huì)違約的重要變量。
cut_points=[5000,10000,15000]
labels=['below 5000', '5000-10000','1000-15000','above 15000']
feq_Income=get_frequency(df,'MonthlyIncome','SeriousDlqin2yrs', cut_points, labels)
feq_Income
看起來(lái)是收入越高,違約率越低。但是MonthlyIncome列數(shù)據(jù)缺失較多,只能作為參考。
cut_points = [1,2,3,4,5]
labels = ["0","1","2","3","4","5 and more"]
feq_dependent=get_frequency(df,'NumberOfDependents','SeriousDlqin2yrs', cut_points, labels,ifright=False)
feq_dependent
擁有不同家屬數(shù)量的人群,其違約率沒(méi)有較大區(qū)別。
以上是分區(qū)間對(duì)各變量統(tǒng)計(jì)獲得的信息。
吐個(gè)槽,簡(jiǎn)書(shū)的markdown不支持html。從jupyter notebook上導(dǎo)出的markdown文件里面,dataframe的顯示都是<table>這樣的html代碼,在簡(jiǎn)書(shū)上顯示不出來(lái),只好一個(gè)個(gè)截圖,所以排版看起來(lái)有點(diǎn)亂。
上面提到的cut函數(shù)和pd.pivot_table都是分析數(shù)據(jù)時(shí)很有效的工具。我也是寫(xiě)代碼時(shí)才慢慢摸索出它們的用法。小伙伴們想進(jìn)一步了解的話,記得谷歌大法。也可留言提問(wèn)_。