Kaggle_Kernel学习_Home Credit Default Risk_EDA部分

在学习了数据分析基本知识后,希望通过实战练习的方式巩固知识,入门数据竞赛,也和大家分享一些kaggle上的入门kernel,整合一下自己所用的资源和学习的过程

Home Credit Default Risk

地址:https://www.kaggle.com/willkoehrsen/start-here-a-gentle-introduction
作者主页:https://www.kaggle.com/willkoehrsen
赛题背景:Home Credit利用各种替代数据(包括电信和交易信息)来预测其客户的还款能力。
资源下载
链接:https://pan.baidu.com/s/1uaMESw1ca_Y9O3YVfrSrEA
提取码:d13i
内容:以原作者内容为主,结合对部分知识点我自己的理解和学习,也写一些自己实际操作中的思考,适合初学者学习。
  1. 当做学习笔记看,学习基本处理技巧和一些常用知识点。
  2. 只看概括的处理步骤,先按照自己的思路处理然后再比对自己的疑惑或者代码的冗余处。

 

相关内容

Kaggle_Kernel学习_Home Credit Default Risk_特征工程_baseline部分

 

 

数据概览

具体可见HomeCredit_columns_description.csv文件
  • application_train / application_test:训练和测试数据,包含Home Credit的每个贷款申请信息。每笔贷款都有自己的一行,并由特征SK_ID_CURR标识。训练集数据附有TARGET,表示0:贷款已偿还或1:贷款未偿还。
  • bureau:有关客户以前来自其他金融机构的信贷的数据。每笔先前信用占一行,application_data中的一个贷款可以具有多个先前的信用。
  • bureau_balance:关于月度数据。每笔先前信用都占一行,并且单个先前信用可以具有多个行,每个月对应一个信用长度。
  • previous_application:以前在家申请贷款的申请在申请数据中有贷款的客户的贷款。application_data中的每个当前贷款可以具有多个先前的贷款。每个先前的应用程序都有一行,并由特征SK_ID_PREV标识。
  • POS_CASH_BALANCE:有关以前销售点或客户现金贷款与Home Credit的月度数据。每行是前一个销售点或现金贷款的一个月,而单个先前的贷款可以有多行。
  • credit_card_balance:有关以前使用Home Credit的信用卡的月度数据。每行是信用卡余额的一个月,单个信用卡可以有多行。
  • installments_payment:Home Credit的先前贷款的付款历史。每次付款或者错过付款都有一行。

 

导入相应模块

# 导入 numpy 和 pandas 作为数据处理的基本工具
import numpy as np
import pandas as pd 

# 预处理分类变量所需要的包
from sklearn.preprocessing import LabelEncoder

# 文件管理
import os

# 抑制编译器警告 
import warnings
warnings.filterwarnings('ignore')

# 作图工具
import matplotlib.pyplot as plt
import seaborn as sns

# 中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.family']='sans-serif'

# 负号乱码
matplotlib.rcParams['axes.unicode_minus'] = False

 

预览和导入

# 文件列表预览(自行调整目录)
print(os.listdir("data/"))
['application_test.csv', 'application_train.csv', 'bureau.csv', 'bureau_balance.csv', 'credit_card_balance.csv', 'HomeCredit_columns_description.csv', 'installments_payments.csv', 'POS_CASH_balance.csv', 'previous_application.csv', 'sample_submission.csv']
# 训练数据集
data_train = pd.read_csv('data/application_train.csv')
data_test = pd.read_csv('data/application_test.csv')
print('Training data shape: ', data_train.shape)
print('Test data shape: ' , data_test.shape)
print(data_train.head())
Training data shape:  (307511, 122)
Testing data shape:   (48744, 121)
# dataframe打印效果不佳

 

EDA

探索性数据分析(EDA)是一个开放式过程,我们计算统计数据并制作数据以查找数据中的趋势,异常,模式或关系。
EDA的目标是了解我们 的数据可以告诉我们什么。它通常以高级概述开始,然后在我们找到有趣的数据区域时缩小到特定区域。这些发现本身可能很有趣,或者它们可以用来告知我们的建模选择,例如帮助我们决定使用哪些功能。

目标情况

data_train['TARGET'].value_counts()
0    282686
1     24825
Name: TARGET, dtype: int64
data_train['TARGET'].astype('int').plot.hist()

