Kaggle贷款准入数据分析及建模实战
Kaggle贷款准入数据分析及建模实战
这个项目是10月份kaggle的playground series,和上一个项目类似,都是金融领域的交易/贷款预测问题。区别之处在于这次的特征数量较少(58645 rows × 13 columns),每个特征都有其明确的含义。通过这个项目,可以深入了解金融信贷业务场景。由于包含许多类别特征,可以采用适用于类别特征的catboost模型。这一次的笔记除了记录有利于建模和提升指标的步骤,还将记录从数据中分析出的信息,在金融这种需要可解释性的领域,这一点尤为重要。数据分析体现在需要从各种角度画图呈现数据规律。
初步探索
数据集包含六万左右的人数和11个特征:
train_data.isnull().sum()
训练集和测试集都没有缺失值;混杂了类别变量和数值变量,之后需要对类别变量进行编码。
特征含义解读
在playground和原始数据集https://www.kaggle.com/datasets/chilledwanker/loan-approval-prediction/data中都没有看到对于特征含义的描述,于是我们只能根据名字去尽量理解。特征包括:
- 年龄:主要是年轻人在贷款
- 收入
- 房子的所有权:租的,贷款,拥有 ,其它
- 工龄
- 贷款意向:教育,医疗,个人,投资,债务合并,家装
- 贷款等级
- 贷款数量
- 贷款利率
- 贷款占收入比例:主要为10%-20%左右
- cb_person_default_on_file:可能是违约记录之类的
- 信用历史记录
标签为:loan_status。分析表明,0表示可以贷款,1表示拒绝贷款。由于是否贷款取决于对客户违约率的估计,所以后面把标签称作贷款违约率。
异常检测
关系异常
数据集中发现'loan_percent_income'贷款占收入比例特征的值不完全等于“贷款数量 / 个人收入”,因此可以定义一个新特征:
train_data['loan_percent_income']!=(train_data['loan_amnt'] / train_data['person_income'])
残差的折线图如图所示:
在一般情况下,可以理解为数据质量有问题,我们可以直接自己来定义一个新特征,用这个特征拿去计算:
train_data['loan_income_rate']=train_data['loan_amnt'] / train_data['person_income']
有异常值
通过画折线图发现'person_age'和'person_emp_length'有一些很大的值,箱型图可以进一步呈现异常值,例如:
此处可以对100以上的值进行截断,抛弃这些样本数据。由于这里的数据异常是数据生成过程中产生的,测试集里面也有这样的异常,因此为了在测试集上也工作良好,保留这些异常值。经过实测,如果把这些异常值删掉反而预测的性能会下降。
逻辑回归
roc_auc_score:0.65
特征重要性
数值特征
相关性
把类别特征进行独热编码,再加上所有衍生特征,可以得到所有特征的相关性矩阵:
和标签的相关性:和贷款违约率(标签)比较相关的特征有loan_int_rate和loan_percent_income,呈正相关。其它的数值特征相关性较弱。其中两个负值说明高收入个人和一些工作历史较长的人不太可能拖欠贷款。但是奇怪的是,在逻辑回归给出的相关性里面,这两个的相关性都很差。
数值特征之间相关性:其中有两对相关性比较强的特征:个人年龄和个人信用历史长度,贷款数量和贷款收入,把这两对作除法可以生成新特征,不过新特征和标签的相关性并没有更强。例如:
train_data['cred_hist_length_to_age']=train_data['person_age']/train_data['cb_person_cred_hist_length']
特征组合
借鉴别人的笔记,发现还可以通过特征组合来合成不少特征,如下:
- 贷款数量 * 贷款利率=财务负担
- 个人收入/工龄=年薪
- 贷款利率 / 工龄
- 贷款数量 / 信用历史长度
- 贷款利息 / 信用历史长度
- 贷款利息 / 工龄
- 贷款数量 / 工龄
- 还有第一条的反过来也可以用:
- 个人收入 / 贷款数量
X['financial_burden'] = X['loan_amnt'] * X['loan_int_rate']
X['income_per_year_emp'] = X['person_income'] / (X['person_emp_length'])
X['int_to_loan_ratio'] = X['loan_int_rate'] / X['loan_amnt']
X['loan_int_emp_interaction'] = X['loan_int_rate'] * X['person_emp_length']
X['debt_to_credit_ratio'] = X['loan_amnt'] / X['cb_person_cred_hist_length']
X['int_to_cred_hist'] = X['loan_int_rate'] / X['cb_person_cred_hist_length']
X['int_per_year_emp'] = X['loan_int_rate'] / (X['person_emp_length'])
X['loan_amt_per_emp_year'] = X['loan_amnt'] / (X['person_emp_length'])
X['income_to_loan_ratio'] = X['person_income'] / X['loan_amnt']
构建的新特征的过程中,由于除以0会出现特征值是inf的情况,这里把它们replace成nan,再fillna。
train_data[numerical_features].replace([np.inf, -np.inf], np.nan, inplace=True)
train_data[numerical_features].fillna(train_data.mean(), inplace=True)
特征选择
查看新构建的特征的相关性,其中financial_burden 和int_per_year_emp与标签的相关性大于0.2,即财务负担和每雇佣年的利率越大,违约风险越高。留下了这两个特征。有些相关性很弱的特征可以删掉,这样可以加快模型训练速度,还能缓解过拟合。
类别特征
对含有等级关系的“贷款等级”进行标签编码,对其它类别变量进行独热编码。可以画出所有特征的相关性(包括前面新构建的特征):
可以看到类别变量中,贷款等级和贷款违约率也比较相关。可以将两种标签下的贷款等级直方图画到一起:
也可以将两个直方图堆叠起来,看到不同等级下的贷款状态比例:
易得随着贷款等级从A到G逐渐降低,违约的比例(红色)逐渐增加。
对于其它类别特征也可以使用堆叠直条形图来可视化不同分类下的标签比例:
(但是好像除了增加对数据的理解之外没什么帮助…)
基于以上特征,可以使用xgb,lgb,catboost建模和调参,调参的方法和前一篇文章类似,调参后这几个模型均能得到0.95左右的roc_auc。感觉这题提升空间比较有限,最后集成了三个模型,预测值是三个模型给出的值按照他们的auc得分的加权平均,稍微提升了点性能。
total_score = score_xgb + score_lgb + score_cat
weight_xgb = score_xgb / total_score
weight_lgb = score_lgb / total_score
weight_cat = score_cat / total_score
# Convert X_test to DMatrix for XGBoost
dtest = xgb.DMatrix(X_test)
preds_xgb = model_xgb.predict(dtest)
preds_lgb = model_lgb.predict(X_test)
preds_cat = model_cat.predict_proba(X_test)[:, 1]
preds_avg = (preds_xgb * weight_xgb + preds_lgb * weight_lgb + preds_cat * weight_cat)
特征重要性
lgb模型给出的特征重要性为