Loan Prediction 数据集(贷款预测)


一、项目介绍

.
Loan Prediction 数据集是保险领域最常引用的一个数据集。利用这个数据集,你可以充分体验到如何处理保险公司的数据,包括会遇到哪些挑战、需要什么策略、哪些变量会影响结果等。这是一个分类问题,数据集包含614行13列个数据。

问题:

预测一个贷款是否能够被批准。

资源:

分析环境:

  • Python3
  • jupyter notebook

二、使用Python pandas库对数据集进行探索性分析

.

导入库和数据集

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

df = pd.read_csv('loan_train.csv')
df.head()

快速数据探索

变量的描述:

  • Loan_ID: 贷款人ID
  • Gender: 性别 (Male, female)
  • ApplicantIncome: 申请人收入
  • Coapplicant Income: 申请收入
  • Credit_History: 信用记录
  • Dependents: 亲属人数
  • Education: 教育程度
  • LoanAmount: 贷款额度
  • Loan_Amount_Term: 贷款时间长
  • Loan_Status: 贷款状态 (Y, N)
  • Married: 婚姻状况(NO,Yes)
  • Property_Area: 所在区域包括:城市地区、半城区和农村地区

描述统计:

df.describe()

根据描述统计数据,我们可以得出一些结论:

  • 我们可以得出一些数据项的缺失值(比如:LoanAmount (614 – 592) 22个缺失值)。
  • 我们还可以看到大约84%的申请人拥有credit_history。Credit_History字段的平均值为0.84(对于有信用记录的人,Credit_History的值为1,否则为0)
  • ApplicantIncome分布似乎符合期望, CoapplicantIncome也一样。

分布分析

现在我们熟悉基本数据特征,让我们研究各种变量的分布。 让我们从数字变量开始 - 即ApplicantIncome和LoanAmount。

首先绘制ApplicantIncome的直方图:

df['ApplicantIncome'].hist(bins=50)

在这里,我们观察到极少数极值。接下来,我们查看箱形图以了解分布。

这证实了存在许多异常值/极值。 这可归因于社会的收入差距。 部分原因可能是我们正在关注具有不同教育水平的人。 让我们通过教育将它们分开:

df.boxplot(column='ApplicantIncome', by = 'Education')

我们可以看到,毕业生和非毕业生的平均收入之间没有实质性的差异。 但是有更多的高收入毕业生,这似乎是异常值。

现在,让我们查看LoanAmount的直方图和boxplot:

df['LoanAmount'].hist(bins=50)
df.boxplot(column='LoanAmount')

同样,有一些极端的价值观。 显然,ApplicantIncome和LoanAmount都需要一些数据量。 LoanAmount缺少并且具有极值,而ApplicantIncome有一些极端值,需要更深入的理解。 我们将在接下来的部分中讨论这个问题。

分类变量分析

现在我们了解了ApplicantIncome和LoanIncome的分布,让我们更详细地了解分类变量。

使用Python生成数据透视表:

temp1 = df['Credit_History'].value_counts(ascending=True)
temp2 = df.pivot_table(values='Loan_Status',index=['Credit_History'],aggfunc=lambda x: x.map({'Y':1,'N':0}).mean())
print ('Frequency Table for Credit History:') 
print (temp1)

print ('\nProbility of getting loan for each Credit History class:')
print (temp2)

结果:

Frequency Table for Credit History:
0.0     89
1.0    475
Name: Credit_History, dtype: int64

Probility of getting loan for each Credit History class:
                Loan_Status
Credit_History             
0.0                0.078652
1.0                0.795789

现在我们可以观察到我们得到了类似于MS Excel的pivot_table。 可以使用“matplotlib”库将其绘制为条形图:

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,4))
ax1 = fig.add_subplot(121)
ax1.set_xlabel('Credit_History')
ax1.set_ylabel('Count of Applicants')
ax1.set_title("Applicants by Credit_History")
temp1.plot(kind='bar')

ax2 = fig.add_subplot(122)
#temp2.plot(kind = 'bar')
plt.bar(temp2.index, temp2['Loan_Status'])
ax2.set_xlabel('Credit_History')
ax2.set_ylabel('Probability of getting loan')
ax2.set_title("Probability of getting loan by credit history")

这表明,如果申请人有有效的信用记录,获得贷款的机会是八倍。 您可以通过Married,Self-Employed,Property_Area等绘制类似的图表。

或者,这两个图也可以通过在堆叠图表中组合来可视化:

1.Credut_History:

temp3 = pd.crosstab(df['Credit_History'], df['Loan_Status'])
temp3.plot(kind='bar', stacked=True, color=['red','blue'], grid=False)

2.Married

Married=pd.crosstab(df['Married'],df['Loan_Status'])
Married.plot(kind="bar", stacked=True)

已结婚客户更容易获得贷款

3.Self-Employed

Self_Employed=pd.crosstab(df['Self_Employed'],df['Loan_Status'])
Self_Employed.plot(kind="bar", stacked=True)

非self_employed更容易获得贷款.

4.Property_Area

Property_Area=pd.crosstab(df['Property_Area'],df['Loan_Status'])
Property_Area.plot(kind="bar", stacked=True)