在这里插入图片描述

从这些信息中,我们看到这是一个不平衡的问题。 按时偿还的贷款远远多于未偿还的贷款。一旦进入更复杂的机器学习模型,我们可以通过它们在数据中的表示来对类进行加权以反映这种不平衡。

缺失值

# 可视化方法
missing = data_train.isnull().sum()
missing = missing[missing > 0]
missing.sort_values(ascending=False,inplace=True)
missing.plot.bar(figsize=(22,6),cmap='Set2')

在这里插入图片描述

# 表格制作方法
def transform_data(df):	
	# 每一列的缺失值数
    data_missing_sum = df.isnull().sum()
    
  	# 每一列缺失值所占的百分比
    data_missing_per = data_missing_sum / len(df) * 100
    
    # 将两列拼接为单独的dataframe
    data_missing = pd.concat([data_missing_sum,data_missing_per],axis=1)
    
    # 重新命名
    data_missing = data_missing.rename(columns ={0:'Missing Value',1:'% Of Total Value'})
    
    # iloc的位置可以是[:,1] [:,0] 
    # 1. 取非0值制表
    # 2. 用sort_values()方法排序,ascending = False 设置为降序
    # 3. round()调整显示格式方便查看
    data_missing = data_missing[data_missing.iloc[:,1]!=0].sort_values('% Of Total Value', ascending = False ).round(1)
    
    return data_missing
data_mis = transform_data(data_train)
data_mis.head(n=20)
						Missing Value				% Of Total Value
COMMONAREA_MEDI				214865						69.9
COMMONAREA_AVG				214865						69.9
COMMONAREA_MODE				214865						69.9
NONLIVINGAPARTMENTS_MEDI	213514						69.4
NONLIVINGAPARTMENTS_MODE	213514						69.4
NONLIVINGAPARTMENTS_AVG		213514						69.4
FONDKAPREMONT_MODE			210295						68.4
LIVINGAPARTMENTS_MODE		210199						68.4
LIVINGAPARTMENTS_MEDI		210199						68.4
LIVINGAPARTMENTS_AVG		210199						68.4
FLOORSMIN_MODE				208642						67.8
FLOORSMIN_MEDI				208642						67.8
FLOORSMIN_AVG				208642						67.8
YEARS_BUILD_MODE			204488						66.5
YEARS_BUILD_MEDI			204488						66.5
YEARS_BUILD_AVG				204488						66.5
OWN_CAR_AGE					202929						66.0
LANDAREA_AVG				182590						59.4
LANDAREA_MEDI				182590						59.4
LANDAREA_MODE				182590						59.4
当需要构建我们的机器学习模型时,我们将不得不填写这些缺失值(称为插补)。以后的工作中,我们将使用XGBoost等可以处理缺失值而不需要插补的模型。另一个选择是删除具有高百分比缺失值的列,但如果这些对我们的模型有帮助,则无法预知。 因此,我们暂时保留所有列。
 
 

查看特征类型

# 主要是为了确定了字符类型 以便后面编码处理
data_train.dtypes.value_counts()
float64    65
int64      41
object     16  
dtype: int64
# 把训练集中为字符类型的特征分别用apply一个nuique方法 
# 注意要打上 axis = 0 标明维度
data_train.select_dtypes('object').apply(pd.Series.nunique,axis=0)
NAME_CONTRACT_TYPE             2
CODE_GENDER                    3
FLAG_OWN_CAR                   2
FLAG_OWN_REALTY                2
NAME_TYPE_SUITE                7
NAME_INCOME_TYPE               8
NAME_EDUCATION_TYPE            5
NAME_FAMILY_STATUS             6
NAME_HOUSING_TYPE              6
OCCUPATION_TYPE               18
WEEKDAY_APPR_PROCESS_START     7
ORGANIZATION_TYPE             58
FONDKAPREMONT_MODE             4
HOUSETYPE_MODE                 3
WALLSMATERIAL_MODE             7
EMERGENCYSTATE_MODE            2
dtype: int64
unique 方法 和 nunique 的区别
如果 ndarray为 [1,2,3,2,3] 用pd.Series()转化后
unique 结果为 [1,2,3] (不同的属性的名字列表)
nunique 结果为 3 (有多少种不同的属性)

 

字符型编码

