一、项目介绍
.
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模型,但真正的艺术和创造力在于增强特性以更好地适应模型。
网友评论