互联网金融借款违约预测

本项目主要关注实现,数据分析、特征工程涉及较少,而且数据量较大,并没有进行多次调参。
另外,由于数据的分类极其不平衡,本项目尝试使用SMOTE增加偏少类的样本数量。

%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
from dateutil.parser import parse
import datetime
import numpy as np
path = ''
lc = pd.read_csv(path + 'LC.csv')
lp = pd.read_csv(path + 'LP.csv')
lc.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 328553 entries, 0 to 328552
Data columns (total 21 columns):
ListingId    328553 non-null int64
借款金额         328553 non-null int64
借款期限         328553 non-null int64
借款利率         328553 non-null float64
借款成功日期       328553 non-null object
初始评级         328553 non-null object
借款类型         328553 non-null object
是否首标         328553 non-null object
年龄           328553 non-null int64
性别           328553 non-null object
手机认证         328553 non-null object
户口认证         328553 non-null object
视频认证         328553 non-null object
学历认证         328553 non-null object
征信认证         328553 non-null object
淘宝认证         328553 non-null object
历史成功借款次数     328553 non-null int64
历史成功借款金额     328553 non-null float64
总待还本金        328553 non-null float64
历史正常还款期数     328553 non-null int64
历史逾期还款期数     328553 non-null int64
dtypes: float64(3), int64(7), object(11)
memory usage: 52.6+ MB
lc.head().T
01234
ListingId126541133291142421149711152141
借款金额180009453270002500020000
借款期限121224126
借款利率1820201816
初始评级CDECC
借款类型其他其他普通其他电商
是否首标
年龄3534413424
性别
手机认证成功认证未成功认证成功认证成功认证成功认证
户口认证未成功认证成功认证未成功认证成功认证成功认证
视频认证成功认证未成功认证未成功认证成功认证成功认证
学历认证未成功认证未成功认证未成功认证未成功认证未成功认证
征信认证未成功认证未成功认证未成功认证未成功认证未成功认证
淘宝认证未成功认证未成功认证未成功认证未成功认证未成功认证
历史成功借款次数1145613
历史成功借款金额4032614500218943619077945
总待还本金8712.737890.6411726.39703.410
历史正常还款期数57132541118
历史逾期还款期数1613114
lp.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3203276 entries, 0 to 3203275
Data columns (total 10 columns):
ListingId     int64
期数            int64
还款状态          int64
应还本金          float64
应还利息          float64
剩余本金          float64
剩余利息          float64
到期日期          object
还款日期          object
recorddate    object
dtypes: float64(4), int64(3), object(3)
memory usage: 244.4+ MB
lp.head()
ListingId期数还款状态应还本金应还利息剩余本金剩余利息到期日期还款日期recorddate
0126541111380.23270.000.00.02015-06-042015-06-042017-02-22
1126541211400.94249.290.00.02015-07-042015-07-042017-02-22
2126541311421.95228.280.00.02015-08-042015-08-042017-02-22
3126541411443.28206.950.00.02015-09-042015-09-042017-02-22
4126541511464.93185.300.00.02015-10-042015-10-042017-02-22
# 提前去掉不需要的列
lc.drop('借款成功日期', axis=1, inplace=True)

合成label

目标是预测借款三个月内是否会逾期30天及以上。这只有在第一期和第二期逾期30天及以上时才会出现。下面开始提取label。

# 第1、2期未还款的提取出来。
u_data = lp.copy()
u12_late = u_data[((u_data['期数'] == 1) | (u_data['期数'] == 2)) \
                  & ((u_data['还款状态'] == 0) | (u_data['还款状态'] == 2) | (u_data['还款状态'] == 4))]

# 将没有还款日期的日期改为'2050-01-01'
u12_late['还款日期'] = u12_late['还款日期'].apply(lambda x : x if x !='\\N' else '2050-01-01')

# 计算逾期天数
u12_late['逾期天数'] = u12_late['还款日期'].apply(parse)-u12_late['到期日期'].apply(parse)

