前一部分我们对一些分类指标等一些预备知识进行了学习,接下来我们要进行的是探索性的数据分析(EDA)。
EDA是我们进行数据挖掘非常重要的一步,做的好的EDA可以让我们对数据作出更准确的分析,一方面是让我们了解整个数据集,包括缺失值,异常值,变量间的练习等,另一方面也是为我们之后的特征工程做好准备。
查看数据集的列数据
train.columns
Index([‘id’, ‘loanAmnt’, ‘term’, ‘interestRate’, ‘installment’,
‘grade’,
‘subGrade’, ‘employmentTitle’, ‘employmentLength’, ‘homeOwnership’,
‘annualIncome’, ‘verificationStatus’, ‘issueDate’, ‘isDefault’,
‘purpose’, ‘postCode’, ‘regionCode’, ‘dti’, ‘delinquency_2years’,
‘ficoRangeLow’, ‘ficoRangeHigh’, ‘openAcc’, ‘pubRec’,
‘pubRecBankruptcies’, ‘revolBal’, ‘revolUtil’, ‘totalAcc’,
‘initialListStatus’, ‘applicationType’, ‘earliesCreditLine’, ‘title’,
‘policyCode’, ‘n0’, ‘n1’, ‘n2’, ‘n2.1’, ‘n4’, ‘n5’, ‘n6’, ‘n7’, ‘n8’,
‘n9’, ‘n10’, ‘n11’, ‘n12’, ‘n13’, ‘n14’],
dtype=‘object’)
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
Column 1 | Column 2 |
---|---|
‘id’ | 贷款清单分配的唯一信用证标识 |
‘loanAmnt’ | 贷款金额 |
#‘term’ | 贷款期限 |
‘interestRate’ | 贷款利率 |
‘installment’ | 分期付款金额 |
‘grade’ | 贷款等级 |
‘subGrade’ | 贷款等级之子级 |
‘employmentTitle’ | 就业职称 |
‘employmentLength’ | 就业年限 |
‘homeOwnership’ | 借款人在登记时提供的房屋所有权状况 |
‘annualIncome’ | 年收入 |
‘verificationStatus’ | 验证状态 |
‘issueDate’ | 贷款发放的月份 |
‘purpose’ | 借款人在贷款申请时的贷款用途类别 |
‘postCode’ | 借款人在贷款申请中提供的邮政编码的前3位数字 |
‘regionCode’ | 地区编码 |
‘dti’ | 债务收入比 |
‘delinquency_2years’ | 借款人过去2年信用档案中逾期30天以上的违约事件数 |
‘ficoRangeLow’ | 借款人在贷款发放时的fico所属的下限范围 |
‘ficoRangeHigh’ | 借款人在贷款发放时的fico所属的上限范围 |
‘openAcc’ | 借款人信用档案中未结信用额度的数量 |
‘pubRec’ | 贬损公共记录的数量 |
‘pubRecBankruptcies’ | 公开记录清除的数量 |
‘revolBal’ | 信贷周转余额合计 |
‘revolUtil’ | 循环额度利用率,或借款人使用的相对于所有可用循环信贷的信贷金额 |
‘totalAcc’ | 借款人信用档案中当前的信用额度总数 |
还可以通过数据分析的基本python语句对数据集进行分析了解
#通过info()来熟悉数据类型
train.info()
#总体粗略的查看数据集各个特征的基本统计情况
train.describe()
查看缺失值
print(f'There are {data_train.isnull().any().sum()} columns in train dataset with missingvalues.')
have_null_fea_dict=(train.isnull().sum()/len(train)).to_dict()
fea_null_moreThanHalf={}
for key,value in have_null_fea_dict.items():
if value >0.5:
fea_null_moreThanHalf[key]=value
fea_null_moreThanHalf
查看具体缺失及缺失率
missing=train.isnull().sum()/len(train)
missing=missing[missing >0]
missing.sort_values(inplace=True)
missing.plot.bar()
了解哪些列存在“nan”,并可以把nan的个数打印,主要的目的在于nan存在个数是否 真的很大,
如果很小一般选择填充,如果使用lgb等树模型可以直接空缺,让树自己去优化;
如果nan存在的过多,可以考虑删除查看训练集测试集中特征属性只有一值的特征
one_value_fea=[col for col in train.columns if train[col].nunique()<=1]
one_value_fea_test=[col for col in test.columns if test[col].nunique()<=1]
one_value_fea
print(f'There are {len(one_value_fea)} columns in train dataset with one unique value.')
print(f'There are {len(one_value_fea_test)} columns in test dataset with one unique value.')
47列数据中有22列都缺少数据,在现实生活中很正常,policycode具有一个唯一值(或全部缺失) 有很多连续变量和一些分类变量。
- 1、特征一般都是由类别型特征和数值型特征组成 。
- 2、类别型特征有时具有非数值关系,有时也具有数值关系。比如‘grade’中的等级A,B,C等,是否只是单纯的 分类,还是A优于其他要结合业务判断。
- 3、数值型特征本是可以直接入模的,但往往风控人员要对其做分箱,转化为WOE编码进而做标准评分卡等操作。 从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
numerical_fea=list(train.select_dtypes(exclude=['object']).columns)
category_fea=list(filter(lambda x: x not in numerical_fea,list(train.columns)))
数值型变量分析,数值型肯定是包括连续型变量和离散型变量的,找出来
划分数值型变量中的连续变量和分类变量
# 过滤数值型类别特征
def get_numerical_serial_fea(data,feas):
numerical_serial_fea=[]
numerical_noserial_fea=[]
for fea in feas:
temp=data[fea].nunique()
if temp <=10:
numerical_noserial_fea.append(fea)
continue
numerical_serial_fea.append(fea)
return numerical_serial_fea,numerical_noserial_fea
numerical_serial_fea,numerical_noserial_fea=get_numerical_serial_fea(train,numerical_fea)
对数据离散量进行分析
离散量如下,将离散量名称名进行替换就可以得出离散量:
'term',
'homeOwnership',
'verificationStatus',
'isDefault',
'initialListStatus',
'applicationType',
'policyCode',
'n11',
'n12'
train['term'].value_counts()
数值连续性变量分析
每个数字特征的分布可视化
f=pd.melt(train,value_vars=numerical_serial_fea)
g=sns.FacetGrid(f,col="variable",col_wrap=2,sharex=False,sharey=False)
g=g.map(sns.distplot,"value")
1、查看某一个数值型变量的分布,查看变量是否符合正态分, 如果不符合正太分布的变量可以log化后再观察下是否符合正态分布。
2. 如果想统一处理一批数据变标准化 必须把这些之前已经正态化的数据提出。
# ploting transaction amount values distribution
plt.figure(figsize=(16,12))
plt.suptitle('Transaction values dictribution',fontsize=22)
plt.subplot(221)
sub_plot_1=sns.distplot(train["loanAmnt"])
sub_plot_1.set_title("loanAmnt Distribuition",fontsize=18)
sub_plot_1.set_xlabel("")
sub_plot_1.set_ylabel("Probability",fontsize=15)
plt.subplot(222)
sub_plot_2=sns.distplot(np.log(train["loanAmnt"]))
sub_plot_2.set_title("loanAmnt(Log) Distribuition",fontsize=18)
sub_plot_2.set_xlabel("")
sub_plot_2.set_ylabel("Probability",fontsize=15)
非数值类别型变量分析,方法同上:
'grade', 'subGrade', 'employmentLength', 'issueDate', 'earliesCreditLine'
train['grade'].value_counts()
plt.figure(figsize=(8,8))
sns.barplot(train["employmentLength"].value_counts(dropna=False)[:20],
train["employmentLength"].value_counts(dropna=False).keys()[:20])
plt.show()
train_loan_fr=train.loc[train["isDefault"]==1]
train_loan_nofr=train.loc[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_fr.groupby("employmentLength")["employmentLength"].count().plot(kind="barh",ax=ax4,title="Count of employmentLength non-fraud")
plt.show()
查看连续型变量在不同y值上的分布
fig,((ax1,ax2))=plt.subplots(1,2,figsize=(15,6))
train.loc[train['isDefault']==1]\
['loanAmnt'].apply(np.log)\
.plot(kind='hist',
bins=100,
title='Log Loan Amt-Fraud',
color='r',
xlim=(-3,10),
ax=ax1)
train.loc[train['isDefault']==0]\
['loanAmnt'].apply(np.log)\
.plot(kind='hist',
bins=100,
title='Log Loan Amt -Not Fraud',
color='b',
xlim=(-3.10),
ax=ax2)
total=len(train)
total_amt=train.groupby(['isDefault'])['loanAmnt'].sum().sum()
plt.figure(figsize=(12,5))
plt.subplot(121)##1代表行,2代表列,所以共有2个图,1代表此时绘制第一个图
plot_tr=sns.countplot(x='isDefault',data=train)
# train'isDefault'这个特征每种类别的数量
plot_tr.set_title('Fraud Loan Distribution \n 0:good user|1:bad user',fontsize=14)
plot_tr.set_xlabel('Is fraud by count',fontsize=16)
plot_tr.set_ylabel('Count',fontsize=16)
for p in plot_tr.patches:
height=p.get_height()
plot_tr.text(p.get_x()+p.get_width()/2.,
height+3,
'{:1.2f}%'.format(height/total*100),
ha="center",fontsize=15)
percent_amt=(train.groupby(['isDefault'])['loanAmnt'].sum())
percent_amt=percent_amt.reset_index()
plt.subplot(122)
plot_tr_2=sns.barplot(x='isDefault',y='loanAmnt',dodge=True,data=percent_amt)
plot_tr_2.set_title("Total Amount in loanAmnt \n 0:good user |1:bad user",fontsize=14)
plot_tr_2.set_xlabel("Is fraud by percent",fontsize=16)
plot_tr_2.set_ylabel("Total Loan Amount Scalar",fontsize=16)
for p in plot_tr_2.patches:
height=p.get_height()
plot_tr_2.text(p.get_x()+p.get_width()/2.,
height+3,
'{:1.2f}%'.format(height/total_amt*100),
ha="center",fontsize=15)
时间格式数据处理及查看
# 转化成时间格式
train['issueDate']=pd.to_datetime(train['issueDate'],format='%Y-%m-%d')
startdate=datetime.datetime.strptime('2007-06-01','%Y-%m-%d')
train['issueDateDT']=train['issueDate'].apply(lambda x: x-startdate).dt.days
# 转化成时间格式
test['issueDate']=pd.to_datetime(test['issueDate'],format='%Y-%m-%d')
startdate=datetime.datetime.strptime('2007-06-01','%Y-%m-%d')
test['issueDateDT']=test['issueDate'].apply(lambda x: x-startdate).dt.days
plt.hist(train['issueDateDT'],label='train');
plt.hist(test['issueDateDT'],label='test');
plt.legend();
plt.title('Distribution of issueDateDT dates');
# train 和issueDateDT日期由重叠,所以使用基于时间的分割进行验证是不明智的