在半城市区获得批准的贷款要高于农村或城市地区.

如果你还没有意识到,我们刚刚在这里创建了两个基本的分类算法,一个基于信用历史,另一个基于2个分类变量(包括性别).我们刚刚学习了如何使用Pandas在Python中进行探索性分析。接下来让我们进一步探索ApplicantIncome和LoanStatus变量,执行数据修改并创建数据集以应用各种建模技术。


三、缺失值和异常值处理

.

在我们对数据进行探索的过程中,我们发现数据集中存在一些问题,需要在数据为良好模型做好准备之前解决。此练习通常称为“Data Munging”。以下是我们已经知道的问题:

  • 某些变量中缺少值。我们应该根据缺失值的数量和变量的预期重要性来明智地估计这些值。
  • 在查看分布时,我们看到ApplicantIncome和LoanAmount似乎在两端都包含极值。虽然它们可能具有直观意义,但应该得到恰当的对待。

除了数字字段的这些问题之外,我们还应该查看非数字字段,例如Gender,Property_Area,Married,Education和Dependents,看它们是否包含任何有用的信息。

缺失值处理

df.isnull().sum()

结果:

Loan_ID               0
Gender               13
Married               3
Dependents           15
Education             0
Self_Employed        32
ApplicantIncome       0
CoapplicantIncome     0
LoanAmount           22
Loan_Amount_Term     14
Credit_History       50
Property_Area         0
Loan_Status           0
dtype: int64

尽管缺失值的数量不是很多,但是许多变量都有它们,并且应该估算这些值中的每一个并将其添加到数据中。此处填充缺失值的方法:

  • 对于数值变量:使用均值或中位数进行插补。
  • 对于分类变量:使用常见众数进行插补,这里主要使用众数进行插补空值。

如下:

df['Gender'].fillna(df['Gender'].value_counts().idxmax(), inplace=True)
df['Married'].fillna(df['Married'].value_counts().idxmax(), inplace=True)
df['Dependents'].fillna(df['Dependents'].value_counts().idxmax(), inplace=True)
df['Self_Employed'].fillna(df['Self_Employed'].value_counts().idxmax(), inplace=True)
df["LoanAmount"].fillna(df["LoanAmount"].mean(skipna=True), inplace=True)
df['Loan_Amount_Term'].fillna(df['Loan_Amount_Term'].value_counts().idxmax(), inplace=True)
df['Credit_History'].fillna(df['Credit_History'].value_counts().idxmax(), inplace=True)

查看是否存在缺失值:

df.info()

结果:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 614 entries, 0 to 613
Data columns (total 13 columns):
Loan_ID              614 non-null object
Gender               614 non-null object
Married              614 non-null object
Dependents           614 non-null object
Education            614 non-null object
Self_Employed        614 non-null object
ApplicantIncome      614 non-null int64
CoapplicantIncome    614 non-null float64
LoanAmount           614 non-null float64
Loan_Amount_Term     614 non-null float64
Credit_History       614 non-null float64
Property_Area        614 non-null object
Loan_Status          614 non-null object
dtypes: float64(4), int64(1), object(8)
memory usage: 62.4+ KB

可以看到数据集中已填充所有缺失值,没有缺失值存在。

异常值处理

让我们先分析一下LoanAmount。 由于极端值实际上是可能的,即某些人可能因特定需求而申请高价值贷款。 因此,不要将它们视为异常值,而是尝试进行log转换以消除它们的影响:

df['LoanAmount_log'] = np.log(df['LoanAmount'])
df['LoanAmount_log'].hist(bins=20)

现在分布看起来更接近正常,极端值的影响已经显着消退。

来到申请人收入。 一种直觉可能是一些申请人的收入较低但是对申请人的支持很强。 因此,将两种收入合并为总收入并对其进行对数转换可能是一个好主意。

df['TotalIncome'] = df['ApplicantIncome'] + df['CoapplicantIncome']
df['TotalIncome_log'] = np.log(df['TotalIncome'])
df['LoanAmount_log'].hist(bins=20) 

四、建立预测模型

.

因为sklearn要求所有输入都是数字,我们应该通过编码类别将所有分类变量转换为数字。

from sklearn.preprocessing import LabelEncoder
var_mod = ['Gender','Married','Dependents','Education','Self_Employed','Property_Area','Loan_Status']
le = LabelEncoder()
for i in var_mod:
    df[i] = le.fit_transform(df[i])
df.dtypes 

接下来,我们将导入所需的模块。 然后我们将定义一个通用的分类函数,它将模型作为输入并确定准确度和交叉验证分数。

# 导入相关模块
from sklearn.linear_model import LogisticRegression
from sklearn.cross_validation import KFold   #For K-fold cross validation
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier, export_graphviz
from sklearn import metrics

# 用于制作分类模型和访问性能的通用函数:
def classification_model(model, data, predictors, outcome):
  # 训练模型
  model.fit(data[predictors],data[outcome])

  #测试数据
  predictions = model.predict(data[predictors])

  # 打印精度
  accuracy = metrics.accuracy_score(predictions,data[outcome])
  print ("Accuracy : %s" % "{0:.3%}".format(accuracy))

  # 用5倍进行k倍交叉验证
  kf = KFold(data.shape[0], n_folds=5)
  error = []
  for train, test in kf:
    # 过滤测试数据
    train_predictors = (data[predictors].iloc[train,:])

    # 我们用来训练算法的目标。
    train_target = data[outcome].iloc[train]

    # 使用预测变量和目标训练算法。
    model.fit(train_predictors, train_target)

    # 记录每次交叉验证运行的错误
    error.append(model.score(data[predictors].iloc[test,:], data[outcome].iloc[test]))

  print ("Cross-Validation Score : %s" % "{0:.3%}".format(np.mean(error)))

  #Fit the model again so that it can be refered outside the function:
  model.fit(data[predictors],data[outcome]) 

逻辑回归

把所有变量进入模型,但这可能导致过度拟合。我们可以很容易的做一些直观的假设再开始进入模型。 获得贷款的可能性会更高的因素:

  • 申请人有一个好的信用记录
  • 高收入申请人
  • 更高的教育水平
  • 区域在城市和半城市

第一个模型与“Credit_History”

outcome_var = 'Loan_Status'
model = LogisticRegression()
predictor_var = ['Credit_History']
classification_model(model, df,predictor_var,outcome_var)

-> 精度:80.945%交叉验证得分:80.946%

我们可以尝试不同的组合的变量:

predictor_var = ['Credit_History','Education','Married','Self_Employed','Property_Area']
classification_model(model, df,predictor_var,outcome_var)

-> 精度:80.945%交叉验证得分:80.946%

通常我们期望在添加变量时增加准确性。 但这是一个更具挑战性的案例。 准确性和交叉验证得分不会受到不太重要的变量的影响。 Credit_History占主导地位。 我们现在有两个选择:

  • 特征工程:取消新信息并尝试预测这些信息。 我会把这留给你的创造力。
  • 更好的建模技术。

决策树

决策树是另一种方法做一个预测模型。 众所周知,提供比逻辑回归模型精度高。

model = DecisionTreeClassifier()
predictor_var = ['Credit_History','Gender','Married','Education']
classification_model(model, df,predictor_var,outcome_var)

-> 精度:81.930%交叉验证得分:76.656%

在这里,基于分类变量的模型无法产生影响,因为信用历史主导着它们。 让我们尝试一些数值变量:

predictor_var = ['Credit_History','Loan_Amount_Term','LoanAmount_log']
classification_model(model, df,predictor_var,outcome_var)

-> 精度:89.414%交叉验证得分:68.397%

在这里,我们观察到虽然增加变量的准确性上升,但交叉验证错误却下降了。 这是模型过度拟合数据的结果。

随机森林

随机森林是另一个算法解决分类问题。

随机森林的一个优点是我们可以使它适用于所有功能,并返回一个功能重要性矩阵,可用于选择功能。

model = RandomForestClassifier(n_estimators=100)
predictor_var = ['Gender', 'Married', 'Dependents', 'Education',
       'Self_Employed', 'Loan_Amount_Term', 'Credit_History', 'Property_Area',
        'LoanAmount_log','TotalIncome_log']
classification_model(model, df,predictor_var,outcome_var)

-> 精度:100.000%交叉验证得分:78.179%

在这里,我们看到,训练集的精度是100%。这是最终的过度拟合的例子,可以通过两种方式解决:

  • 减少数量的预测
  • 优化模型参数

首先,我们看到功能重要性矩阵,我们将从中获取最重要的功能。

#Create a series with feature importances:
featimp = pd.Series(model.feature_importances_, index=predictor_var).sort_values(ascending=False)
print (featimp)


Credit_History      0.272855
TotalIncome_log     0.263706
LoanAmount_log      0.222033
Dependents          0.051060
Property_Area       0.048117
Loan_Amount_Term    0.045753
Married             0.025963
Education           0.025323
Gender              0.023837
Self_Employed       0.021353
dtype: float64

让我们使用前5个变量来创建模型。 另外,我们将稍微修改随机森林模型的参数:

model = RandomForestClassifier(n_estimators=25, min_samples_split=25, max_depth=7, max_features=1)
predictor_var = ['TotalIncome_log','LoanAmount_log','Credit_History','Dependents','Property_Area']
classification_model(model, df,predictor_var,outcome_var)

-> 精度:82.899%交叉验证得分:80.949%

请注意,虽然准确度降低了,但交叉验证得分正在提高,表明该模型已经很好地推广。 请记住,随机森林模型并不完全可重复。

你会注意到,即使在随机森林上进行了一些基本的参数调整之后,我们的交叉验证精度也只比原始逻辑回归模型略好。 这个练习给了我们一些非常有趣和独特的学习:

  • 使用更复杂的模型并不能保证更好的结果。
  • 在不理解基本概念的情况下,避免将复杂的建模技术用作模型。 这样做会增加过度拟合的倾向,从而使您的模型不易解释
  • 特征工程是成功的关键。 每个人都可以使用Xgboost模型,但真正的艺术和创造力在于增强特性以更好地适应模型。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容