贷款违约预测项目-数据清洗

贷款违约预测项目复盘:一个二分类项目,通过数据清洗,数据分箱,模型调优,尽可能提高模型的AUC指标,为以后评分卡系统打下基础。

查看数据缺失情况

当我们拿到数据后,第一时间需要查看数据是否有大量的缺失值,数据分布是否均匀,数据中各特征与标签之间的分布是否存在某种联系。在实际操作中,推荐使用图像的方式,使得数据更加直观清晰。
首先查看数据缺失值情况:

# 查看数据缺失值占所有数据的比重
hava_nan_fea_dict = data_train.isnull().sum()/len(data_train)
fea_null_moreThanHalf = {}

for key, value in hava_nan_fea_dict.items():
    if value > 0:
        fea_null_moreThanHalf[key] = value
#使用直方图查看数据的缺失情况
hava_nan_fea_dict = data_train.isnull().sum()/len(data_train)
hava_nan_fea_dict = hava_nan_fea_dict[hava_nan_fea_dict > 0]
hava_nan_fea_dict.sort_values(inplace = True)
hava_nan_fea_dict.plot.bar()

缺失值分布情况
从图片中可以看出,缺失值占比并不高,可以采用手动填补缺失值的方式。当然,在众多以决策树为基础学习器的模型当中,有自己处理缺失值的情况,我们可以分别比较不处理缺失值以及处理缺失值后,模型评估效果哪个会更好。本文中,为更好的实现数据分箱,所以还是进行缺失值的填充。

数据分类与数据分布

在进行缺失值填充之前,我们需要知道数据类型以及他的分布情况。数据类型包括:类别型与数值型,数值型中又分为连续型与离散型。通过判断数据的类型很容易将其区分开来:

# 根据数据类型是否为 ‘object’ 区分数值特征与类别特征
numberical_fea = list(data_train.select_dtypes(exclude=['object']).columns)
category_fea = list(data_train.select_dtypes(include=['object']).columns)

# 将数值型特征区分为联系型与离散型
def get_number_serial_fea(data,feas):
    number_serial_fea = []    # 连续型
    number_noserial_fea = []  # 离散型
    for fea in feas:
        if data[fea].nunique() <= 20:      # 定义当特征的值大于20种,为连续型
            number_noserial_fea.append(fea)
        else: number_serial_fea.append(fea)
    return number_serial_fea, number_noserial_fea

严格的来说,连续型与离散型没有明确的分界线,这里,我们采用特征取值是否大于20为分界线。但是,即使采用上述方法进行区分,也需要通过数据的 value_counts 等方法进行佐证。往往很多特征,虽然有较多的特征值,但相较于以万为单位的训练数据集来说,依然属于离散型特征。
下一步,观察连续型变量的分布方式,查看其分布是否符合正态分布。

# 观察连续特征数据分布情况
number_serial_fea2 = number_serial_fea.copy()

for fea in remove_fea:
    number_serial_fea2.remove(fea)

f = pd.melt(data_train, value_vars=number_serial_fea2)
g = sns.FacetGrid(f, col='variable', col_wrap=2, sharex=False, sharey=False)
g = g.map(sns.distplot, "value")
# 比较数据的分布于log后的分布,查看是否属于正态分布
plt.figure(figsize=(16,20))
plt.suptitle('Transaction Values Distribution', fontsize=22)
plt.subplot(221)
sub_plot1 = sns.distplot(data_train['loanAmnt'])
sub_plot1.set_title('loanAmnt Distribuition', fontsize=18)
sub_plot1.set_xlabel('')
sub_plot1.set_ylabel('Probability',fontsize=15)

plt.subplot(222)
sub_plot2 = sns.distplot(np.log(data_train['loanAmnt']))
sub_plot2.set_title('loanAmnt(Log) Distribuition', fontsize=18)
sub_plot2.set_xlabel('')
sub_plot2.set_ylabel('Probability',fontsize=15)

数据概况分布情况
如果数据分布类似于上图“loanAmnt”特征,其概率分布曲线与对数处理后的概率分布曲线都能呈现出类似于正态分布的形状,那我们可以认为该特征下的数值是符合正态分布的。
然而在实际应用中,大部分的数据都不是正态分布,往往具有高密度的分布,这需要我们去判别,该条件下数据是否还有效。

特征在标签下的联合分布

除了查看特征数据的分布外,根据标签信息将数据集分开,再比较不同标签下,数据的分布情况,也是一种非常有效的分析方式。如在y=0、y=1情况下,数据呈现出不同的分布方式,我们则可认为,该特征蕴含有较多的信息,在进行数据建模时,可特别关注。

# 查看类别型特征在y值上的分布

train_loan_fr = data_train[data_train['isDefault'] == 1]
train_loan_nofr = data_train[data_train['isDefault'] == 0]

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 8))
train_loan_fr.groupby('grade')['grade'].count().plot(kind='barh', ax=ax1, title='Count of grade fraud')
train_loan_nofr.groupby('grade')['grade'].count().plot(kind='barh', ax=ax2, title='Count of grade non-fraud')
train_loan_fr.groupby('employmentLength')['employmentLength'].count().plot(kind='barh', ax=ax3, title='Count of employmentLength fraud')
train_loan_nofr.groupby('employmentLength')['employmentLength'].count().plot(kind='barh', ax=ax4, title='Count of employmentLength non-fraud')
plt.show()

类别特征在Y上的分布
在上图中,可以明显看出,特征“grade”在y=0与y=1的情况下,A,B所占比例明显有区别,说明,grade=A与grade=B对分类有正相关性。

数据EDA可进行的内容还有很多,比如在本例金融借贷项目中,还可查看违约数量占比、违约金额占比、贷款发放的时间占比等等,这里不一一尝试。我们进行上述的目的,是希望在对数据有了一个大体的了解后,能够选择合适的方法来对少量的缺失值进行填充。

缺失值填充

缺失值的填充方式, 通常有:平均值,中位数,众数,缺失值的前一位数(多用于时序序列)等等,根据特殊情况特殊分析。这里我们采用均值、众数的方法进行填充。

from sklearn.impute import SimpleImputer
# 对于特征,employmentTitle, dti, pubRecBankruptcies, revolUtil, title 采用众数进行nan的填充
mode_index = ['employmentTitle', 'dti', 'pubRecBankruptcies', 'revolUtil', 'title']
imp = SimpleImputer(missing_values=np.nan, strategy='most_frequent')
data_train[mode_index] = imp.fit_transform(data_train[mode_index].values)
data_test[mode_index] = imp.fit_transform(data_test[mode_index].values)

# 对于特征postCode, 分布较为均匀,考虑采用平均值
data_train['postCode'] = data_train['postCode'].fillna(data_train['postCode'].median())
data_test['postCode'] = data_test['postCode'].fillna(data_test['postCode'].median())

# 对于缺失值较大的数据,且数据的密度函数有正态分布的趋势,我们采用平均值填充
feature_index = ['n0','n1','n2','n3','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']
data_train[feature_index] = data_train[feature_index].fillna(data_train[feature_index].median())
data_test[feature_index] = data_test[feature_index].fillna(data_test[feature_index].median())

时间类型转换

数据中往往有时间类型数据,如(xx年xx月xx日),对于带有中文的时间类型数据,任何模型都无法直接处理。我们可把时间类型数据转换为:%Y-%m-%d 的格式,或者以数据中的最小日期为基准,计算各日期的时间差,以此带入模型计算。

# 将issueDate转换为时间差
for data in [data_train, data_test]:
    data['issueDate'] = pd.to_datetime(data['issueDate'],format='%Y-%m-%d')
    startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d')
    #构造时间特征
    data['issueDateDT'] = data['issueDate'].apply(lambda x: x-startdate).dt.days
    data.drop('issueDate',axis=1,inplace=True) 
# 将employmentLength转换为数值
def employmentLength_to_int(x):
    if pd.isnull(x): return 0
    else: return np.int8(x.split()[0]) 
    
for data in [data_train, data_test]:
    data['employmentLength'].replace(to_replace='10+ years', value='10 years', inplace=True)
    data['employmentLength'].replace('< 1 year', '0 years', inplace=True)
    data['employmentLength'] = data['employmentLength'].apply(employmentLength_to_int)

对于月份等信息,可采用标签编码或截取月份数值的方法将其转化为数值型。

3σ准则判定异常数据

按照统计学的观点,我们希望数据都能符合正态分布或者拟正态分布的分布方式,为了构造这样的数据,我们采用3σ准则。以数据的3倍标准差为界限,低于平均值加三倍标准差或高于平均值减三倍标准差范围的数据,计为正常数据。在该范围之外的数据定义为异常数据,对数据进行区分。

def find_outliers_by_3segame(data, fea):
    data_std = np.std(data[fea])
    data_mean = np.mean(data[fea])
    outlier_cut_off = data_std * 3
    lower_rule = data_mean - outlier_cut_off
    upper_rule = data_mean + outlier_cut_off
    data[fea+'_outliers'] = data[fea].apply(lambda x:str('异常值') if x > upper_rule or x < lower_rule else str('正常值'))
    return data

data_train = data_train.copy()
for fea in numberical_fea:
    data_train = find_outliers_by_3segame(data_train, fea)

诚如,在3σ准则范围外的数据,可以定义为异常数据,但不能直接定义为无效数据。通过实验我们发现:如果只是简单的删除异常值,模型的AUC指标不升反降,这说明,异常数据往往是我们要判断出的正向数据。但为了更好的拟合模型,减少异常值所带来的误差,我们可以采用数据分箱的方法来处理数据。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值