文章目录
注意:此文章的博客所用到的数据集,请点击我
一、数据的操作
1.1数据的读取
train=pd.read_csv("./data/train.csv")#读取训练集
testA=pd.read_csv('./data/testA.csv')#读取测试集
1.2查看训练集数据
print(train.head().T)
print(train.head().T) 的意思是将 train 数据集的前五行以及它们每一列的横向转置打印出来。
其中,head() 函数是 Pandas 库中的一个方法,可以默认输出前五行数据。T 则表示对结果进行转置操作,使得原本的行变为列,列变为行。在这里使用 T 是为了让列名和它们所对应的数据更加紧密地放在一起,方便用户查看和理解数据。
1.2.1查看数据集样本数和特征数
print("训练集样本数和特征数",train.shape)
print("测试集样本数和特征数",testA.shape)
1.2.2查看训练集数据样本分布
print("数据集样本分类均衡情况\n",train.isDefault.value_counts())
“isDefault.value_counts()”是一个Pandas函数,用于统计每个不同的值出现的次数,即统计标签为isDefault的样本中,标签取值为0和1的样本数量分别是多少。输出结果可以用来评估样本类别之间是否均衡,便于后续进行模型选择和评估
1.3将训练集和测试集按行进行合并
data=pd.concat([train,testA],axis=0,ignore_index=True)
print("合并后的数据样本数和特征数:",data.shape)
#了解数据基本信息
data.describe().T
print(data.info())
1.4数据预处理
#查看是否有缺失值的特征及缺失值NAN的个数及占比
for col in data.columns:
total_col=data[col].isin([np.NAN]).sum()
if total_col !=0:
total=data[col].count()
print(col+'特征中NAN值的个数有:',total_col,'占比:%.3f'%(total_col/total))
missing=data.isnull().sum()/len(data)
missing=missing[missing>0]
missing.sort_values(inplace=True)
missing.plot.bar()
#对数据集进行填充,对于出现的NAN变量,可将变量取值众数,赋值给NAN
imp=SimpleImputer(missing_values=np.NAN,strategy="most_frequent")
for col in data.columns:
if col not in ["isDefault"]:
data[col]=imp.fit_transform(np.array(data[col]).reshape(-1,1))
print(data.info())
#对字符型数据进行处理
for col in ["grade","subGrade","employmentLength","issueDate","earliesCreditLine"]:
print("特征%的类别个数是:%d,类别有%s" %(col,data[col].nunique(),data[col].unique()))
对数据集缺失值进行填充处理,若采用LightGBM,则无需进行缺失值得填充,因为LightGBM能够自动处理缺失值的问题
1.4.1 特征处理
#统计特征类别的样本数
data['employmentLength'].value_counts().sort_index()
特征数值化处理
#将类别10+years 换成 10years,便于统一处理
data['employmentLength'].replace(to_replace='10+ years',value='10 years',inplace=True)
##将类别<1 year 换成 0 years,便于统一处理
data['employmentLength'].replace(to_replace='< 1 year',value='0 years',inplace=True)
data['employmentLength'].value_counts().sort_index()
def employmentLength_to_int(s):
if pd.isnull(s):
return s
else:
return np.int8(s.split()[0])
查看数据特征
#随即查看数据特征 earliesCreditLine 的样本
data['earliesCreditLine'].sample(5)
#对具有含有年月的时间特征 earliesCreditLine 只提取年份(字符串后4位),相当于对特征按年份进行分箱处理
data['earliesCreditLine']=data['earliesCreditLine'].apply(lambda s:int(s[-4:]))
data['earliesCreditLine'].sample(5)
#随机查看数据特征 earliesCreditLine的样本
data['issueDate'].sample(5)
#对具有含有年月的时间特征 issueDate 只提取年份(字符串前4位),相当于对特征按年份进行分箱处理
data['issueDate']=data['issueDate'].apply(lambda s: int(s.split('-')[0]))
data['issueDate'].sample(5)
对其他数据进行处理
#对其他类别数据进行处理
cate_features=["grade","subGrade","employmentLength","homeOwnership","verificationStatus","purpose","postCode","regionCode",\
"applicationType","initialListStatus","title","policyCode"]
for col in cate_features:
print(col,"类别个数:",data[col].nunique())
对具有非高维类别数的特征构建虚拟量
data=pd.get_dummies(data,columns=["grade","subGrade","homeOwnership","verificationStatus","purpose","regionCode"],drop_first=False)
data.head()
data["postCode"].sample(5)
for col in ["employmentTitle","postCode","title"]:
data[col+"_cns"]=data.groupby([col])["id"].transform("count")
data[col+"_rank"]=data.groupby([col])["id"].rank(ascending=False).astype(int)
del data[col]
data["employmentTitle_cns"].sample(5)
data["employmentTitle_cns"].nunique(5)
print(data.info())
data.describe()
二、数据集的划分
features=[col for col in data.columns if col not in ["id","issueDate","isDefault","employmentTitle","postCode","title"]]
#根据default是否是空,分开训练集和测试集数据
train=data[data.isDefault.notnull()].reset_index(drop=True)
test=data[data.isDefault.isnull()].reset_index(drop=True)
X_train_old=train[features]
X_test_old=test[features]
y_train_old=train["isDefault"]
#因测试集无标签数据 无法进行模型评价 因此将训练集划分部分数据作为本次模型评价的测试集
train_size=0.8 #设置训练集的比例
X_train,X_test,y_train,y_test=train_test_split(X_train_old,y_train_old,train_size=train_size,random_state=1)
三、模型的训练与选择
model_list=[LogisticRegression(solver='liblinear'),
BernoulliNB(),
DecisionTreeClassifier(criterion="entropy",max_depth=8,class_weight="balanced")]
model_names=["LogisticRegression","Native_Bayes","Decision_Tree"]
for name,model in zip(model_names,model_list):
start_time=time.time()
#根据选择的模型进行训练
model.fit(X_train,y_train)
costtime=time.time()-start_time
print("{}分类模型训练耗时:{:.3f} s".format(name,costtime))
#todo:计算分类评价指标 测试集的准确率accuracy、精确率precision、召回率recall和综合评价指标F1
y_test_predict=model.predict(X_test)
acc_test=accuracy_score(y_test,y_test_predict)
precision_test=precision_score(y_test,y_test_predict)
recall_test=recall_score(y_test,y_test_predict)
f1score_test=f1_score(y_test,y_test_predict)
#计算auc的值
y_test_predict_proba=model.predict_proba(X_test)
false_positive_rate,recall,thresholds=roc_curve(y_test,y_test_predict_proba[:,1])
roc_auc=auc(false_positive_rate,recall)
print("{}分类模型在训练集上的评价结果位:".format(name))
print("准确率为:%.4f\t精确率为:%.4f\t召回率为:%.4f\tF1值为:%.4f\tAUC值为:%.4f"%(acc_test,precision_test,recall_test,f1score_test,roc_auc))
plt.figure()
plt.title("{}的ROC_AUC图".format(name))
plt.plot(false_positive_rate,recall,'r',label="AUC=%0.3f"%roc_auc)
plt.legend(loc='best')
plt.plot([0,1],[0,1],'k--')
plt.xlim([0.0,1.0])
plt.ylim([0.0,1.0])
plt.ylabel("真正例率")
plt.xlabel("假正例率")
plt.show()
四、全部源码展示
#encoding:utf-8
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pandas.core.common import SettingWithCopyWarning
from sklearn.model_selection import train_test_split
from sklearn.impute import SimpleImputer
from sklearn.naive_bayes import GaussianNB, BernoulliNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, roc_curve,auc
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
import math
from sklearn.linear_model import LogisticRegression
# 通过 predict_proba() 方法
import time
import warnings
warnings.filterwarnings('ignore', category=SettingWithCopyWarning)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体为SimHei
plt.rcParams['axes.unicode_minus']=False #解决坐标轴负数的负号显示问题
pd.set_option("display.max_columns",500) # 让所有列都能加载出来
# 1.todo:读取数据
train=pd.read_csv('./data/train.csv')
testA=pd.read_csv('./data/testA.csv')
# 打印数据(数据示例)
print("train.head():\n",train.head())
print("testA.head():\n",testA.head())
# 数据集样本数和特征数
print('训练集的样本数和特征数:',train.shape)
print('测试集的样本数和特征数:',testA.shape)
# 查看训练集数据样本分布
# 如果 isDefault 列中取值为 0 的数有 34000 个,取值为 1 的数有 6000 个,
# 那么运行该代码会返回:0 34000 1 6000
print('数据集样本分类均衡情况:\n',train.isDefault.value_counts())
# 测试集缺少 目标变量 isDefault 按测试集进行合并
data=pd.concat([train,testA],axis=0,ignore_index=True)
print('合并后的样本数和特征数:',data.shape)
# 了解数据的基本信息
print('data.describe().T:\n',data.describe().T)
print(data.info())
# 3.todo:数据预处理
# 查看缺失值个数及占比
for col in data.columns:
total_col = data[col].isin([np.NAN]).sum() # 统计缺失值个数
if total_col != 0:
total = data[col].count() # 统计总个数
print(col+'中NaN值的个数有:',total_col,'\t 占比为:%.3f'% (total_col/total))
# 缺失值填充
imp_most_frequent=SimpleImputer(missing_values=np.NaN, strategy='most_frequent')
for col in data.columns:
if col not in ['isDefault']:
data[col]=imp_most_frequent.fit_transform(np.array(data[col]).reshape(-1,1))
print("填充完后的数据信息:")
print(data.info())
# 3.2 todo:对字符型数据进行处理
# 了解字符型数据的类别数
for col in ['grade','subGrade','employmentLength','issueDate','earliesCreditLine']:
print('特征%s的类别个数是:%d,类别有:%s'%(col,data[col].nunique(),data[col].unique()))
# 3.2.1 todo:对特征'employmentLength'进行处理
# 统计特征的类别的样本数
print('处理前:\n',data['employmentLength'].value_counts().sort_index())
# 对特征进行数值化处理
# 将类别’10 + years‘替换为’10 years‘ 便于统一处理
data['employmentLength'].replace(to_replace='10+ years',value='10 years',inplace=True)
# 将类别’<1 year'替换为‘0 year',便于统一处理
data['employmentLength'].replace('< 1 year','0 years',inplace=True)
print(data['employmentLength'].value_counts().sort_index())
# 定义函数将字符串’years‘去掉
def employmentLength_to_int(s):
if pd.isnull(s):
return s
else:
return np.int8(s.split()[0])
data['employmentLength']=data['employmentLength'].apply(employmentLength_to_int)
print('处理后:\n',data['employmentLength'].value_counts(dropna=False).sort_index())
# 3.2.2 todo:对特征earliesCreditLine进行处理
# 随机查看数据特征earliesCreditLine的样本
print('处理前:\n',data['earliesCreditLine'].sample(5))
# 对具有含有年月的时间特征earliesCreditLine只提取年份,相当于对特征按年份进行分箱处理
data['earliesCreditLine']=data['earliesCreditLine'].apply(lambda s:int(s[-4:]))
print('处理后:\n',data['earliesCreditLine'].sample(5))
# 3.2.3 todo:对特征issueDate进行处理
# 随机查看数据特征issueDate的样本
print('处理前:\n',data['issueDate'].sample(5))
data['issueDate']=data['issueDate'].apply(lambda s: int(s[:4]))
print('处理后:\n',data['issueDate'].sample(5))
# 3.2.4 todo:对其他类别进行处理
cate_features=['grade','subGrade','employmentTitle','homeOwnership','verificationStatus','purpose','postCode','regionCode','applicationType','applicationType','title','policyCode']
for col in cate_features:
print(col,'类别个数',data[col].nunique())
# 对具有非常高维度的类别个数构建虚拟变量
data= pd.get_dummies(data,columns=['grade','subGrade','homeOwnership','verificationStatus','purpose','regionCode'],drop_first=False)
print(data.head())
print(data['postCode'].sample(5))
for col in ['employmentTitle','postCode','title']:
data[col+'_cnts']=data.groupby([col])['id'].transform('count')
data[col+'_cnts']=data.groupby([col])['id'].rank(ascending=False).astype(int)
del data[col]
print(data['employmentTitle_cnts'].sample(5))
#print(data['employmentTitle_rank'].nunique())
print(data.info())
print(data.describe())
print(data.columns)
# 4 todo:数据集的划分
features=[col for col in data .columns if col not in ['id','issueDate','isDefault','employmentTitle','postCode','title']]
# 根据isDefault否为空 分开训练集和测试集
train=data[data.isDefault.notnull()].reset_index(drop=True)
test=data[data.isDefault.isnull()].reset_index(drop=True)
X_train_old=train[features]
X_test_old=test[features]
y_train_old=train['isDefault']
# 因测试集 没有标签数据 无法进行模型评价 因此将训练集划分部分数据作为本次模型的测试集
train_size=0.8 #设置训练集的比例
X_train,X_test,y_train,y_test=train_test_split(X_train_old,y_train_old,train_size=train_size,random_state=1)
# 5.todo:模型的选择和训练
model_list = [LogisticRegression(solver='liblinear'),
BernoulliNB(),
DecisionTreeClassifier(criterion='entropy',max_depth=8,class_weight='balanced')
]
model_name = ['LogisticRegression','Naive_Bayes','Decision_Tree']
for name,model in zip(model_name,model_list):
start_time=time.time()
# 根据选择的模型进行训练
model.fit(X_train, y_train)
costtime=time.time()-start_time
print("{}分类模型训练耗时:{:.3f} s ".format(name,costtime) )
y_test_predict = model.predict(X_test)
# 准确率
acc_test = accuracy_score(y_test,y_test_predict)# 和模型自带的model,score一致
# 精确率
precision_test = precision_score(y_test, y_test_predict)
# 召回率
recall_test = recall_score(y_test, y_test_predict)
# f1值
f1score_test = f1_score(y_test, y_test_predict)
# 计算auc的值
y_test_predict_proba= model.predict_proba(X_test)
false_positive_rate,recall,thresholds=roc_curve(y_test,y_test_predict_proba[:,1])
roc_auc=auc(false_positive_rate,recall)
print('{}分类模型在测试集上的评价结果为:'.format(name))
print('准确率为:%.3f \t 精确率为:%.4f \t 召回率为:%.4f \t F1值为:%.4f \t'
% (acc_test,precision_test, recall_test, f1score_test))
plt.figure()
plt.title('{}的roc_auc图'.format(name))
plt.plot(false_positive_rate, recall, 'r', label='AUC = %0.3f' % roc_auc)
plt.legend(loc='best')
plt.plot([0,1],[0,1],'k--')
plt.xlim([0.0,1.0])
plt.ylim([0.0, 1.0])
plt.ylabel('真正例率(召回率')
plt.xlabel('假正例率')
plt.show()
plt.savefig('./result/ROC_AUC_{}.png')
六、文件说明
# ## 问题
# 根据某信贷平台的客户个人贷款记录数据信息,预测其是否有违约的可能,以此判断是否通过其贷款申请。
#
# ## 实践
# 根据敏捷式实践思路,首先完成一个简单的baseline项目。
#
# ### 实践过程
# - 1、进行简单的数据分析处理
# - 2、利用常见的机器学习模型进行模型训练
# - 3、模型测试及评价
#
# ### 依赖的库要求:
# - Scikit-learn 1.1.2
# - NumPy 1.22.4
# - pandas 1.2.4
# - matplotlib 3.3.4
# - seaborn==0.11.1
# - ...
#
# #安装jupyter lab
# !pip install jupyterlab
#
# #启动jupyter lab
# jupyter lab
# ### 实践代码基本流程
# 
# #### 特征含义如下:
# 客户属性数据
# - employmentTitle: 就业职称
# - employmentLength: 就业年限(年)
# - homeOwnership: 借款人在登记时提供的房屋所有权状况
# - annualIncome: 年收入
# - regionCode: 地区编码
# - dti: 债务收入比
#
# 客户信用行为数据
# - delinquency_2years: 借款人过去2年信用档案中逾期30天以上的违约事件数
# - ficoRangeLow: 借款人在贷款发放时的fico所属的下限范围
# - ficoRangeHigh: 借款人在贷款发放时的fico所属的上限范围
# - openAcc: 借款人信用档案中未结信用额度的数量
# - pubRec: 贬损公共记录的数量
# - pubRecBankruptcies: 公开记录清除的数量
# - revolBal: 信贷周转余额合计
# - revolUtil: 循环额度利用率,或借款人使用的相对于所有可用循环信贷的信贷金额
# - totalAcc: 借款人信用档案中当前的信用额度总数
#
# 客户借贷数据
# - purpose: 借款人在贷款申请时的贷款用途类别
# - postCode: 借款人在贷款申请中提供的邮政编码的前3位数字
# - applicationType: 表明贷款是个人申请还是与两个共同借款人的联合申请
# - earliesCreditLine: 借款人最早报告的信用额度开立的月份
# - title: 借款人提供的贷款名称
#
# 贷款信息数据
# - id:为贷款清单分配的唯一信用证标识
# - loanAmnt:贷款金额
# - term:贷款期限(year)
# - interestRate: 贷款利率
# - installment: 分期付款金额
# - grade: 贷款等级
# - subGrade: 贷款等级之子级
# - policyCode: 公开可用的策略_代码=1新产品不公开可用的策略_代码=2
# - initialListStatus: 贷款的初始列表状态
# - issueDate: 贷款发放的月份
# - verificationStatus: 验证状态
#
# 匿名数据
# - 匿名特征n0-n14:为一些贷款人行为计数特征的处理
#
# 目标变量数据
# - isDefault: 是否进行贷款
#
# print('合并后的数据样本数和特征数:', data.shape)
# 合并后的数据样本数和特征数: (1000000, 47)
#
# # 查看合并后的数据
# print(data[799995:800005].T)
# # 发现测试集中的目标变量‘isDefault’被填充为NAN
#
# # 了解数据基本信息
# print(data.describe().T)
#
#
# print(data.info())
#
# # ### 从数据集基本信息可以看出:
# #
# # 1)部分数据中存在缺失值情况,需进行填充;
# #
# # 2)数据中有字符型数据:grade、subGrade、employmentLength、issueDate、earliesCreditLine,需进行字符型数据转化处理;
# #
# # In[8]:
#
#
# 查看有缺失值的特征及缺失值NAN个数及占比
# print(type(data.columns))
# for col in data.columns:
# print(col)
# total_col = data[col].isin([np.NAN]).sum()
# if total_col != 0:
# total = data[col].count()
# print(col + ' 特征中 NAN值的个数有:', total_col, '占比:%.3f' % (total_col / total))
# #
# missing = data.isnull().sum() / len(data)
# missing = missing[missing > 0]
# missing.sort_values(inplace=True)
# missing.plot.bar()
# # In[9]:
#