这里采取的是常用的两种编码方式,label_encoding 和 one-hot encoding

标签编码的问题在于它为类别提供了任意排序。
分配给每个类别的值是随机的,并不反映该类别的任何固有方面。比如,程序员为4和数据科学家为1,但如果我们再次做同样的过程,标签可能会颠倒或完全不同。整数的实际赋值是任意的。因此,当我们执行标签编码时,模型可能会使用特征的相对值来分配不是我们想要的权重。
 
如果我们只为分类变量(例如男性/女性)提供两个唯一值,那么标签编码就可以了,但对于2个以上的唯一类别,独热编码是安全选项。

标签编码

在这里插入图片描述

独热编码

在这里插入图片描述

# 标签编码
lbcoder = LabelEncoder()
le_count = 0 
for col in data_train:
    if data_train[col].dtype == 'object':
         if len(list(data_train[col].unique())) <= 2:
         		# 训练labelEncoder
                lbcoder.fit(data_train[col])
                
                # 分别进行变换
                data_train[col] = lbcoder.transform(data_train[col])
                data_test[col] = lbcoder.transform(data_test[col])
                
                # 记录次数
                le_count+=1
print('标签编码进行的次数 : ',le_count)
标签编码进行的次数 :  3
# one-hot 编码
data_train = pd.get_dummies(data_train)
data_test = pd.get_dummies(data_test)

编码后调整训练集和测试集

由于在编码时,训练集有些特征的属性值在测试集中从未出现过,导致独热编码处理后有些特征旨在训练集中存在测试集中没有的属性
在这里插入图片描述

# 保存目标列(目标列只在训练集有,会造成丢失)
target = data_train['TARGET']

# join = 'inner' 
# 保留两个数据集中均有的特征
data_train, data_test = data_train.align(data_test,axis=1,join='inner')

# 再次加入
data_train['TARGET'] = target

# 检测情况
print(data_train.shape)
print(data_test.shape)
(307511, 239)
(48744, 239)  # 维度相同

 

 

异常处理(举两个例子)

1.日期
data_train['DAYS_BIRTH'].describe()
count    307511.000000
mean     -16036.995067
std        4363.988632
min      -25229.000000
25%      -19682.000000
50%      -15750.000000
75%      -12413.000000
max       -7489.000000
Name: DAYS_BIRTH, dtype: float64

结合语义简单处理

(data_train['DAYS_BIRTH'] / -365).describe()
count    307511.000000
mean         43.936973
std          11.956133
min          20.517808
25%          34.008219
50%          43.150685
75%          53.923288
max          69.120548
Name: DAYS_BIRTH, dtype: float64

2.雇佣天数

# 同样有异常
data_train['DAYS_EMPLOYED'].describe()
count    307511.000000
mean      63815.045904
std      141275.766519
min      -17912.000000
25%       -2760.000000
50%       -1213.000000
75%        -289.000000
max      365243.000000
Name: DAYS_EMPLOYED, dtype: float64

画直方图进行查看

data_train['DAYS_EMPLOYED'].plot.hist()

在这里插入图片描述

# 由以上结果可知聚集于 365242 和 0 附近
anom = data_train[data_train['DAYS_EMPLOYED'] == 365243]
non_anom = data_train[data_train['DAYS_EMPLOYED'] != 365243]
print('非异常条目的违约率为 %0.2f%% ' % (100 * non_anom['TARGET'].mean()))
print('异常条目的违约率为 %0.2f%% ' % (100 * anom['TARGET'].mean()))
非异常条目的违约率为 8.66% 
异常条目的违约率为 5.40% 

处理方式

# 创建类型为布尔值的一列
data_train['DAYS_EMPLOYED_ANOM'] = data_train['DAYS_EMPLOYED'] == 365243
data_test['DAYS_EMPLOYED_ANOM'] = data_test["DAYS_EMPLOYED"] == 365243

# 替换原来的异常值
data_train['DAYS_EMPLOYED'].replace({365243:np.nan},inplace = True)
data_test["DAYS_EMPLOYED"].replace({365243: np.nan}, inplace = True)

# 再次画图
data_train['DAYS_EMPLOYED'].plot.hist(title='DAYS E MPLOYMENT')

# 打印结果
print('有 %d 条异常记录在测试集的 %d  条记录中' % (data_test["DAYS_EMPLOYED_ANOM"].sum(), len(data_test)))

