網貸平台Prosper數據分析預測
ProsPer:美國P2P網貸平台是一個通過讓有借款需求者和有閑置資金的出資人能夠自行配對的平台站點,目前擁有超過98萬會員,超過2億美元的借貸額,是目前世界上最大的P2P借貸平台。
1.摘要
本文詳述了如何通過數據預覽,基本數據分析、探索式數據分析,缺失數據填補等方法,實現對kaggle上Prosper借貸平台貸款者還款與否這一分類問題如何進行數據分析的具體探索式實踐。
2.項目內容介紹
Prosper LoanData是Joshua Schnessl從Udacity Data Analyst Nanodegree上把數據放到kaggle的上供感興趣的人分析的一個實例項目,這並非一個競賽項目。本人試圖通過訓練數據集分析出什麼類型的借款人更可能不違約,並預測出測試數據集中的每筆貸款是否違約。
本人將LoanStatus(貸款狀態)分成完成和違約兩種,所以把該項目定義為一個二元分類問題。
3.理解數據
3.1.導入數據
載入相關數據處理包
# 忽略警告提示import warningswarnings.filterwarnings(ignore)#導入處理數據包import pandas as pdimport numpy as npimport datetimeimport pandas as pdfrom scipy import stats as st#Plottingimport matplotlib.pyplot as plt%matplotlib inline#導入數據#訓練數據集proDf=pd.read_csv("./prosperLoanData.csv")
從上可見,數據集包含81個變數,113,937條數據,由於數據變數太多,本文只對重要變數作解釋說明,見下圖:
數據說明
- LoanStatus:貸款狀態(Completed、Current、Defaulted、Chargedoff等)
- EstimatedEffectiveYield 估計借錢人有效收益
- EmploymentStatus:受僱傭狀態(Self-employed、Employed等)
- EmploymentStatusDuration:受僱傭狀態持續時間(以月為計算單位)
- IsBorrowerHomeowner:借款人是否擁有房屋
- CreditScoreRangeLower/CreditScoreRangeUpper:消費信用最低/最高分
- InquiriesLast6Months:最近6個月查過多少次徵信記錄
- BorrowerRate:借款標利率,作為P2P平台資金借貸價格的代理變數,BorrowerRate不包含其他費用,是籌資者付給投資人的報酬,也是融資最直接和最重要的成本,其體現了資金供求雙方在綜合考慮各種因素情況下所認可的資金使用成本.
- Term:期限,籌資者通過網貸平台進行借款時所承諾的最終償還期限,借款期限體現該資產的流動性,期限較長的資產應存在著流動性溢價(利率上漲).
- CreditGrade/ProsperRating(Alpha):信用等級,前者反映的是2009年7月1日前客戶的信用等級,後者反映的是2009年7月1日後的信用等級.信用等級越高,其償債能力越強.
- CreditScore:由消費信用公司提供的消費信用評分,類似於國內的芝麻信用分。
- StatedMonthlyIncome:客戶月收入,月收入越高,投資者對該借款本息按時迴流越有信心.
- DelinquenciesLast7Years:信用資料提交時借款人過去7年違約次數,該指標在一定程度上可以體現借款標的發布者的信用狀況
- BankCarduse:信用資料提交時借款人信用卡使用額度和信用卡總透支額度的百分比,本文將這個數據分成四組(mild use;medium use;heavy use;super use)
- LoanOriginalAmount:借款人在借款時已經向prosper借入的資金,如果沒有歷史記錄則為0,顯然,借入本金越多,其還款壓力越大,但是這項指標大的話也可能說明該客戶對prosper依賴性較強.
- DebtToIncomeRatio:借款人的債務收入比,債務收入比越高說明籌資者財務狀況越差,還款能力較低.其向P2P平台借款時,投資者應要求有更高的回報.
- IncomeRange:貸款人年收入範圍
- Customer_clarify:0或NA是未在prosper貸過款的客戶,反之是貸過款的。
- IncomeVerifiable:收入是否可核查的
3.3將貸款狀態分為違約與不違約
平台把借款狀態分位以上12種:Cancelled(取消)、Chargedoff(沖銷,投資人有損失)、Completed(正常完成,投資人無損失)、Current(貸款還款中)、Defaulted(壞賬,投資人有損失)、FinalPaymentInProgress(最後還款中,投資人無損失)、Past Due(逾期還款,投資人無損失)。
本分析依據交易是在進行正常還款期內還是已關閉將LoanStatus分成兩組,並根據投資人有無損失將已關閉的交易分成Completed和Defaulted:Current(貸款還款中)、Defaulted(包含Defaulted、Chargedoff、Cancelled)、Completed(包含Completed、FinalPaymentInProgress、Past Due)三組。由於本文是研究違約問題,狀態Current(貸款還款中),不能確定是否違約,所以這部分數據不是有效數據,應該去掉,只能用Defaulted(包含Defaulted、Chargedoff、Cancelled)、Completed(包含Completed、FinalPaymentInProgress、Past Due)這2組數據去研究。將LoanStatus進行二分類編碼:0,1
def loanStatus(value): if(value in [Completed,FinalPaymentInProgress,Past Due (1-15 days), Past Due (31-60 days),Past Due (61-90 days) ,Past Due (91-120 days) , Past Due (16-30 days) ]): return 1 else: return 0LSdf["LoanStatus"]=LSdf["LoanStatus"].map(lambda status : loanStatus(status))
4.探索影響違約率的各種因素
為方便後面進行探索性數據分析,應該要統一封裝一個函數,用來去除所研究特徵的缺失值數據,而不是填充缺失值,這樣研究出來的結果會比較靠譜一點,函數如下:
def chooseData(data,colu): data=LSdf[LSdf[colu].notnull()] defaulted=LSdf[LSdf.LoanStatus==0] return data,defaulted
4.1收入情況對違約率的影響
4.1.1收入穩定性(有工作的比沒工作的借款人違約率低,且工作越穩定違約率越低)
data1,defaulted1=chooseData(LSdf,EmploymentStatus)grouped1 = data1[LoanStatus].groupby(data1[EmploymentStatus]).count()grouped2 = defaulted1[LoanStatus].groupby(defaulted1[EmploymentStatus]).count()grouped3=grouped2/grouped1df1=pd.DataFrame([grouped2,grouped1,grouped3]).Tdf1
plt.figure(figsize=(14,7))grouped3.plot.bar()plt.xticks(rotation=1)plt.title(僱傭狀態與違約率關係)plt.xlabel(僱傭狀態)plt.ylabel(違約率)plt.show()
4.1.2工作年限
data2,defaulted2=chooseData(LSdf,EmploymentStatusDuration)min=data2.EmploymentStatusDuration.min()max=data2.EmploymentStatusDuration.max()duration_all = pd.cut(data2.EmploymentStatusDuration, bins = [min,6,12,36,60,120,180,240,360,max])duration_defaulted=pd.cut(defaulted2.EmploymentStatusDuration, bins = [min,6,12,36,60,120,180,240,360,max])duration_all_count = duration_all.value_counts()duration_defaulted_count = duration_defaulted.value_counts()rate=(duration_defaulted_count/duration_all_count)plt.figure(figsize=(12,8))rate.plot.bar()plt.xlabel(違約率)plt.xticks(rotation=1)plt.ylabel(工作年限)plt.title(工作時間與違約率)plt.show()
持續工作了3-5和5-10年的違約率比較低,然後違約率分別向左右兩邊上升
- 低於10年工作經驗是,工作年限越高違約率越低
- 高於10年工作經驗以後,工作年限越長,違約率越高。可能10年以後事業收入遇到瓶頸
4.1.3 年收入水平(年收入越高,違約率越低)
data3,defaulted3=chooseData(LSdf,IncomeRange)income1=data3[LoanStatus].groupby(data3[IncomeRange]).count()income2=defaulted3[LoanStatus].groupby(defaulted3[IncomeRange]).count()income3=income2/income1df3=pd.DataFrame([income2,income1,income3]).Tplt.figure(figsize=(12,8))income3.plot.bar()plt.xlabel(收入)plt.xticks(rotation=1)plt.ylabel(違約率)plt.show()
4.2 借款人固定資產與違約率的關係
4.2.1 借款人有房產比沒房產的違約率要低一點,低5%左右
data4,defaulted4=chooseData(LSdf,IsBorrowerHomeowner)house_all = data4[LoanStatus].groupby(data4[IsBorrowerHomeowner]).count()house_default= defaulted4[LoanStatus].groupby(defaulted4[IsBorrowerHomeowner]).count()default_rate2=house_default/house_allplt.figure(figsize=(5,5))default_rate2.plot(kind=bar)plt.xticks(rotation=1)plt.title(是否有房產與違約率關係)plt.xlabel(False:沒房產,True:有房產)plt.ylabel(違約率)plt.show()
4.3借款人信用情況
4.3.1信用等級(信用等級越高,違約率越低)
data6,defaulted6=chooseData(LSdf,ProsperRating (Alpha))credit_all = data6[LoanStatus].groupby(data6[ProsperRating (Alpha)]).count()credit_default= defaulted6[LoanStatus].groupby(defaulted6[ProsperRating (Alpha)]).count()default_rate3=credit_default/credit_alldefault_rate3plt.figure(figsize=(20,10))default_rate3.plot(kind=bar)plt.xticks(rotation=1)plt.title(信用等級與違約率關係)plt.xlabel(信用等級)plt.ylabel(違約率)plt.show()
由於信用等級由高到低的順序為:AA>A>B>C>D>E>HR
可見信用等級越高,違約率越低,所以這是評估借款人是否會違約的一個很重要的依據
4.3.2過去7年信用總額度
data7,defaulted7=chooseData(df_0915,TotalCreditLinespast7years)min=data7.TotalCreditLinespast7years.min()max=data7.TotalCreditLinespast7years.max()all_df= pd.cut(data7.TotalCreditLinespast7years, bins = [min,20,40,60,80,100,max])defaulted_df=pd.cut(defaulted7.TotalCreditLinespast7years, bins = [min,20,40,60,80,100,max])all_count = all_df.value_counts()defaulted_count = defaulted_df.value_counts()default_rate1=(defaulted_count/all_count).sort_values()plt.figure(figsize=(20,10))default_rate1.plot(kind=bar )plt.xticks(rotation=1)plt.title(信用額度與違約率關係)plt.xlabel(信用總額度)plt.ylabel(違約率)plt.show()
數據顯示,信用總額度低於100時,信用總額度越高,違約率越低。
2.3.3過去7年違約次數
data8,defaulted8=chooseData(df_0915,DelinquenciesLast7Years)complete8=data8[data8.LoanStatus==1]default= defaulted8[LoanStatus].groupby(defaulted8[DelinquenciesLast7Years]).count()complete= complete8[LoanStatus].groupby(complete8[DelinquenciesLast7Years]).count()plt.figure(figsize=(20,5))default.plot(kind=line,label=違約,c=r )complete.plot(kind=line,label=不違約,c=b )plt.xticks([0,1,5,10,15,25,30])plt.title(違約次數與違約率關係)plt.xlabel(違約率次數)plt.ylabel(數量)plt.xlim((0, 30))plt.legend( loc=best)plt.show()
過去七年違約次數(DelinquenciesLast7Years)能夠衡量一個人在過去七年中徵信情況,違約一次或以上的人在借款時違約概率更大。如下圖所示,DelinquenciesLast7Years < 7的借款人中,違約筆數與未違約的筆數線貼合。從下圖也可看出大部分借款人的DelinquenciesLast7Years 在1次以下,說明整個平台的風險可控。
5.數據清洗
挑選欄位原則
1.這裡81個欄位里,像CreditGrade ,ProsperRating (numeric) ,ProsperScore ,欄位缺失值太多,幾乎有一半以上,所以在預測上,放棄使用這些欄位
2.觀察原始數據,結合實際應用,像BorrowerAPR,BorrowerRate,LenderYield這些欄位之間本身就是存在很大的線性相關,只選用其中一個這種欄位數據;
3.捨棄一些像ListingNumber,LoanKey等標記性的欄位數據
columns=[ ProsperRating (numeric),#平台評分 LoanStatus,#貸款狀態 EmploymentStatus,#僱傭狀態 EmploymentStatusDuration,#僱傭時長 IsBorrowerHomeowner,#是否有房屋 CreditScoreRangeLower,#消費信用最低 CreditScoreRangeUpper,#消費信用最高分 CurrentCreditLines,#總信用額度 OpenCreditLines,#公開信用額度 TotalCreditLinespast7years,#過去7年的總信用額度 OpenRevolvingAccounts,#公開帳戶 OpenRevolvingMonthlyPayment,#申請貸款已有的月供 InquiriesLast6Months,#最近6個月查過多少次徵信記錄 TotalInquiries,#被催款次數 CurrentDelinquencies,#不良次數 AmountDelinquent,#不良金額數 LoanOriginalAmount,#原始金額的貸款 RevolvingCreditBalance,#循環信貸餘額 BankcardUtilization,#銀行卡利用率 TradesNeverDelinquent (percentage),#交易從來沒有拖欠 DebtToIncomeRatio,#借款人的債務收入比 IncomeRange,#貸款人年收入範圍 IncomeVerifiable,#可核查的收入 StatedMonthlyIncome, MonthlyLoanPayment#每月付息]data=LSdf.loc[:,columns]data.head()
5.2.缺失值處理
5.2.1將數據類型為object的欄位的缺失值用unknown填充
categorical=data.select_dtypes(include=[object]).columns.valuesdata[categorical]=data[categorical].fillna(unknown)data.select_dtypes(exclude=[np.number]).isnull().sum()
5.2.2將數值類型缺失值用中間值填充
#篩選出數值類型欄位categorical_num = data.select_dtypes(include=[np.number"]).columns.values# 篩選有缺失值欄位的函數def find_na_column(df,columns): miss_columns = [] for column in columns: if (df[column].isnull().sum())> 0: miss_columns.append(column) return miss_columns#篩選出數值類型的有缺失值的欄位categorical_num=find_na_column(data,categorical_num)## 用中間值填充缺失值的函數def fillNull(column): null_count=data[column].isnull().sum() sum_count=data.shape[0] null_rate=null_count/sum_count data[column]=data[column].fillna( data[column].median() ) #調用函數填充缺失值for column in categorical_num: fillNull(column)data.select_dtypes(include=[np.number]).isnull().sum()
5.3將object轉換為int類型
5.3.1 將rangeIncome的數據類型轉換為int
data[IncomeRange].unique()
def incomeType(value): if(value==$0 ): return 0 elif(value==$1-24,999): return 1 elif(value==$25,000-49,999): return 2 elif(value==$50,000-74,999): return 3 elif(value==$75,000-99,999): return 4 elif(value==$100,000+): return 5 elif(value==Not employed): return 6 else: return 7data.IncomeRange=data.IncomeRange.map(lambda range: incomeType(range))data[IncomeRange].unique()
5.2.3將EmploymentStatus數據類型轉換為int
5.4 清洗後的數據預覽
6.建模
根據第5步選擇並清洗後的特徵向量,建立一個預測借款人是否違約的模型。
數據建模————隨機森林分類模型
y=data[LoanStatus]data.drop("LoanStatus",axis=1, inplace=True)X=datafrom sklearn.cross_validation import train_test_split #這裡是引用了交叉驗證 X_train,X_test, y_train, y_test = train_test_split(X, y, train_size=.7)#輸出數據集大小print (原始數據集特徵:,X.shape, 訓練數據集特徵:,X_train.shape , 測試數據集特徵:,X_test.shape)print (原始數據集標籤:,y.shape, 訓練數據集標籤:,y_train.shape , 測試數據集標籤:,y_test.shape)
from sklearn.ensemble import RandomForestClassifier rf1 = RandomForestClassifier() rf1.fit(X_train,y_train) pred_y=rf.predict(X_test)# 分類問題,score得到的是模型的準確率rf1.score(X_test , y_test )
總結:
1.對於這次高達99.8%的預測準確率,我感覺有點不真實了,不過數據就是如此。同時,我也嘗試把特徵數據從25個減少到12個相關性更高的的欄位數據,而準確率馬上降低到72.1%。
2.由此可以知,類似的網貸平台的風控預測系統需要儘可能地收集借款人的各種信息,這樣用於建模的特徵就越多,模型的準確率就越高,就越能減少整個平台的經營風險,降低未知的損失。
推薦閱讀: