德国信贷数据建模baseline
本文是基于3大树模型对一份德国信贷数据的建模,主要内容包含:
导入库
导入的库用于数据处理、可视化、建模等
1 | import pandas as pd |
数据简介
数据来自UCI官网:http://archive.ics.uci.edu/ml/datasets/Statlog+(German+Credit+Data)
基本信息:1000条数据 + 20个变量 + 目标变量 + 无缺失值
特征变量的中文与英文含义:
-
特征向量中文:1.支票账户状态;2.借款周期;3.历史信用;4.借款目的;5.信用额度;6.储蓄账户状态;7.当前就业状态;8.分期付款占可支配收入百分比;9.性别与婚姻状态;10.他人担保信息;11.现居住地;12.财产状态;13.年龄;14.其他分期情况;15.房产状态;16.信用卡数量;17.工作状态;18.赡养人数;19.电话号码注册情况;20.是否有海外工作经历
-
特征向量对应英文:1.status_account, 2.duration, 3.credit_history, 4,purpose, 5.amount, 6.svaing_account, 7.present_emp, 8.income_rate, 9.personal_status, 10.other_debtors, 11.residence_info, 12.property, 13.age, 14.inst_plans, 15.housing, 16.num_credits, 17.job, 18.dependents, 19.telephone, 20.foreign_worker
读入数据
下载的数据没有表头,网上搜索到对应英文表头,生成DataFrame:
In [4]:
1 | df.shape |
Out[4]:
1 | (1000, 21) |
In [5]:
1 | df.dtypes |
Out[5]:
1 | checking_account_status object |
In [6]:
1 | # 不同的字段类型统计 |
Out[6]:
1 | object 13 |
In [7]:
1 | df.isnull().sum() |
Out[7]:
1 | checking_account_status 0 |
不同字段下的取值统计
In [8]:
1 | columns = df.columns # 字段 |
Out[8]:
1 | Index(['checking_account_status', 'duration', 'credit_history', 'purpose', |
1、针对字符类型字段的取值情况统计:
1 | string_columns = df.select_dtypes(include="object").columns |
2、针对数值型字段的分布情况:
1 | number_columns = df.select_dtypes(exclude="object").columns.tolist() |
字段处理
支票状态-checking_account_status
中文含义:现有支票帐户的状态
- A11:<0 DM
- A12:0 <= x <200 DM
- A13:> = 200 DM /至少一年的薪水分配
- A14:无支票帐户)
In [11]:
1 | df["checking_account_status"].value_counts() |
Out[11]:
1 | A14 394 |
In [12]:
1 | fig,ax = plt.subplots(figsize=(12,8), dpi=80) |
在这里我们根据每个人的支票账户金额的大小进行硬编码:
In [13]:
1 | # A11:<0 DM,A12:0 <= x <200 DM,A13:> = 200 DM /至少一年的薪水分配,A14:无支票帐户 |
借款周期-duration
中文含义是:持续时间(月)
In [14]:
1 | duration = df["duration"].value_counts() |
Out[14]:
1 | 24 184 |
In [15]:
1 | fig = px.violin(df,y="duration") |
信用卡历史-credit_history
中文含义
- A30:未提取任何信用/已全额偿还所有信用额
- A31:已偿还该银行的所有信用额
- A32:已到期已偿还的现有信用额
- A33:过去的还款延迟
- A34:关键帐户/其他信用额现有(不在此银行)
In [17]:
1 | ch = df["credit_history"].value_counts().reset_index() |
Out[17]:
index | credit_history | |
---|---|---|
0 | A32 | 530 |
1 | A34 | 293 |
2 | A33 | 88 |
3 | A31 | 49 |
4 | A30 | 40 |
In [18]:
1 | fig = px.pie(ch,names="index",values="credit_history") |
1 | # 编码2:独热码 |
借款目的-purpose
借款目的
In [20]:
1 | # 统计每个目的下的人数,根据人数的多少来实施硬编码 |
1 | # 编码3 |
信用额度-credit_amount
表示的是信用额度
In [22]:
1 | px.violin(df["credit_amount"]) |
账户储蓄-savings
账户/债券储蓄(A61:<100 DM,A62:100 <= x <500 DM,A63:500 <= x <1000 DM,A64:> = 1000 DM,A65:未知/无储蓄账户
In [24]:
1 | string_columns |
Out[24]:
1 | Index(['checking_account_status', 'credit_history', 'purpose', 'savings', |
In [25]:
1 | df["savings"].value_counts() |
Out[25]:
1 | A61 603 |
In [26]:
1 | # 编码6:硬编码 |
目前状态-present_employment
- A71:待业
- A72:<1年
- A73:1 <= x <4年
- A74:4 <= x <7年
- A75:…> = 7年
In [28]:
1 | df["present_employment"].value_counts() |
Out[28]:
1 | A73 339 |
In [29]:
1 | # 编码7:独热码 |
In [30]:
1 | df = df.join(df_present_employment) |
个人婚姻状态和性别-personal
个人婚姻状况和性别(A91:男性:离婚/分居,A92:女性:离婚/分居/已婚,A93:男性:单身,A94:男性:已婚/丧偶,A95:女性:单身)
In [31]:
1 | # 编码8:独热码 |
其他担保人-other_debtors
A101:无,A102:共同申请人,A103:担保人
In [32]:
1 | # 编码9:独热码 |
资产-property
In [33]:
1 | # 编码10:独热码 |
住宿-housing
A151:租房,A152:自有,A153:免费
In [34]:
1 | # 编码11:独热码 |
其他投资计划-other_installment_plans
A141:银行,A142:店铺,A143:无
In [35]:
1 | fig,ax = plt.subplots(figsize=(12,8), dpi=80) |
1 | # 编码12:独热码 |
工作-job
- A171 : 非技术人员-非居民
- A172:非技术人员-居民
- A173:技术人员/官员
- A174:管理/个体经营/高度合格的员工/官员
In [37]:
1 | fig,ax = plt.subplots(figsize=(12,8), dpi=80) |
1 | # 编码13:独热码 |
电话-telephone
A191:无,A192:有,登记在客户名下
In [39]:
1 | # 编码14:独热码 |
是否国外工作-foreign_worker
A201: 有,A202: 无
In [40]:
1 | # 编码15:独热码 |
两种类型顾客统计-customer_type
预测类别:1 =良好,2 =不良
In [41]:
1 | fig,ax = plt.subplots(figsize=(12,8), dpi=80) |
打乱数据shuffle
In [42]:
1 | from sklearn.utils import shuffle |
建模
数据分割
In [44]:
1 | # 选取特征 |
In [45]:
1 | # 2-8比例 |
数据标准化
In [46]:
1 | ss = StandardScaler() |
In [47]:
1 | y_train |
Out[47]:
1 | 556 1 |
In [48]:
1 | # 分别求出训练集的均值和标准差 |
将上面求得的均值和标准差用于测试集中:
In [50]:
1 | # 归一化之后的测试集中的特征数据 |
模型1:决策树
In [51]:
1 | dt = DecisionTreeClassifier(max_depth=5) |
Out[51]:
1 | DecisionTreeClassifier(max_depth=5) |
In [52]:
1 | # 预测 |
Out[52]:
1 | array([2, 1, 1, 2, 1]) |
In [53]:
1 | # 混淆矩阵 |
Out[53]:
1 | array([[450, 118], |
In [54]:
1 | # 混淆矩阵可视化 |
1 | ## auc-roc |
模型2:随机森林
In [56]:
1 | rf = RandomForestClassifier() |
Out[56]:
1 | RandomForestClassifier() |
In [57]:
1 | # 预测 |
Out[57]:
1 | array([1, 1, 1, 2, 1]) |
In [58]:
1 | # 混淆矩阵 |
Out[58]:
1 | array([[476, 92], |
In [59]:
1 | # 混淆矩阵可视化 |
1 | ## auc-roc |
模型3:XGboost
In [62]:
1 | from xgboost.sklearn import XGBClassifier |
In [63]:
1 | clf.fit(X_train, y_train) |
Out[63]:
1 | XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1, |
In [65]:
1 | # 先转成数组再传进来 |
Out[65]:
1 | array([1, 1, 1, 2, 1]) |
In [66]:
1 | # 混淆矩阵 |
Out[66]:
1 | array([[445, 123], |
In [67]:
1 | # 混淆矩阵可视化 |
1 | ## auc-roc |
模型优化
基于相关系数进行特征筛选
1 |
|
相关系数的描述统计信息:发现整体的相关系数(绝对值)都比较小
热力图
1 | ax = plt.subplots(figsize=(20,16)) |
根据相关系数筛选前20个变量
1 | k = 20 |
1 | Index(['customer_type', 'duration', 'checking_account_status', 'credit_amount', |
1 | cm = np.corrcoef(data[cols].values.T) |
筛选相关系数绝对值大于0.1的变量
1 | threshold = 0.1 |
新数据建模
1 | # 筛选出为True的特征 |
1 | new_df = df[useful_col] |
数据切分
1 |
|
1 | # 3-7比例 |
标准化
1 | ss = StandardScaler() |
1 | # 分别求出训练集的均值和标准差 |
建模
1 | from xgboost.sklearn import XGBClassifier |
1 | clf.fit(X_train, y_train) |
1 | XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1, |
In [80]:
1 | # 先转成数组再传进来 |
Out[80]:
1 | array([2, 1, 2, 2, 1]) |
In [81]:
1 | # 混淆矩阵 |
Out[81]:
1 | array([[406, 94], |
In [82]:
1 | ## auc-roc |
Out[82]:
1 | 0.666 |
优化方向
经过3种不同树模型的建模,我们发现模型的AUC值并不是很高。AUC 值是一个概率值,AUC 值越大,分类算法越好。可以考虑优化的方向:
- 特征工程处理:这个可以重点优化。目前对原始的特征变量使用了3种不同类型编码、独热码和硬编码;有些字段的编码可能需要优化
- 相关系数筛选变量:相关系数是用来检测两个连续型变量之间线性相关的程度;特征变量和最终因变量的关系不一定线性相关。本文中观察到相关系数都很低,似乎佐证了这点。后续考虑通过其他方法来筛选变量进行建模
- 模型调优:通过网格搜索等优化单个模型的参数,或者通过模型融合来增强整体效果