在这里插入图片描述

9274 条异常记录在测试集的 48744 条记录中

 

 

相关性

尝试理解数据的一种方法是查找要素与目标之间的相关性。

我们可以使用.corr()方法计算每个变量和目标之间的Pearson相关系数。相关系数不是表示特征“相关性”的最佳方法,但它确实让我们了解数据中的可能关系。

相关系数绝对值的一般解释是

在这里插入图片描述

# 从相关系数矩阵中提取与 TARGET 相关的系数 并进行排序
correlations = data_train.corr()['TARGET']
correlations = correlations.sort_values()
print('相关系数最低的特征:\n\n',correlations.head(n=10))
print('相关系数最高的特征:\n\n',correlations.tail(n=10))
相关系数实际值最低的变量:
EXT_SOURCE_3                           -0.178919
EXT_SOURCE_2                           -0.160472
EXT_SOURCE_1                           -0.155317
NAME_EDUCATION_TYPE_Higher education   -0.056593
CODE_GENDER_F                          -0.054704
NAME_INCOME_TYPE_Pensioner             -0.046209
DAYS_EMPLOYED_ANOM                     -0.045987
ORGANIZATION_TYPE_XNA                  -0.045987
FLOORSMAX_AVG                          -0.044003
FLOORSMAX_MEDI                         -0.043768
Name: TARGET, dtype: float64

相关系数实际值最高的变量:
REG_CITY_NOT_WORK_CITY         0.050994
DAYS_ID_PUBLISH                0.051457
CODE_GENDER_M                  0.054713
DAYS_LAST_PHONE_CHANGE         0.055218
NAME_INCOME_TYPE_Working       0.057481
REGION_RATING_CLIENT           0.058899
REGION_RATING_CLIENT_W_CITY    0.060893
DAYS_EMPLOYED                  0.074958
DAYS_BIRTH                     0.078239
TARGET                         1.000000
Name: TARGET, dtype: float64
从结果可以发现 DAY_BIRTH 特征正相关最强
三个外部数据源特征 EXT_SOURCE_3 EXT_SOURCE_2 EXT_SOURCE_1 负相关最强,根据特点分别对这几个变量做一些可视化的研究。

 

基于年龄的EDA工作
由于此项数据为负数(以借贷天数为基准到出生的天数),因此需要先转换
# 寻找出生天数和 TARGET 之间的相关性
data_train['DAYS_BIRTH'] = abs(data_train['DAYS_BIRTH'])
data_train['DAYS_BIRTH'].corr(data_train['TARGET'])
所以初步可得出年龄越大越倾向于按时还款

 

简单的了解年龄分布情况
# 画出借贷者年龄分布情况
plt.hist(data_train['DAYS_BIRTH'] / 365, edgecolor = 'w', bins = 25,color='pink')
plt.title('客户年龄分布'); plt.xlabel('年龄'); plt.ylabel('人数');

在这里插入图片描述

sns.set()
plt.figure(figsize=(10,6))

# 用 kdeplot 绘制 核密度估计图
sns.kdeplot(data_train.loc[data_train['TARGET']==0,'DAYS_BIRTH'] / 365, label = 'Fully Paid')
sns.kdeplot(data_train.loc[data_train['TARGET']==1,'DAYS_BIRTH'] / 365, label = 'Charged off')

# 由于中文改动 sns 默认风格 此处标签用英文表示
plt.xlabel('Age (years)');
plt.ylabel('Density'); 
plt.title('Distribution of Ages');

在这里插入图片描述

将年龄信息分区 / 制表查看

age_data = data_train[['TARGET','DAYS_BIRTH']]
age_data['YEARS_BIRTH'] = age_data['DAYS_BIRTH'] / 365 

# 分为20-70的10个区间
age_data['YEARS_BINNED'] = pd.cut(age_data['YEARS_BIRTH'],bins=np.linspace(20,70,11))
# 查看数据情况
age_data.head(10)
	TARGET	DAYS_BIRTH	YEARS_BIRTH	YEARS_BINNED