# 提取出违约名单
u_label = u12_late[u12_late['逾期天数'] > datetime.timedelta(30)]
id_tar = u_label.ListingId.unique()
label_id = pd.DataFrame({'ListingId':id_tar,'label':np.ones(id_tar.size)})

# 联接,并删除不再需要的ListingId
all_data = pd.merge(lc,label_id,how='outer',on='ListingId')
all_data.drop('ListingId', axis=1, inplace=True)

# 后面就不需要lp了
del lp, u_data
all_data.head()
借款金额借款期限借款利率初始评级借款类型是否首标年龄性别手机认证户口认证视频认证学历认证征信认证淘宝认证历史成功借款次数历史成功借款金额总待还本金历史正常还款期数历史逾期还款期数label
0180001218.0C其他35成功认证未成功认证成功认证未成功认证未成功认证未成功认证1140326.08712.735716NaN
194531220.0D其他34未成功认证成功认证未成功认证未成功认证未成功认证未成功认证414500.07890.64131NaN
2270002420.0E普通41成功认证未成功认证未成功认证未成功认证未成功认证未成功认证521894.011726.32253NaN
3250001218.0C其他34成功认证成功认证成功认证未成功认证未成功认证未成功认证636190.09703.41411NaN
420000616.0C电商24成功认证成功认证成功认证未成功认证未成功认证未成功认证1377945.00.0011814NaN
# 缺失值分析
check_null = all_data.isnull()\
    .sum(axis=0)\
    .sort_values(ascending=False)/float(len(lc))
check_null
label       0.85136
历史逾期还款期数    0.00000
借款期限        0.00000
借款利率        0.00000
初始评级        0.00000
借款类型        0.00000
是否首标        0.00000
年龄          0.00000
性别          0.00000
手机认证        0.00000
户口认证        0.00000
视频认证        0.00000
学历认证        0.00000
征信认证        0.00000
淘宝认证        0.00000
历史成功借款次数    0.00000
历史成功借款金额    0.00000
总待还本金       0.00000
历史正常还款期数    0.00000
借款金额        0.00000
dtype: float64
# 对label,NaN的填0
all_data['label'].fillna(value=0,inplace=True)
# 数据探索与分析
# 大致观察数据统计指标情况,发现一些需要修正的outlier。另外,label的类型极度不平衡,在后面将采用smote扩展样本。
all_data.describe().T
countmeanstdmin25%50%75%max
借款金额328553.04423.81690611219.664024100.02033.03397.005230.00500000.00
借款期限328553.010.2135942.7804441.06.012.0012.0024.00
借款利率328553.020.6014391.7724086.520.020.0022.0024.00
年龄328553.029.1430426.62428617.024.028.0033.0056.00
历史成功借款次数328553.02.3231592.9223610.00.02.003.00649.00
历史成功借款金额328553.08785.85677135027.3634820.00.05000.0010355.007405926.00
总待还本金328553.03721.6653618626.0612050.00.02542.415446.811172652.87
历史正常还款期数328553.09.94765814.8398990.00.05.0013.002507.00
历史逾期还款期数328553.00.4232501.5956810.00.00.000.0060.00
label328553.00.1486400.3557330.00.00.000.001.00

特征工程

# 分开attributes和label
y = all_data["label"].copy()
all_data = all_data.drop('label', axis=1)
# 特征的合并
# 1.加权利率 = 借款期限 * 借款的利率
# 2.还款期数比 = 历史正常还款期数 / (历史正常还款期数 + 逾期期数)
# 3.未还款比 = 总待还本金/历史成功借款金额
# 4.剩余还款压力 = 总待还本金 / 借款金额
all_data['加权利率'] = all_data['借款期限'] * all_data['借款利率']
all_data['还款期数比'] = all_data['历史正常还款期数'] / (all_data['历史正常还款期数'] + all_data['历史逾期还款期数'])
all_data['未还款比'] = all_data['总待还本金'] / all_data['历史成功借款金额']
all_data['剩余还款压力'] = all_data['总待还本金'] / all_data['借款金额']
# fillna
median = all_data[['还款期数比','未还款比','剩余还款压力']].mean()
all_data.fillna(median, inplace=True)
all_data.head().T
01234
借款金额180009453270002500020000
借款期限121224126
借款利率1820201816
初始评级CDECC
借款类型其他其他普通其他电商
是否首标
年龄3534413424
性别
手机认证成功认证未成功认证成功认证成功认证成功认证
户口认证未成功认证成功认证未成功认证成功认证成功认证
视频认证成功认证未成功认证未成功认证成功认证成功认证
学历认证未成功认证未成功认证未成功认证未成功认证未成功认证
征信认证未成功认证未成功认证未成功认证未成功认证未成功认证
淘宝认证未成功认证未成功认证未成功认证未成功认证未成功认证
历史成功借款次数1145613
历史成功借款金额4032614500218943619077945
总待还本金8712.737890.6411726.39703.410
历史正常还款期数57132541118
历史逾期还款期数1613114
加权利率21624048021696
还款期数比0.7808220.9285710.8928570.976190.893939
未还款比0.2160570.5441820.5355950.2681240
剩余还款压力0.4840410.8347230.4343080.3881360
all_data.hist(bins=100, figsize=(20,15))
plt.show()

1523511-20190119223918695-877020783.png

SMOTENC

# 首先numerise cat数据
# 划分每一列类型
row_ordial_attribs = ['初始评级']
cat_attribs = ['借款类型', '是否首标', '性别', '手机认证', '户口认证', '视频认证', '学历认证', '征信认证', '淘宝认证']
num_attribs = list(all_data.drop(list(row_ordial_attribs + cat_attribs), axis=1)) 

# ordinarise
# 下面方法自定义mapper
# key = list(np.sort(all_data['初始评级'].unique()))
# mapper = dict(zip(key, range(len(key))))
# all_data['初始评级'].replace(mapper, inplace=True)

# auto方式
for col_name in row_ordial_attribs + cat_attribs:
    if col_name in row_ordial_attribs:
        all_data[col_name] = pd.factorize(all_data[col_name],sort=True)[0]
    else:
        all_data[col_name] = pd.factorize(all_data[col_name])[0]

# one-hot
# cat_attribs = ['借款类型', '是否首标', '性别', '手机认证', '户口认证', '视频认证', '学历认证', '征信认证', '淘宝认证']
# all_data = pd.get_dummies(all_data, columns=cat_attribs)
# SMOTE抽样。由于label极度不平衡。
from imblearn.over_sampling import SMOTENC

cols = list(all_data.columns)
cat_index = []
for col in row_ordial_attribs + cat_attribs:
    cat_index.append(cols.index(col))
    
sm = SMOTENC(sampling_strategy=0.2, categorical_features=cat_index ,k_neighbors=5,n_jobs=4)
X_res, y_res = sm.fit_sample(all_data, y) # 返回ndarray

sm = SMOTENC(categorical_features=[1],k_neighbors=2)

# 合成回df
X_col = all_data.columns
X_data = pd.DataFrame(X_res, columns=X_col)
y_data = pd.DataFrame(y_res, columns=['label'])
resample_data = pd.concat([X_data, y_data], axis=1)
# 特征的离散化
# 借款金额,借款期限,借款利率,年龄

def money_2_cat(money):
    if money <= 10000:
        return money//3000
    elif money <= 100000:
        return money//10000 + 3
    elif money <= 1000000:
        return money//100000 + 13
    else:
        return 23


def due_2_cat(day):
    if day <= 6:
        return 0
    elif day <= 12:
        return 1
    else:
        return 2


def rate_2_cat(rate):
    if rate <= 13:
        return 0
    elif rate <= 17:
        return 1
    elif rate <= 21:
        return 2
    else:
        return 3


