介紹
對於金融機構的貸款業務來說,一個顧客的信用信息是極其重要的。因為只有了解客戶的信用情況,才能決定是否通過客戶的貸款申請。本次將會介紹如何根據用戶的一些基本信息來判斷顧客的信用或貸款償還能力。
知識點
- 數據導入與預覽
- 數據可視化-plotly
- 特征工程
- 預測模型的選擇
貸款信用評估介紹
本次的內容主要來自於在 Kaggle 上關於貸款信用分析的一個競賽: 貸款償還能力評估 。該競賽是一個含獎競賽,是由 Home Credit 公司發起,並提供相關的數據。我們來簡單看一下相關的信息。
Home Credit 是一家借貸公司,其主要是為那些無銀行賬戶客戶或信用記錄的客戶服務。但為了確保貸款的安全性,需要對這些客戶的信用或者還款能力進行評估。因此 Home Credit 利用各種數據,例如:電話繳費記錄,網購交易信息等來預測其客戶的還款能力。例如:電話繳費記錄、網購交易信息等
而本次的任務則是使用 Kaggle 提供的數據,構建一個模型。使其能夠預測一個顧客的信用或貸款償還能力。
數據導入並預覽
由於原始的數據比較大,考慮到運行時間的問題。這里只取了一部份數據來進行分析。如果你想分析所有的數據,可以去 Kaggle 下載完整的數據。接下來直接導入數據,並預覽前 5 行。
鏈接:https://pan.baidu.com/s/1C-YAhx7qIQPhs7hlDJ3cXg 提取碼:vq1h
import pandas as pd
df = pd.read_csv("HomeCredit.csv")
df.head()
從上面的結果可以看到,該數據集中總共含有 122 列。查看一下數據描述。
df.describe()
查看一下數據的形狀。
df.shape
由上面的輸出結果可知,該數據集總共包含 5000 份數據。
查看一下數據集中都包含哪些列。
df.columns
由於列數較多,這里只選幾個重要的列來講解。如下:
- AMT_CREDIT:貸款金額
- AMT_INCOME_TOTAL:申請人的收入
- AMT_GOODS_PRICE:如果貸款是一個商品的話,商品的價格
- NAME_TYPE_SUITE:陪同申請者來申請的人
- TARGET:申請者是否有能力償還
- NAME_CONTRACT_TYPE:貸款類型
- NAME_INCOME_TYPE:申請者的收入來源情況
- NAME_FAMILY_STATUS:申請者的婚姻狀況
- OCCUPATION_TYPE:申請者的職業類型
- NAME_EDUCATION_TYPE:申請者的受教育情況
- NAME_HOUSING_TYPE:申請者的住房情況
- DAYS_BIRTH:申請人出生到申請當天的總日子數
- DAYS_EMPLOYED:該特征列表示申請人的工作年限
數據可視化分析
現在先來對數據進行可視化,我們來看一下在數據集中申請貸款金額的分布情況。
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings("ignore")
%matplotlib inline
plt.figure(figsize=(12, 5))
plt.title("Distribution of AMT_CREDIT")
ax = sns.distplot(df["AMT_CREDIT"]) # 畫出數據分布圖
從上圖可以看出,大部分申請人的申請貸款金額為 0 到 2000000 區間。
來看一下申請人的收入情況。
plt.figure(figsize=(12, 5))
plt.title("Distribution of AMT_INCOME_TOTAL")
# 畫出數據分布圖
ax = sns.distplot(df["AMT_INCOME_TOTAL"].dropna())
從上圖可以看到,大部分申請人的收入都為 0 。現在看一下,如果貸款的對象是貨物的話,看一下這些貨物的價格分布。
plt.figure(figsize=(12, 5))
plt.title("Distribution of AMT_GOODS_PRICE")
ax = sns.distplot(df["AMT_GOODS_PRICE"].dropna())
上面主要查看幾個常見參數的數據分布。現在我們使用 ploty 繪圖庫來進行更具體的分析。先安裝 Plotly 庫。
!pip install plotly
導入繪圖工具庫。
import plotly.offline as offline
import plotly.graph_objs as go
import plotly.offline as py
from plotly.offline import init_notebook_mode, iplot
init_notebook_mode(connected=True)
offline.init_notebook_mode()
查看一下,這些申請貸款的人來貸款時,都有哪些人陪同。
temp = df["NAME_TYPE_SUITE"].value_counts()
# 畫出柱狀圖
trace = [go.Bar(x=temp.index, y=(temp / temp.sum())*100,)]
# 設置圖的字體顏色等
layout = go.Layout(
title="Who accompanied client when applying for the application in % ",
xaxis=dict(title='Name of type of the Suite',
tickfont=dict(size=14, color='rgb(107, 107, 107)')),
yaxis=dict(title='Count of Name of type of the Suite in %',
titlefont=dict(size=16, color='rgb(107, 107, 107)'),
tickfont=dict(size=14, color='rgb(107, 107, 107)'))
)
fig = go.Figure(data=trace, layout=layout)
iplot(fig, filename='schoolStateNames')
從上圖的顯示結果可以看到,幾乎 80% 的人都沒有人陪同。而只有少部分人有家人或合伙人陪同。現在查看一下,申請人的還款能力。
temp = df["TARGET"].value_counts()
# 畫出餅狀圖
trace = [go.Pie(labels=temp.index, values=temp.values)]
# 設置圖題
layout = go.Layout(
title='Loan Repayed or not',
)
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
在上圖中,1 表示有償還能力,0 表示沒有償還能力。從上圖可以看到,有超過 90% 的人沒有還款能力。現在查看一下貸款的類型。
temp = df["NAME_CONTRACT_TYPE"].value_counts()
# 畫出餅狀圖
trace = [go.Pie(labels=temp.index, values=temp.values, hole=0.6)]
# 設置圖題
layout = go.Layout(
title='Types of loan',
)
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
在上圖中,Revolving loan 表示周期性貸款,類似於分期貸款。 Cash loans 貸款表示現金貸款。由上圖可知,有超過 90% 的人申請的貸款為現金貸款。接下來我們看這些申請人貸款的目的是什么。
temp1 = df["FLAG_OWN_CAR"].value_counts()
temp2 = df["FLAG_OWN_REALTY"].value_counts()
# 畫出餅狀圖
trace = [go.Pie(labels=temp1.index, values=temp1.values, domain={"x": [0, .48]}, hole=0.6),
go.Pie(labels=temp2.index, values=temp2.values, domain={"x": [0.5, 1]}, hole=0.6)]
# 設置圖中的字體,圖題等
layout = go.Layout(
title='Purpose of loan',
annotations=[{"font": {
"size": 20},
"showarrow": False,
"text": "Own Car",
"x": 0.15,
"y": 0.5},
{"font": {
"size": 20},
"showarrow": False,
"text": "Own Realty",
"x": 0.85,
"y": 0.5}])
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
從上圖可知,有接近 34% 的人貸款的錢要花在車上, 30% 的人要花在物業上。接下來看一下這些申請人的收入來源。
temp = df["NAME_INCOME_TYPE"].value_counts()
# 畫出餅狀圖
trace = [go.Pie(labels=temp.index, values=temp.values, hole=0.4)]
# 設置圖題
layout = go.Layout(
title='Income sources of Applicant',
)
# 畫出圖題
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
從上圖可知,52.1% 的人收入來源於工作,有 23.5% 的人收入來源於商業合作,有 18% 的申請者的收入主要來自於養老金。現在看一下這些申請人的婚姻狀況。
temp = df["NAME_FAMILY_STATUS"].value_counts()
# 畫出餅狀圖
trace = [go.Pie(labels=temp.index, values=temp.values)]
# 設置圖題
layout = go.Layout(
title='Family Status of Applicant',
)
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
從上圖可以看到,有 63.7% 的申請都是已婚的,有 14.7% 為單身或未婚。現在看一下這些申請者的職業。
temp = df["OCCUPATION_TYPE"].value_counts()
# 畫出柱狀圖
trace = [go.Bar(x=temp.index, y=temp.values)]
# 設置圖題
layout = go.Layout(
title='Occupation of Applicant',
)
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
從上圖可以看到,人數最多的職業為工人,其次是銷售員等。現在來看一下申請人的受教育情況。
temp = df["NAME_EDUCATION_TYPE"].value_counts()
# 畫出餅狀圖
trace = [go.Pie(labels=temp.index, values=temp.values, hole=0.5)]
# 設置圖題
layout = go.Layout(
title='Education of Applicant',
)
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
有 71.5% 的人為中等學歷,24% 的人為高等學歷。接下來來看這些申請人的房子類型。
temp = df["NAME_HOUSING_TYPE"].value_counts()
# 畫出餅狀圖
trace = [go.Pie(labels=temp.index, values=temp.values)]
# 設置圖題
layout = go.Layout(
title='Loan Repayed or not',
)
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
從上圖可知,有 88.7% 的申請者有自己的房子或住在公寓,有 4.54% 的人跟父母一起住。
上面主要是通過可視化來觀察數據集中一些基本的信息。現在來分析一下信息與是否有能力償還貸款的關系。
import numpy as np
temp = df["NAME_INCOME_TYPE"].value_counts()
temp_y0 = [] # 沒有償還能力
temp_y1 = [] # 有償還能力
for val in temp.index:
temp_y1.append(np.sum(df["TARGET"][df["NAME_INCOME_TYPE"] == val] == 1))
temp_y0.append(np.sum(df["TARGET"][df["NAME_INCOME_TYPE"] == val] == 0))
temp_y1 = np.array(temp_y1)
temp_y0 = np.array(temp_y0)
# 畫出柱狀圖
trace = [go.Bar(x=temp.index, y=(temp_y1 / temp.sum()) * 100, name='YES'),
go.Bar(x=temp.index, y=(temp_y0 / temp.sum()) * 100, name='NO'),
go.Bar(x=temp.index, y=(temp_y1 / (temp_y0+temp_y1)) * 100, name='RATE'),
]
# 設置圖題,字體等
layout = go.Layout(
title="Income sources of Applicant's in terms of loan is repayed or not in %",
xaxis=dict(title='Income source', tickfont=dict(
size=14, color='rgb(107, 107, 107)')),
yaxis=dict(title='Count in %', titlefont=dict(size=16, color='rgb(107, 107, 107)'),
tickfont=dict(size=14, color='rgb(107, 107, 107)'))
)
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
在上圖中,YES 表示有償還能力,NO 表示無償還能力,RATE 表示在該取值中有償還能力所占的比例,例如,在 Working 中,RATE 的取值越高表示當一個人的收入來源於 Working 時,該人有很大的可能有償還能力。
從上圖可知,無業人員和產假人員的償還能力反而是最高的。當然,這兩個取值的統計數量都較少,不足以說明什么問題。
接下來來看一下婚姻狀況與是否有償還能力的關系。
temp = df["NAME_FAMILY_STATUS"].value_counts()
temp_y0 = [] # 沒有償還能力
temp_y1 = [] # 有償還能力
for val in temp.index:
temp_y1.append(np.sum(df["TARGET"][df["NAME_FAMILY_STATUS"] == val] == 1))
temp_y0.append(np.sum(df["TARGET"][df["NAME_FAMILY_STATUS"] == val] == 0))
temp_y1 = np.array(temp_y1)
temp_y0 = np.array(temp_y0)
# 畫出柱狀圖
trace = [go.Bar(x=temp.index, y=(temp_y1 / temp.sum()) * 100, name='YES'),
go.Bar(x=temp.index, y=(temp_y0 / temp.sum()) * 100, name='NO'),
go.Bar(x=temp.index, y=(temp_y1 / (temp_y0+temp_y1)) * 100, name='RATE')]
# 設置字體、圖題等
layout = go.Layout(
title="Family Status of Applicant's in terms of loan is repayed or not in %",
xaxis=dict(title='Family Status', tickfont=dict(
size=14, color='rgb(107, 107, 107)')),
yaxis=dict(title='Count in %', titlefont=dict(size=16, color='rgb(107, 107, 107)'),
tickfont=dict(size=14, color='rgb(107, 107, 107)')))
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
從上圖可知,償還能力似乎與婚姻狀況無關。現在來看申請者職業與償還能力的關系。
temp = df["OCCUPATION_TYPE"].value_counts()
temp_y0 = [] # 沒有償還能力
temp_y1 = [] # 有償還能力
for val in temp.index:
temp_y1.append(np.sum(df["TARGET"][df["OCCUPATION_TYPE"] == val] == 1))
temp_y0.append(np.sum(df["TARGET"][df["OCCUPATION_TYPE"] == val] == 0))
temp_y1 = np.array(temp_y1)
temp_y0 = np.array(temp_y0)
# 畫出柱狀圖
trace = [go.Bar(x=temp.index, y=(temp_y1 / temp.sum()) * 100, name='YES'),
go.Bar(x=temp.index, y=(temp_y0 / temp.sum()) * 100, name='NO'),
go.Bar(x=temp.index, y=(temp_y1 / (temp_y0+temp_y1)) * 100, name='RATE'),
]
# 設置圖題、字體等
layout = go.Layout(
title="Occupation of Applicant's in terms of loan is repayed or not in %",
width=1000,
xaxis=dict(title='Occupation of Applicant\'s',
tickfont=dict(size=14, color='rgb(107, 107, 107)')),
yaxis=dict(title='Count in %', titlefont=dict(size=16, color='rgb(107, 107, 107)'),
tickfont=dict(size=14, color='rgb(107, 107, 107)'))
)
# 顯示圖形
fig = go.Figure(data=trace, layout=layout)
iplot(fig)
由上圖可知,像管理員、核心員工等這些職業的償還能力都較低,而像工人、駕駛司機等職業要高一點。
當然我們還可以繼續分析其他信息與償還能力的關系,不過方法都是大同小異,因此這里不再列出。下面我們來構建預測模型。
構建預測模型
因為數據集比較復雜,而且涉及到的金融術語比較多,這里不再對數據集進行精細的預處理和特征提取工作。此外,為了方便,我們之間刪除掉存在缺失值的特征列。
df_drop = df.dropna(axis=1)
df_drop.head()
編碼特征
因為數據中存在一列是字符串形式的,現在將其編碼成為數值形式。
from sklearn import preprocessing
# 取出非數值的列
categorical_feats = [
f for f in df_drop.columns if df_drop[f].dtype == 'object'
]
# 對非數值的列進行編碼
for col in categorical_feats:
lb = preprocessing.LabelEncoder()
lb.fit(list(df_drop[col].values.astype('str')))
df_drop[col] = lb.transform(list(df_drop[col].values.astype('str')))
查看編碼結果。
df_drop.head()
划分數據
在上面顯示的數據中,SK_ID_CURR 列為顧客的 ID ,因此要將此列刪除掉。
df_drop1 = df_drop.drop("SK_ID_CURR", axis=1)
提取訓練特征數據和目標值。這里的目標值就是申請者的償還能力,在數據集中為 TARGET 列。
data_X = df_drop1.drop("TARGET", axis=1)
data_y = df_drop1['TARGET']
為了測試預測模型的性能,划分數據集為訓練數據集和測試數據集。因為數據集較大,所以只取了 20% 的數據來作為訓練集。
from sklearn import model_selection
train_x, test_x, train_y, test_y = model_selection.train_test_split(data_X.values,
data_y.values,
test_size=0.8,
random_state=0)
構建預測模型
當我們完成上面所有的操作之后,得到的是一份訓練集和一份測試集。現在就可以構建模型了。由於這里是一個分類任務,所以我們選用隨機森林來完成。
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier() # 構建模型
model.fit(train_x, train_y) # 訓練模型
上面完成了模型的構建和訓練,現在測試一下模型的准確率。
from sklearn import metrics
y_pred = model.predict(test_x) # 預測測試集
metrics.accuracy_score(y_pred, test_y) # 評價預測結果
我們可以使用 sklaern 提供的分類報告方法來得到一個全面的評估。
print(metrics.classification_report(y_pred, test_y))
我們還可以通過模型的結果來得到特征的重要性。
features = data_X.columns.values # 取出數據集中的列名,即特征名
# 得到特征與其重要性
x, y = (list(x) for x in zip(*sorted(zip(model.feature_importances_, features),
reverse=False)))
# 畫出柱狀圖
trace2 = go.Bar(x=x, y=y, marker=dict(color=x, colorscale='Viridis', reversescale=True),
name='Random Forest Feature importance', orientation='h',)
# 設置圖題、字體等
layout = dict(title='Barplot of Feature importances', width=900, height=2000,
yaxis=dict(showgrid=False, showline=False, showticklabels=True,), margin=dict(l=300,))
# 顯示圖形
fig1 = go.Figure(data=[trace2])
fig1['layout'].update(layout)
iplot(fig1, filename='plots')
從上面的結果可以看到,不同的特征具有不同的重要性。
上面我們主要使用邏輯回歸來構建預測模型,當然還有許多中方法,現在嘗試一下其他方法。
from sklearn.tree import DecisionTreeClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import BaggingClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
# 構建 7 種算法
models = [LogisticRegression(solver='lbfgs'), # 邏輯回歸
RandomForestClassifier(n_estimators=100), # 隨機森林
DecisionTreeClassifier(), # 決策樹
MLPClassifier(max_iter=100), # 多層感知機
AdaBoostClassifier(), # 自適應梯度提升
BaggingClassifier(), # 裝袋算法
GradientBoostingClassifier()] # 梯度提升算法
model_name = ['LogisticRegression',
'RandomForestClassifier',
"DecisionTreeClassifier",
'MLPClassifier',
'AdaBoostClassifier',
'BaggingClassifier',
'GradientBoostingClassifier']
acc = [] # 存放各算法的准確率
f1 = [] # 存放各算法的 f1 值
recall = [] # 存放各算法的召回率
for model in models: # 訓練每個算法
model.fit(train_x, train_y)
acc.append(model.score(test_x, test_y))
y_pred = model.predict(test_x)
f1.append(metrics.f1_score(y_pred, test_y))
recall.append(metrics.recall_score(y_pred, test_y))
# 打印每種算法的評估結果
pd.DataFrame({"name": model_name, "acc": acc, "f1": f1, "recall": recall})
從上面的結果可知,除了決策樹分類(DecisionTreeClassifier)和感知機分類(MLPClassifier)之外,大部分算法的准確率均超過了 90% 。
總結
本次主要完成了對貸款數據集的可視化分析,其中主要使用 Plotly 庫來完成,本次的重點也是可視化分析,而特征提取與數據預處理等問題中並未做過多的探討,這主要是由於該數據集提供的數據較多,信息量較多,專業知識較強。如果你有興趣,可以自行在線下完成。