0		1		9461	25.920548	(25.0, 30.0]
1		0		16765	45.931507	(45.0, 50.0]
2		0		19046	52.180822	(50.0, 55.0]
3		0		19005	52.068493	(50.0, 55.0]
4		0		19932	54.608219	(50.0, 55.0]
5		0		16941	46.413699	(45.0, 50.0]
6		0		13778	37.747945	(35.0, 40.0]
7		0		18850	51.643836	(50.0, 55.0]
8		0		20099	55.065753	(55.0, 60.0]
9		0		14469	39.641096	(35.0, 40.0]

数据情况

# 利用groupby的统计功能查看每个年龄阶段在还款上的逾期率表现
age_groups = age_data.groupby('YEARS_BINNED').mean()
age_groups
YEARS_BINNED	TARGET		DAYS_BIRTH	   YEARS_BIRTH		
(20.0, 25.0]	0.123036	8532.795625		23.377522
(25.0, 30.0]	0.111436	10155.219250	27.822518
(30.0, 35.0]	0.102814	11854.848377	32.479037
(35.0, 40.0]	0.089414	13707.908253	37.555913
(40.0, 45.0]	0.078491	15497.661233	42.459346
(45.0, 50.0]	0.074171	17323.900441	47.462741
(50.0, 55.0]	0.066968	19196.494791	52.593136
(55.0, 60.0]	0.055314	20984.262742	57.491131
(60.0, 65.0]	0.052737	22780.547460	62.412459
(65.0, 70.0]	0.037270	24292.614340	66.555108

 

plt.figure(figsize = (10, 6))

# 制作柱状图(TARGET * 100 算逾期概率) 
plt.bar(age_groups.index.astype(str), 100 * age_groups['TARGET'])

# 标签
plt.xticks(rotation = 65); plt.xlabel('Age Group (years)'); plt.ylabel('Failure to Repay (%)')
plt.title('Failure to Repay by Age Group');

#   

 

基于对外部数据源特征的EDA工作
# 提取目标特征 计算相关系数
ext_data = data_train[['TARGET', 'EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']]
ext_data_corrs = ext_data.corr()
ext_data_corrs

				TARGET		EXT_SOURCE_1   EXT_SOURCE_2  EXT_SOURCE_3  DAYS_BIRTH
TARGET			1.000000	-0.155317		-0.160472	-0.178919	  -0.078239
EXT_SOURCE_1	-0.155317	1.000000		0.213982	0.186846	  0.600610
EXT_SOURCE_2	-0.160472	0.213982		1.000000	0.109167	  0.091996
EXT_SOURCE_3	-0.178919	0.186846		0.109167	1.000000	  0.205478
DAYS_BIRTH		-0.078239	0.600610		0.091996	0.205478	  1.000000

热力图绘制

plt.figure(figsize = (8, 6))
# 调色
cmap = sns.diverging_palette(220, 10, as_cmap=True)
# 热力图
sns.heatmap(ext_data_corrs, cmap = cmap, vmin = -0.35, annot=True,vmax = 0.6)
# 标签
plt.title('Correlation Heatmap');
plt.xticks(rotation = 50)

在这里插入图片描述

所有三个EXT_SOURCE特征都与目标具有负相关性,表明随着EXT_SOURCE值的增加,客户更有可能偿还贷款。 还可以看到DAYS_BIRTH与EXT_SOURCE_1正相关,表明该分数中的一个因素可能是客户年龄。
接下来,我们可以查看由目标值着色的每个特征的分布。 这将让我们可视化此变量对目标的影响

 
plt.figure(figsize=(18,4))

# 利用 enumerate 聚合三个特征 并用 subplots 画子图
for i , source in enumerate(['EXT_SOURCE_1','EXT_SOURCE_2','EXT_SOURCE_3']):

    # 用 i 确定位置
    plt.subplot(1,3,i+1)
    
    # 核密度估计图
    sns.kdeplot(data_train.loc[data_train['TARGET'] == 0,source],label = 'Fully Paid')
    sns.kdeplot(data_train.loc[data_train['TARGET'] == 1,source],label = 'Charged off')
    
    #标签
    plt.title('Distribution of %s by Target Value' % source)
    plt.tight_layout(h_pad = 5)

在这里插入图片描述

EXT_SOURCE_3中的分布差异最大。 可以清楚地看到,此功能与申请人偿还贷款的可能性有一定关系。 这种关系不是很强(实际上它们都被认为非常弱),但这些变量对于机器学习模型仍然有用,可以预测申请人是否会按时偿还贷款。
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值