def age_2_cat(age):
    if age <= 22:
        return 0
    elif age <= 25:
        return 1
    elif age <= 30:
        return 2
    elif age < 40:
        return 3
    else:
        return 4


resample_data['借款金额'] = resample_data['借款金额'].apply(money_2_cat)
resample_data['借款期限'] = resample_data['借款期限'].apply(due_2_cat)
resample_data['借款利率'] = resample_data['借款利率'].apply(rate_2_cat)
resample_data['年龄'] = resample_data['年龄'].apply(age_2_cat)
final_data = resample_data.sample(frac=1)
final_data.to_csv(path+'final_data.csv', index=None, encoding='utf-8')
final_data.head().T
203329997988800311869289215
借款金额1.0000000.0000000.0000001.0000000.000000
借款期限0.0000001.0000001.0000001.0000000.000000
借款利率1.0000003.0000003.0000003.0000002.000000
初始评级1.0000003.0000003.0000002.0000002.000000
借款类型1.0000003.0000001.0000001.0000003.000000
是否首标0.0000000.0000000.0000000.0000000.000000
年龄3.0000003.0000000.0000001.0000001.000000
性别1.0000000.0000000.0000000.0000000.000000
手机认证0.0000001.0000001.0000000.0000001.000000
户口认证0.0000000.0000000.0000000.0000000.000000
视频认证1.0000001.0000000.0000001.0000001.000000
学历认证0.0000000.0000000.0000000.0000001.000000
征信认证0.0000000.0000000.0000000.0000000.000000
淘宝认证0.0000000.0000000.0000000.0000000.000000
历史成功借款次数1.0000003.0000002.0000005.0000003.000000
历史成功借款金额7500.0000005975.0000003935.00000014544.0000005149.000000
总待还本金5704.6800004340.6400003199.8000004132.5000003298.040000
历史正常还款期数3.0000008.0000005.00000033.00000010.000000
历史逾期还款期数0.0000000.0000000.0000003.0000000.000000
加权利率96.000000264.000000264.000000264.000000120.000000
还款期数比1.0000001.0000001.0000000.9166671.000000
未还款比0.7606240.7264670.8131640.2841380.640520
剩余还款压力1.5032095.4668012.4613850.8265004.704765
label0.0000000.0000000.0000001.0000000.000000
final_data = pd.read_csv(path+'final_data.csv')

模型训练与调优

Y = final_data['label'].copy()
X = final_data.drop('label', axis=1)
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import GradientBoostingClassifier

param_grid = [
    {'n_estimators':[50], 'learning_rate': [0.1], 'min_samples_leaf': [100], 'min_samples_split': [100], 'max_depth':[7]}
  ]

gbc = GradientBoostingClassifier()
grid_search = GridSearchCV(gbc, param_grid, cv=4,
                           scoring='roc_auc', 
                           n_jobs=4,
                           return_train_score=True)
grid_search.fit(X, Y)
# 查看结果
grid_search.best_params_

grid_search.best_estimator_

cvres = grid_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
    print(mean_score, params)
    
pd.DataFrame(grid_search.cv_results_).T
0.7771415886609713 {'learning_rate': 0.1, 'max_depth': 7, 'min_samples_leaf': 100, 'min_samples_split': 100, 'n_estimators': 50}
0
mean_fit_time170.069
std_fit_time1.23085
mean_score_time0.262221
std_score_time0.0795939
param_learning_rate0.1
param_max_depth7
param_min_samples_leaf100
param_min_samples_split100
param_n_estimators50
params{'learning_rate': 0.1, 'max_depth': 7, 'min_sa...
split0_test_score0.776663
split1_test_score0.776003
split2_test_score0.777184
split3_test_score0.778716
mean_test_score0.777142
std_test_score0.0010006
rank_test_score1
split0_train_score0.791537
split1_train_score0.792024
split2_train_score0.791077
split3_train_score0.79236
mean_train_score0.79175
std_train_score0.0004864

转载于:https://www.cnblogs.com/code2one/p/10293531.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值