文章目录
- import pandas as pd import matplotlib.pyplot as plt # 读取数据 train = pd.read_csv('train.csv') testA = pd.read_csv('testA.csv') print('Train data shape:', train.shape) print('testA data shape:', testA.shape) train.head() testA.head()
- """
1.EDA价值主要在于熟悉了解整个数据集的基本情况(缺失值,异常值),对数据集进行验证是否可以进行接
下来的机器学习或者深度学习建模.
2.了解变量间的相互关系、变量与预测值之间的存在关系。
3.为特征工程做准备
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
import warnings
warnings.filterwarnings('ignore')
data_train = pd.read_csv('./train.csv')
data_test_a = pd.read_csv('./testA.csv')
# 通过os.getcwd()查看当前工作目录
import os
os.getcwd()
# 通过nrows参数读取某几行
data_train_sample = pd.read_csv('./train.csv', nrows=5)
data_train_sample
# 通过设置chunksize参数来控制每次迭代数据的大小
chunker = pd.read_csv('./train.csv', chunksize=5)
for item in chunker:print(type(item)) # <class 'pandas.core.frame.DataFrame'>print(len(item)) # 5display(item)break
- data_test_a.shape data_train.shape data_train.columns # 查看数据类型 data_train.info() # 查看各个特征(数值类型)的基本统计量 data_train.describe() # data_train.head(3).append(data_train.tail(3)) # 未来会被弃用 pd.concat([data_train.head(3), data_train.tail(3)]) # 建议使用
- # 查看缺失值 print(f'There are {data_train.isnull().any().sum()} columns in train dataset with missing values.') # 进一步查看缺失特征中缺失率大于50%的特征 have_null_fea_dict = (data_train.isnull().sum()/len(data_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 # 没有 # 具体查看缺失特征及缺失率 # nan可视化 missing = data_train.isnull().sum()/len(data_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 data_train.columns if data_train[col].nunique() <= 1] # number of unique oen_value_fea_test = [col for col in data_test_a.columns if data_test_a[col].nunique() <= 1] print(one_value_fea) print(oen_value_fea_test) 总结:47列数据中有22列都缺少数据,这在现实世界中很正常。‘policyCode’具有一个唯一值(或全部缺失)。有很多连 续变量和一些分类变量。
- 特征一般都是由类别型特征和数值型特征组成 类别型特征有时具有非数值关系,有时也具有数值关系。比如‘grade’中的等级A,B,C等,是否只是单纯的 分类,还是A优于其他要结合业务判断。 数值型特征本是可以直接入模的,但往往风控人员要对其做分箱,转化为WOE编码进而做标准评分卡等操作。从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。 # 数值型特征 numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns) print(numerical_fea) # 类别型特征 category_fea = list(filter(lambda x: x not in numerical_fea, list(data_train.columns))) category_fea data_train.grade # 数值型变量分析: # 1. 划分数值型变量中的连续型变量和和离散型变量 # 过滤数值型类别特征:如果特征下面的不同取值小于10种,则认为是离散型数值变量,否则认为是连续型数值变量 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)continuenumerical_serial_fea.append(fea)return numerical_serial_fea, numerical_noserial_feanumerical_serial_fea, numerical_noserial_fea = get_numerical_serial_fea(data_train,numerical_fea) print(numerical_serial_fea) print(numerical_noserial_fea) # 离散型数值变量分析 data_train['term'].value_counts() data_train['homeOwnership'].value_counts() data_train['isDefault'].value_counts() data_train['initialListStatus'].value_counts() data_train['applicationType'].value_counts() data_train['policyCode'].value_counts() # 无用变量,全部一个值 data_train['n11'].value_counts() # 相差悬殊,不用再分析 data_train['n12'].value_counts() # 相差悬殊,不用再分析 # 连续型数值变量分析 # 每个数值特征的分布可视化 f = pd.melt(data_train, value_vars=numerical_serial_fea) g = sns.FacetGrid(f, col='variable', col_wrap=2, sharex=False, sharey=False) # 结构化多绘图网格,初始化FacetGrid对象 g = g.map(sns.distplot, 'value') # 将sns.distplot应用到每个数据子集 # 参考资料:https://blog.csdn.net/weixin_43618989/article/details/105613021 # https://zhuanlan.zhihu.com/p/484363632 """ 1. 查看某一个数值型变量的分布,查看变量是否符合正态分布,如果不符合正态分布的变量可以log化后再观察下是否符合正态分布。 2. 如果想统一处理一批数据变标准化 必须把这些之前已经正态化的数据提出 """ # 绘制交易数据(loanAmnt)分布 plt.figure(figsize=(16, 12)) # 画布大小,宽为16,高为12 plt.suptitle('Transaction Values Distribution', fontsize=22) # 标题 plt.subplot(221) # 整个画布分为两行两列,当前子图位于第一个位置 sub_plot_1 = sns.distplot(data_train['loanAmnt']) # 该子图上绘制直方图,默认包含核密度曲线 sub_plot_1.set_title('loanAmnt Distribution', 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(data_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) # 类别型特征分析 category_fea data_train['grade'].value_counts() # 默认 data_train['subGrade'].value_counts() data_train['employmentLength'].value_counts() data_train['issueDate'].value_counts() data_train['earliesCreditLine'].value_counts() 总结: 上面我们用value_counts()等函数看了特征属性的分布,但是图表是概括原始信息最便捷的方式。 数无形时少直觉。 同一份数据集,在不同的尺度刻画上显示出来的图形反映的规律是不一样的。python将数据转化成图表,但结论是否正确需要由你保证。
-
- plt.figure(figsize=(8, 8)) sns.barplot(data_train['employmentLength'].value_counts(dropna=False)[:20], # .values可加可不加data_train['employmentLength'].value_counts(dropna=False).keys()[:20]) plt.show()
- # 1.首先查看类别型变量在不同y值上的分布 train_loan_fr = data_train[data_train['isDefault'] == 1] # .loc可加可不加 train_loan_nofr = data_train.loc[data_train['isDefault'] == 0] fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 8)) # 优化:对索引排序 # 欺诈用户的不同等级的数量分布 train_loan_fr.groupby('grade').size().sort_index(ascending=False).plot(kind='barh', ax=ax1, title='Count of grade fraud') # 非欺诈用户的不同等级的数量分布 train_loan_nofr.groupby('grade').size().sort_index(ascending=False).plot(kind='barh', ax=ax2, title='Count of grade non-fraud') # 欺诈用户的就业年限的数量分布 employmentLength_index = train_loan_fr.groupby('employmentLength').size().index b = np.arange(11) b[0], b[1], b[10] = b[10], b[0], b[1] train_loan_fr.groupby('employmentLength').size().reindex(employmentLength_index[b][::-1]).plot(kind='barh', ax=ax3, title='Count of employmentLength fraud') # 非欺诈用户的就业年限的数量分布 train_loan_nofr.groupby('employmentLength').size().reindex(employmentLength_index[b][::-1]).plot(kind='barh', ax=ax4, title='Count of employmentLength non-fraud') plt.show() # 2.查看连续型变量在不同y值上的分布 fig, axes = plt.subplots(1, 2, figsize=(15, 6)) data_train[data_train['isDefault'] == 1]['loanAmnt'].apply(np.log).plot(kind='hist',bins = 100,title = 'Log Loan Amt – Fraud',color = 'r',xlim = (-3, 10),ax = axes[0]) data_train[data_train['isDefault'] == 0]['loanAmnt'].apply(np.log).plot(kind='hist',bins = 100,title = 'Log Loan Amt – Not Fraud',color = 'b',xlim = (-3, 10),ax = axes[1]) """ 图一:绘制欺诈用户和非欺诈用户对应的数量,并在图上标注百分比 """ total = len(data_train) print(total_amt) plt.figure(figsize=(12, 5)) plt.subplot(121) # 下面的图画在第一个坐标轴上 plot_tr = sns.countplot(x='isDefault', data=data_train) # 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,'{:1.2f}%'.format(height/total*100),ha="center", fontsize=15) # ha是horizontal alignment(水平对齐)的含义""" 图二:绘制欺诈用户和非欺诈用户对应的贷款金额总数,并在图上标注百分比 """ total_amt = data_train['loanAmnt'].sum() # 总的loanAmnt percent_amt = (data_train.groupby(['isDefault'])['loanAmnt'].sum()) percent_amt = percent_amt.reset_index() plt.subplot(122) # 下面的图画在第二个坐标轴上 plot_tr_2 = sns.barplot(x='isDefault', y='loanAmnt', 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,'{:1.2f}%'.format(height/total_amt * 100),ha="center", fontsize=15)
- # 训练集:利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列 # issueDate: 贷款发放日期 data_train['issueDate'] = pd.to_datetime(data_train['issueDate'], format='%Y-%m-%d') # 将数据类型转化为datetime类型 startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d') # 设置起始日期,将字符串变成datetime类型 data_train['issueDateDT'] = data_train['issueDate'].apply(lambda x: x-startdate).dt.days # 计算距离起始日期的天数,添加到新的一列中 # 测试集:利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列 data_test_a['issueDate'] = pd.to_datetime(data_train['issueDate'],format='%Y-%m-%d') startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d') data_test_a['issueDateDT'] = data_test_a['issueDate'].apply(lambda x: x-startdate).dt.days plt.hist(data_train['issueDateDT'], label='train') # 根据数据绘制直方图 plt.hist(data_test_a['issueDateDT'], label='train') plt.legend() # 添加图例 plt.title('Distribution of issueDateDT dates'); # train 和 test issueDateDT 日期有重叠 所以使用基于时间的分割进行验证是不明智的
- # 透视图 索引可以有多个,“columns(列)”是可选的,聚合函数aggfunc最后是被应用到了变量“values”中你所列举的项目上。 # 透视图 行索引是'grade'(贷款等级),列索引是"issueDateDT"(贷款发放距离初始日期),值是'loanAmnt'(贷款金额)经过np.sum聚合的结果 pivot = pd.pivot_table(data_train, index=['grade'], columns=['issueDateDT'], values=['loanAmnt'], aggfunc=np.sum) pivot
- import pandas_profiling pfr = pandas_profiling.ProfileReport(data_train) pfr.to_file('./example.html')
- 数据探索性分析是我们初步了解数据,熟悉数据为特征工程做准备的阶段,甚至很多时候EDA阶段提取出来的特征可以直接当作规则来用。可见EDA的重要性,这个阶段的主要工作还是借助于各个简单的统计量来对数据整体的了解,分析各个类型变量相互之间的关系,以及用合适的图形可视化出来直观观察。希望本节内容能给初学者带来帮助,更期待各位学习者对其中的不足提出建议。
- 学习特征预处理、缺失值、异常值处理、数据分桶等特征处理方法 学习特征交互、编码、选择的相应方法 完成相应学习打卡任务,两个选做的作业不做强制性要求,供学有余力同学自己探索
- 数据预处理: a. 缺失值的填充 b. 时间格式处理 c. 对象类型特征转换到数值 异常值处理: a. 基于3sigma原则 b. 基于箱型图 数据分箱 a. 固定宽度分箱 b. 分位数分箱 离散数值型数据分箱 连续数值型数据分箱 c. 卡方分箱(选做作业) 4. 特征交互 a. 特征和特征之间组合 b. 特征和特征之间衍生 c. 其他特征衍生的尝试(选做作业) 5. 特征编码 a. one-hot编码 b. label-encode编码 6. 特征选择 a. 1 Filter b. 2 Wrapper (RFE) c. 3 Embedded
-
- import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import datetime from tqdm import tqdm from sklearn.preprocessing import LabelEncoder # from sklearn.feature_selection import SelectKBest # from sklearn.feature_selection import chi2 # from sklearn.preprocessing import MinMaxScaler # import xgboost as xgb # import lightgbm as lgb # from catboost import CatBOostRegressor # from sklearn.model_selection import StratifiedKFold, KFold # from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss import warnings warnings.filterwarnings('ignore') data_train =pd.read_csv('./train.csv') data_test_a = pd.read_csv('./testA.csv')
- 数据EDA部分我们已经对数据的大概和某些特征分布有了了解,数据预处理部分一般我们要处理一些EDA阶段分析出来的问题,这里介绍了数据缺失值的填充,时间格式特征的转化处理,某些对象类别特征的处理。
在比赛中数据预处理是必不可少的一部分,对于缺失值的填充往往会影响比赛的结果,在比赛中不妨尝试多种填充然后比较结果选择结果最优的一种; 比赛数据相比真实场景的数据相对要“干净”一些,但是还是会有一定的“脏”数据存在,清洗一些异常值往往会获得意想不到的效果。
# 找出数据中的object类型特征和非object类型特征
numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns) # 非object类型特征
category_fea = list(filter(lambda x: x not in numerical_fea,list(data_train.columns))) # object类型特征
# 也可以category_fea = list(data_train.select_dtypes(include=['object']).columns)
label = 'isDefault'
numerical_fea.remove(label) # 剔除label标签
- 把所有缺失值替换为指定的值0 data_train = data_train.fillna(0) 纵向用缺失值上面的值替换缺失值 data_train = data_train.fillna(axis=0,method=‘ffill’) 纵向用缺失值下面的值替换缺失值,且设置最多只填充两个连续的缺失值 data_train = data_train.fillna(axis=0,method=‘bfill’,limit=2) 参考资料:https://www.jb51.net/article/255677.htm # 查看缺失值情况 data_train.isnull().sum() # 按照平均数填充数值型特征 data_train.fillna(data_train[numerical_fea].median(), inplace=True) # 优化 data_test_a.fillna(data_train[numerical_fea].median(), inplace=True) # 按照众数填充类别型特征 data_train.fillna(data_train[category_fea].mode().iloc[0], inplace=True) # 勘误 data_test_a.fillna(data_train[category_fea].mode().iloc[0], inplace=True) data_train.isnull().sum()
- # 利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列 for data in [data_train, data_test_a]: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_train['employmentLength'].value_counts(dropna=False).sort_index() def employmentLength_to_int(s):if pd.isnull(s):return selse:return np.int(s.split()[0])for data in [data_train, data_test_a]: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) data['employmentLength'].value_counts(dropna=False).sort_index() # 对earliesCreditLine(借款人最早报告的信用额度开立的月份)进行预处理 data_train['earliesCreditLine'].sample(5) # 采五个样本 for data in [data_train, data_test_a]:data['earliesCreditLine'] = data['earliesCreditLine'].apply(lambda s: int(s[-4:])) # 截取年份 data_train['earliesCreditLine'].sample(5) # 采五个样本 # cate_features category_fea
- # 部分类别特征 cate_features = ['grade', 'subGrade', \'employmentTitle', 'homeOwnership', 'verificationStatus', 'purpose', 'postCode', 'regionCode', \'applicationType', 'initialListStatus', 'title', 'policyCode'] for f in cate_features:print(f, '类型数:', data[f].nunique()) # 像等级这种类别特征,是有优先级的可以用map映射成数值 for data in [data_train, data_test_a]:data['grade'] = data['grade'].map({'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7}) # 类型数在2之上,又不是高维稀疏的,且纯分类的特征(没有优先级比较),可以用get_dummies转化为one-hot编码 # 参考: https://blog.csdn.net/u010712012/article/details/83002388 # https://zhuanlan.zhihu.com/p/139144355 # 勘误: data_train = pd.get_dummies(data_train, columns=['homeOwnership', 'verificationStatus', 'purpose', 'regionCode']) data_test_a = pd.get_dummies(data_test_a, columns=['homeOwnership', 'verificationStatus', 'purpose', 'regionCode'])
- 当你发现异常值后,一定要先分清是什么原因导致的异常值,然后再考虑如何处理。首先,如果这一异常值并不代表一种规律性的,而是极其偶然的现象,或者说你并不想研究这种偶然的现象,这时可以将其删除。其次,如果异常值存在且代表了一种真实存在的现象,那就不能随便删除。在现有的欺诈场景中很多时候欺诈数据本身相对于正常数据来说就是异常的,我们要把这些异常点纳入,重新拟合模型,研究其规律。能用监督的用监督模型,不能用的还可以考虑用异常检测的算法来做。
注意test的数据不能删。
- 在统计学中,如果一个数据分布近似正态,那么大约 68% 的数据值会在均值的一个标准差范围内,大约 95% 会在两个标准差范围内,大约 99.7% 会在三个标准差范围内。 def find_outliers_by_3sigma(data,fea):data_std = np.std(data[fea])data_mean = np.mean(data[fea])outliers_cut_off = data_std * 3lower_rule = data_mean – outliers_cut_offupper_rule = data_mean + outliers_cut_offdata[fea+'_outliers'] = data[fea].apply(lambda x: '异常值' if x > upper_rule or x < lower_rule else '正常值')return data # data_train = data_train.copy() for item in ['homeOwnership', 'verificationStatus', 'purpose', 'regionCode']:numerical_fea.remove(item) for fea in numerical_fea:data_train = find_outliers_by_3sigma(data_train,fea)print(data_train[fea+'_outliers'].value_counts())print(data_train.groupby(fea+'_outliers')['isDefault'].sum()) # 当前特则为正常值(异常值)时,欺诈用户的数量print('*'*10) # 删除异常值 for fea in numerical_fea:data_train = data_train[data_train[fea+'_outliers']=='正常值'] # 保留正常值对应的行 data_train = data_train.reset_index(drop=True) # 勘误
- 总结一句话:四分位数会将数据分为三个点和四个区间,IQR = Q3 -Q1,下触须=Q1 − 1.5x IQR,上触须=Q3 + 1.5x IQR;
- 特征分箱的目的: a. 从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。 数据分桶的对象: a. 将连续变量离散化 b. 将多状态的离散变量合并成少状态 分箱的原因: a. 数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧氏距离作为相似度函数来 测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间量化即数据 分桶也叫做数据分箱,然后使用量化后的结果。 分箱的优点: a. 处理缺失值:当数据源可能存在缺失值,此时可以把null单独作为一个分箱。 b. 处理异常值:当数据中存在离群点时,可以把其通过分箱离散化处理,从而提高变量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。 c. 业务解释性:我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但实际x与y之间经常存在 着非线性关系,此时可经过WOE变换。 特别要注意一下分箱的基本原则: a. (1)最小分箱占比不低于5% b. (2)箱内不能全部是好客户 c. (3)连续箱单调 """ 1. 固定宽度分箱 当数值横跨多个数量级时,最好按照 10 的幂(或任何常数的幂)来进行分组:0-9、10-99、100-999、1000-9999,等 等。固定宽度分箱非常容易计算,但如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子。 """ # 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是loanAmnt/1000 data['loanAmnt_bin1'] = np.floor_divide(data['loanAmnt'], 1000) # 向下取整 # 通过对数函数映射到指数宽度分箱 data['loanAmnt_bin2'] = np.floor(np.log10(data['loanAmnt'])) """ 2.分位数分箱 """ data['loanAmnt_bin3'] = pd.qcut(data['loanAmnt'], 10, labels=False) # 根据分位数分10箱,且只显示第几箱(不显示箱的范围如(xxx,xxx])) """ 3.卡方分箱及其他分箱方法的尝试(略) """
- 交互特征的构造非常简单,使用起来却代价不菲。如果线性模型中包含有交互特征对,那它的训练时间和评 分时间就会从 O(n) 增加到 O(n2n^2n2),其中 n 是单一特征的数量。 # 获得grade, subGrade和isDefault的均值的映射关系,并用均值创建新的列 for col in ['grade', 'subGrade']:# temp_dict:获得每个col元素对应的isDefault的均值的映射关系# temp_dict = data_train.groupby([col])['isDefault'].agg(['mean']).reset_index().rename(columns={'mean': col + '_target_mean'})# temp_dict.index = temp_dict[col].values# temp_dict = temp_dict[col + '_target_mean'].to_dict()# 上面的代码可优化为temp_dict = data_train.groupby([col])['isDefault'].agg('mean').to_dict()data_train[col + '_target_mean'] = data_train[col].map(temp_dict)data_test_a[col + '_target_mean'] = data_test_a[col].map(temp_dict)# 其他衍生变量 mean 和 std for df in [data_train, data_test_a]:for item in ['n0','n1','n2','n3','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']:df['grade_to_mean_' + item] = df['grade'] / df.groupby([item])['grade'].transform('mean')df['grade_to_std_' + item] = df['grade'] / df.groupby([item])['grade'].transform('std')
-
- #label-encode:subGrade,postCode,title # 高维类别特征需要进行转换 for col in tqdm(['employmentTitle', 'postCode', 'title','subGrade']): # tqdm用于展示进度条le = LabelEncoder()le.fit(list(data_train[col].astype(str).values) + list(data_test_a[col].astype(str).values))data_train[col] = le.transform(list(data_train[col].astype(str).values))data_test_a[col] = le.transform(list(data_test_a[col].astype(str).values)) print('Label Encoding 完成') # 参考资料: # Python数据预处理中的LabelEncoder与OneHotEncoder: https://blog.csdn.net/quintind/article/details/79850455
- 对特征做归一化,去除相关性高的特征 归一化目的是让训练过程更好更快的收敛,避免特征大吃小的问题 去除相关性是增加模型的可解释性,加快预测过程。 # 举例归一化过程 # 伪代码 # for fea in [要归一化的特征列表]: # data[fea] = ((data[fea] – np.min(data[fea])) / (np.max(data[fea]) – np.min(data[fea])))
- 特征选择技术可以精简掉无用的特征,以降低最终模型的复杂性,它的最终目的是得到一个简约模型,在不降低预测准确率或对预测准确率影响不大的情况下提高计算速度。特征选择不是为了减少训练时间(实际上,一些技术会增加总体训练时间),而是为了减少模型评分时间。
特征选择的方法:
1 Filter
a. 方差选择法
b. 相关系数法(pearson 相关系数)
c. 卡方检验
d. 互信息法
2 Wrapper (RFE)
a. 递归特征消除法
3 Embedded
a. 基于惩罚项的特征选择法
b. 基于树模型的特征选择
- 基于特征间的关系进行筛选 ['a', 'b'] + ['c'] """ 方差选择法 1. 方差选择法中,先要计算各个特征的方差,然后根据设定的阈值,选择方差大于阈值的特征 """ from sklearn.feature_selection import VarianceThreshold #其中参数threshold为方差的阈值 VarianceThreshold(threshold=3).fit_transform(train,target_train) """ 相关系数法 1. Pearson 相关系数 皮尔森相关系数是一种最简单的,可以帮助理解特征和响应变量之间关系的方法,该方法衡量的是变量之间的线性相关性。 结果的取值区间为 [-1,1] , -1 表示完全的负相关, +1表示完全的正相关,0 表示没有线性相关。 """ from sklearn.feature_selection import SelectKBest from scipy.stats import pearsonr #选择K个最好的特征,返回选择特征后的数据 #第一个参数为计算评估特征是否好的函数,该函数输入特征矩阵和目标向量, #输出二元组(评分,P值)的数组,数组第i项为第i个特征的评分和P值。在此定义为计算相关系数 #参数k为选择的特征个数SelectKBest(k=5).fit_transform(train,target_train) """ 卡方检验 1. 经典的卡方检验是用于检验自变量对因变量的相关性。 假设自变量有N种取值,因变量有M种取值,考虑自变 量等于i且因变量等于j的样本频数的观察值与期望的差距。 其统计量如下: χ2=Σ(A−T)2T,其中A为实际值, T为理论值 2. (注:卡方只能运用在正定矩阵上,否则会报错Input X must be non-negative) """ from sklearn.feature_selection import SelectKBest from sklearn.feature_selection import chi2 #参数k为选择的特征个数 SelectKBest(chi2, k=5).fit_transform(train,target_train) """ 互信息法 1. 经典的互信息也是评价自变量对因变量的相关性的。 在feature_selection库的SelectKBest类结合最大信息系数 法可以用于选择特征,相关代码如下: """ from sklearn.feature_selection import SelectKBest from minepy import MINE #由于MINE的设计不是函数式的,定义mic方法将其为函数式的, #返回一个二元组,二元组的第2项设置成固定的P值0.5 def mic(x, y):m = MINE()m.compute_score(x, y)return (m.mic(), 0.5) #参数k为选择的特征个数 SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T, k=2).fit_transform(train,target_train)
- """ 1. 递归特征消除法 递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征, 再基于新的特征集进行下一轮训练。 在feature_selection库的RFE类可以用于选择特征,相关代码如下(以逻辑 回归为例): """ from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression #递归特征消除法,返回特征选择后的数据 #参数estimator为基模型 #参数n_features_to_select为选择的特征个数 RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(train,target_train)
- """ 1. 基于惩罚项的特征选择法 使用带惩罚项的基模型,除了筛选出特征外,同时也进行了降维。 在 feature_selection库的SelectFromModel类结合逻辑回归模型可以用于选择特征,相关代码如下: """ from sklearn.feature_selection import SelectFromModel from sklearn.linear_model import LogisticRegression #带L1惩罚项的逻辑回归作为基模型的特征选择 SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(train,target_train) """ 基于树模型的特征选择 树模型中GBDT也可用来作为基模型进行特征选择。 在feature_selection库的 SelectFromModel类结合GBDT模型可以用于选择特征,相关代码如下: """ from sklearn.feature_selection import SelectFromModel from sklearn.ensemble import GradientBoostingClassifier #GBDT作为基模型的特征选择 SelectFromModel(GradientBoostingClassifier()).fit_transform(train,target_train)
- 本数据集中我们删除非入模特征后,并对缺失值填充,然后用计算协方差的方式看一下特征间相关性,然后进行模型训练 # 删除不需要的数据 for data in [data_train, data_test_a]:data.drop(['issueDate','id'], axis=1,inplace=True)# 略
- 特征工程是机器学习,甚至是深度学习中最为重要的一部分,在实际应用中往往也是所花费时间最多的一步。各 种算法书中对特征工程部分的讲解往往少得可怜,因为特征工程和具体的数据结合的太紧密,很难系统地覆盖所 有场景。本章主要是通过一些常用的方法来做介绍,例如缺失值异常值的处理方法详细对任何数据集来说都是适 用的。但对于分箱等操作本章给出了具体的几种思路,需要读者自己探索。在特征工程中比赛和具体的应用还是 有所不同的,在实际的金融风控评分卡制作过程中,由于强调特征的可解释性,特征分箱尤其重要。学有余力同 学可以自行多尝试,希望大家在本节学习中有所收获。
- 学习在金融分控领域常用的机器学习模型 学习机器学习模型的建模过程与调参流程 完成相应学习打卡任务
-
- https://blog.csdn.net/han_xiaoyang/article/details/49123419
- https://blog.csdn.net/c406495762/article/details/76262487
- https://zhuanlan.zhihu.com/p/45145899
- https://blog.csdn.net/wuzhongqiang/article/details/104854890
- https://blog.csdn.net/wuzhongqiang/article/details/105350579
- https://mp.weixin.qq.com/s/xloTLr5NJBgBspMQtxPoFA
- RNN:https://zhuanlan.zhihu.com/p/45289691 LSTM:https://zhuanlan.zhihu.com/p/83496936
- 《机器学习》 https://book.douban.com/subject/26708119/ 《统计学习方法》 https://book.douban.com/subject/10590856/ 《面向机器学习的特征工程》 https://book.douban.com/subject/26826639/ 《信用评分模型技术与应用》https://book.douban.com/subject/1488075/ 《数据化风控》https://book.douban.com/subject/30282558/
-
- import pandas as pd import numpy as np import warnings import os import seaborn as sns import matplotlib.pyplot as plt import warnings warnings.filterwarnings('ignore') """ sns 相关设置 """ # 声明使用 Seaborn 样式 sns.set() # 有五种seaborn的绘图风格,它们分别是:darkgrid, whitegrid, dark, white, ticks。默认的主题是darkgrid。 sns.set_style("whitegrid") # 有四个预置的环境,按大小从小到大排列分别为:paper, notebook, talk, poster。其中,notebook是默认的。 sns.set_context('talk') # 中文字体设置-黑体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决保存图像是负号'-'显示为方块的问题 plt.rcParams['axes.unicode_minus'] = False # 解决Seaborn中文显示问题并调整字体大小 sns.set(font='SimHei')
- # reduce_mem_usage 函数通过调整数据类型,帮助我们减少数据在内存中占用的空间 def reduce_mem_usage(df):start_mem = df.memory_usage().sum()/1024/1024print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))for col in df.columns:col_type = df[col].dtype# 非object类型的对象(数值类型对象)if col_type != object:c_min = df[col].min()c_max = df[col].max()if str(col_type)[:3] == 'int':if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:df[col] = df[col].astype(np.int8)elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:df[col] = df[col].astype(np.int16)elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:df[col] = df[col].astype(np.int32)elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:df[col] = df[col].astype(np.int64)else:if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:df[col] = df[col].astype(np.float16)elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:df[col] = df[col].astype(np.float32)else:df[col] = df[col].astype(np.float64)else:df[col] = df[col].astype('category')end_mem = df.memory_usage().sum()/1024/1024print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))print('Decreased by {:.1f}%'.format(100 * (start_mem – end_mem) / start_mem))return df# 读取数据 train = pd.read_csv('./train.csv') train = reduce_mem_usage(train) test = pd.read_csv('./testA.csv') test = reduce_mem_usage(test)
- Tips1:金融风控的实际项目多涉及到信用评分,因此需要模型特征具有较好的可解释性,所以目前在实际项目 中多还是以逻辑回归作为基础模型。但是在比赛中以得分高低为准,不需要严谨的可解释性,所以大多基于集 成算法进行建模。 Tips2:因为逻辑回归的算法特性,需要提前对异常值、缺失值数据进行处理【参考task3部分】 Tips3:基于树模型的算法特性,异常值、缺失值处理可以跳过,但是对于业务较为了解的同学也可以自己对缺 失异常值进行处理,效果可能会更优于模型处理的结果。 注:以下建模的源数据参考baseline进行了相应的特征工程,对于异常缺失值未进行相应的处理操作。 # 建模之前的预操作 from sklearn.model_selection import KFold # 分离数据集,方便进行交叉验证 X_train = train.drop(['id', 'issueDate', 'isDefault'], axis=1) X_test = test.drop(['id', 'issueDate'], axis=1) y_train = train.loc[:, 'isDefault'] # 使用Lightgbm进行建模 from sklearn.model_selection import train_test_split import lightgbm as lgb # 数据集划分 X_train_split, X_val, y_train_split, y_val = train_test_split(X_train, y_train, test_size=0.2) # 训练集划分为训练集和验证集 train_matrix = lgb.Dataset(X_train_split, label=y_train_split) valid_matrix = lgb.Dataset(X_val, label=y_val)params = {'boosting_type': 'gbdt', # gbdt'objective': 'binary', # 二分类'learning_rate': 0.1, # 学习率'metric': 'auc', # 评价指标'min_child_weight': 1e-3,'num_leaves': 31, # 叶子节点数量'max_depth': -1,'reg_lambda': 0, # 目标函数lambda'reg_alpha': 0, # 目标函数alpha'feature_fraction': 1, # 建树的特征选择比例'bagging_fraction': 1, # 建树的样本采样比例'bagging_freq': 0, # k 意味着每 k 次迭代执行bagging'seed': 2020,'nthread': 8, # 'silent': True,'verbose': -1, # <0 显示致命的, =0 显示错误 (警告), >0 显示信息 } """使用训练集数据进行模型训练""" model = lgb.train(params, train_set=train_matrix, valid_sets=valid_matrix, \num_boost_round=20000, verbose_eval=1000, early_stopping_rounds=200) # verbose_eval是打印log的间隔 # 对验证集进行预测 from sklearn import metrics from sklearn.metrics import roc_auc_score"""预测并计算roc的相关指标""" val_pre_lgb = model.predict(X_val, num_iteration=model.best_iteration) # val的预测值 fpr, tpr, threshold = metrics.roc_curve(y_val, val_pre_lgb) # 绘制roc曲线 roc_auc = metrics.auc(fpr, tpr) print('未调参前lightgbm单模型在验证集上的AUC:{}'.format(roc_auc)) """画出roc曲线图""" plt.figure(figsize=(8, 8)) plt.title('Validation ROC') plt.plot(fpr, tpr, 'b', label = 'Val AUC = %0.4f' % roc_auc) plt.ylim(0,1) plt.xlim(0,1) plt.legend(loc='best') plt.ylabel('True Positive Rate') plt.xlabel('False Positive Rate') # 画出对角线 plt.plot([0,1],[0,1],'r–') plt.show() # 更进一步的,使用5折交叉验证进行模型性能评估 """使用lightgbm 5折交叉验证进行建模预测""" cv_scores = []# 5折交叉验证 folds = 5 seed = 2020 kf = KFold(n_splits=folds, shuffle=True, random_state=seed)for i, (train_index, valid_index) in enumerate(kf.split(X_train, y_train)):print('************************************ {} \************************************'.format(str(i+1)))X_train_split, y_train_split, X_val, y_val = X_train.iloc[train_index], \y_train[train_index], X_train.iloc[valid_index], y_train[valid_index]train_matrix = lgb.Dataset(X_train_split, label=y_train_split)valid_matrix = lgb.Dataset(X_val, label=y_val)params = {'boosting_type': 'gbdt','objective': 'binary','learning_rate': 0.1,'metric': 'auc','min_child_weight': 1e-3,'num_leaves': 31,'max_depth': -1,'reg_lambda': 0,'reg_alpha': 0,'feature_fraction': 1,'bagging_fraction': 1,'bagging_freq': 0,'seed': 2020,'nthread': 8,'verbose': -1,}model = lgb.train(params, train_set=train_matrix, num_boost_round=20000, \valid_sets=valid_matrix, verbose_eval=1000, early_stopping_rounds=200)val_pred = model.predict(X_val, num_iteration=model.best_iteration)cv_scores.append(roc_auc_score(y_val, val_pred))print("lgb_scotrainre_list:{}".format(cv_scores)) print("lgb_score_mean:{}".format(np.mean(cv_scores))) print("lgb_score_std:{}".format(np.std(cv_scores)))
- 贪心调参 先使用当前对模型影响最大的参数进行调优,达到当前参数下的模型最优化,再使用对模型影响次之的参数进行调优,如此下去,直到所有的参数调整完毕。 这个方法的缺点就是可能会调到局部最优而不是全局最优,但是只需要一步一步的进行参数最优化调试即可,容易理解。 需要注意的是在树模型中参数调整的顺序,也就是各个参数对模型的影响程度,这里列举一下日常调参过程中常用的参数和调参顺序: ①:max_depth、num_leaves ②:min_data_in_leaf、min_child_weight ③:bagging_fraction、 feature_fraction、bagging_freq ④:reg_lambda、reg_alpha ⑤:min_split_gain from sklearn.model_selection import cross_val_score# 调objective best_obj = dict() for obj in objective:model = LGBMRegressor(objective=obj)"""预测并计算roc的相关指标"""score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()best_obj[obj] = score# num_leaves best_leaves = dict() for leaves in num_leaves: model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0], num_leaves=leaves) """预测并计算roc的相关指标""" score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean() best_leaves[leaves] = score# max_depth best_depth = dict() for depth in max_depth: model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0], num_leaves=min(best_leaves.items(), key=lambda x:x[1])[0], max_depth=depth) """预测并计算roc的相关指标""" score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean() best_depth[depth] = score""" 可依次将模型的参数通过上面的方式进行调整优化,并且通过可视化观察在每一个最优参数下模型的得分情况 """ 查看全文 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/2279046.html 如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!
- [学习笔记]金融风控实战
- 【C++】vector的使用 以及 迭代器失效问题
- 一文吃透Http协议
- 观察者模式及其应用(多种语言实现)
- 【软考备战·希赛网每日一练】2023年4月11日
- 工程机械车辆环保数据监控平台
- [ 汇编语言 (一) ] —— 踩着硬件的鼓点,掌握计算机的精髓
- 对目前的人工智能的一些思考
- 十种方式|||程序员业余时间副业技术变现赚钱的10个私藏方法,悄悄告诉你!
- 代码随想录算法训练营第五十三天 | 1143. 最长公共子序列、1035. 不相交的线、53. 最大子数组和
- 反序列化渗透与攻防(五)之shiro反序列化漏洞
- vue2+vue3
- 【hello Linux】环境变量
- 【Linux基础】常用命令整理
- 客快物流大数据项目(一百一十二):初识Spring Cloud
- C和C++中的struct有什么区别
- docker的数据卷详解
- 13、Qt生成dll-QLibrary方式使用
- 基于mapreduce 的 minHash 矩阵压缩
- 关于hashmap使用迭代器的问题
参考资料:
零基础入门金融风控-贷款违约预测
import pandas as pd
import matplotlib.pyplot as plt
# 读取数据
train = pd.read_csv('train.csv')
testA = pd.read_csv('testA.csv')
print('Train data shape:', train.shape)
print('testA data shape:', testA.shape)
train.head()
testA.head()
import pandas as pd
import matplotlib.pyplot as plt
# 读取数据
train = pd.read_csv('train.csv')
testA = pd.read_csv('testA.csv')
print('Train data shape:', train.shape)
print('testA data shape:', testA.shape)
train.head()
testA.head()
# 混淆矩阵
import numpy as np
from sklearn.metrics import confusion_matrix
"""预:反 预:正
真:反 true negatives false positives
真:正 false negatives true positives
"""
y_pred = [0, 1, 0, 1]
y_true = [0, 1, 1, 0]
print('混淆矩阵:\n', confusion_matrix(y_true, y_pred))
# accuracy
from sklearn.metrics import accuracy_score
y_pred = [0, 1, 0, 1]
y_true = [0, 1, 1, 0]
print('ACC:', accuracy_score(y_true, y_pred))
# Precision, Recall, F1-score
from sklearn import metrics
y_pred = [0, 1, 0, 1]
y_ture = [0, 1, 1, 0]
print('Precision', metrics.precision_score(y_ture, y_pred))
print('Recall', metrics.recall_score(y_true, y_pred))
print('F1-score', metrics.f1_score(y_true, y_pred))
# P-R曲线
import matplotlib.pyplot as plt
from sklearn.metrics import precision_recall_curve
y_pred = [0, 1, 1, 0, 1, 1, 0, 1, 1, 1]
y_true = [0, 1, 1, 0, 1, 0, 1, 1, 0, 1]
precision , recall, thresholds = precision_recall_curve(y_true, y_pred)
plt.plot(recall, precision)
# ROC曲线
from sklearn.metrics import roc_curve
y_pred = [0, 1, 1, 0, 1, 1, 0, 1, 1, 1]
y_true = [0, 1, 1, 0, 1, 0, 1, 1, 0, 1]
FPR, TPR, thresholds = roc_curve(y_true, y_pred)
plt.title('ROC')
plt.plot(FPR, TPR, 'b') # 蓝实线
plt.plot([0,1], [0,1], 'r--') # 红虚线
plt.ylabel('TPR')
plt.xlabel('FPR')
# AUC
from sklearn.metrics import roc_auc_score
y_true = np.array([0, 0, 1, 1])
y_scores = np.array([0.1, 0.4, 0.35, 0.8])
print('AUC score', roc_auc_score(y_true, y_scores))
# 最大KS值 在实际操作时往往使用ROC曲线配合求出KS值
from sklearn.metrics import roc_curve
y_pred = [0, 1, 1, 0, 1, 1, 0, 1, 1, 1]
y_true = [0, 1, 1, 0, 1, 0, 1, 1, 1, 1]
FPR, TPR, thresholds = roc_curve(y_true, y_pred)
KS = abs(FPR-TPR).max()
print('最大KS值:', KS)
# 混淆矩阵
import numpy as np
from sklearn.metrics import confusion_matrix
"""预:反 预:正
真:反 true negatives false positives
真:正 false negatives true positives
"""
y_pred = [0, 1, 0, 1]
y_true = [0, 1, 1, 0]
print('混淆矩阵:\n', confusion_matrix(y_true, y_pred))
# accuracy
from sklearn.metrics import accuracy_score
y_pred = [0, 1, 0, 1]
y_true = [0, 1, 1, 0]
print('ACC:', accuracy_score(y_true, y_pred))
# Precision, Recall, F1-score
from sklearn import metrics
y_pred = [0, 1, 0, 1]
y_ture = [0, 1, 1, 0]
print('Precision', metrics.precision_score(y_ture, y_pred))
print('Recall', metrics.recall_score(y_true, y_pred))
print('F1-score', metrics.f1_score(y_true, y_pred))
# P-R曲线
import matplotlib.pyplot as plt
from sklearn.metrics import precision_recall_curve
y_pred = [0, 1, 1, 0, 1, 1, 0, 1, 1, 1]
y_true = [0, 1, 1, 0, 1, 0, 1, 1, 0, 1]
precision , recall, thresholds = precision_recall_curve(y_true, y_pred)
plt.plot(recall, precision)
# ROC曲线
from sklearn.metrics import roc_curve
y_pred = [0, 1, 1, 0, 1, 1, 0, 1, 1, 1]
y_true = [0, 1, 1, 0, 1, 0, 1, 1, 0, 1]
FPR, TPR, thresholds = roc_curve(y_true, y_pred)
plt.title('ROC')
plt.plot(FPR, TPR, 'b') # 蓝实线
plt.plot([0,1], [0,1], 'r--') # 红虚线
plt.ylabel('TPR')
plt.xlabel('FPR')
# AUC
from sklearn.metrics import roc_auc_score
y_true = np.array([0, 0, 1, 1])
y_scores = np.array([0.1, 0.4, 0.35, 0.8])
print('AUC score', roc_auc_score(y_true, y_scores))
# 最大KS值 在实际操作时往往使用ROC曲线配合求出KS值
from sklearn.metrics import roc_curve
y_pred = [0, 1, 1, 0, 1, 1, 0, 1, 1, 1]
y_true = [0, 1, 1, 0, 1, 0, 1, 1, 1, 1]
FPR, TPR, thresholds = roc_curve(y_true, y_pred)
KS = abs(FPR-TPR).max()
print('最大KS值:', KS)
"""
1.EDA价值主要在于熟悉了解整个数据集的基本情况(缺失值,异常值),对数据集进行验证是否可以进行接
下来的机器学习或者深度学习建模.
2.了解变量间的相互关系、变量与预测值之间的存在关系。
3.为特征工程做准备
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
import warnings
warnings.filterwarnings('ignore')
data_train = pd.read_csv('./train.csv')
data_test_a = pd.read_csv('./testA.csv')
# 通过os.getcwd()查看当前工作目录
import os
os.getcwd()
# 通过nrows参数读取某几行
data_train_sample = pd.read_csv('./train.csv', nrows=5)
data_train_sample
# 通过设置chunksize参数来控制每次迭代数据的大小
chunker = pd.read_csv('./train.csv', chunksize=5)
for item in chunker:print(type(item)) # <class 'pandas.core.frame.DataFrame'>print(len(item)) # 5display(item)break
"""
1.EDA价值主要在于熟悉了解整个数据集的基本情况(缺失值,异常值),对数据集进行验证是否可以进行接
下来的机器学习或者深度学习建模.
2.了解变量间的相互关系、变量与预测值之间的存在关系。
3.为特征工程做准备
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
import warnings
warnings.filterwarnings('ignore')
data_train = pd.read_csv('./train.csv')
data_test_a = pd.read_csv('./testA.csv')
# 通过os.getcwd()查看当前工作目录
import os
os.getcwd()
# 通过nrows参数读取某几行
data_train_sample = pd.read_csv('./train.csv', nrows=5)
data_train_sample
# 通过设置chunksize参数来控制每次迭代数据的大小
chunker = pd.read_csv('./train.csv', chunksize=5)
for item in chunker:print(type(item)) # <class 'pandas.core.frame.DataFrame'>print(len(item)) # 5display(item)break
data_test_a.shape
data_train.shape
data_train.columns
# 查看数据类型
data_train.info()
# 查看各个特征(数值类型)的基本统计量
data_train.describe()
# data_train.head(3).append(data_train.tail(3)) # 未来会被弃用
pd.concat([data_train.head(3), data_train.tail(3)]) # 建议使用
data_test_a.shape
data_train.shape
data_train.columns
# 查看数据类型
data_train.info()
# 查看各个特征(数值类型)的基本统计量
data_train.describe()
# data_train.head(3).append(data_train.tail(3)) # 未来会被弃用
pd.concat([data_train.head(3), data_train.tail(3)]) # 建议使用
# 查看缺失值
print(f'There are {data_train.isnull().any().sum()} columns in train dataset with missing values.')
# 进一步查看缺失特征中缺失率大于50%的特征
have_null_fea_dict = (data_train.isnull().sum()/len(data_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 # 没有
# 具体查看缺失特征及缺失率
# nan可视化
missing = data_train.isnull().sum()/len(data_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 data_train.columns if data_train[col].nunique() <= 1] # number of unique
oen_value_fea_test = [col for col in data_test_a.columns if data_test_a[col].nunique() <= 1]
print(one_value_fea)
print(oen_value_fea_test)
# 查看缺失值
print(f'There are {data_train.isnull().any().sum()} columns in train dataset with missing values.')
# 进一步查看缺失特征中缺失率大于50%的特征
have_null_fea_dict = (data_train.isnull().sum()/len(data_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 # 没有
# 具体查看缺失特征及缺失率
# nan可视化
missing = data_train.isnull().sum()/len(data_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 data_train.columns if data_train[col].nunique() <= 1] # number of unique
oen_value_fea_test = [col for col in data_test_a.columns if data_test_a[col].nunique() <= 1]
print(one_value_fea)
print(oen_value_fea_test)
总结:47列数据中有22列都缺少数据,这在现实世界中很正常。‘policyCode’具有一个唯一值(或全部缺失)。有很多连
续变量和一些分类变量。
- 特征一般都是由类别型特征和数值型特征组成
- 类别型特征有时具有非数值关系,有时也具有数值关系。比如‘grade’中的等级A,B,C等,是否只是单纯的
分类,还是A优于其他要结合业务判断。
- 数值型特征本是可以直接入模的,但往往风控人员要对其做分箱,转化为WOE编码进而做标准评分卡等操作。从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
# 数值型特征
numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns)
print(numerical_fea)
# 类别型特征
category_fea = list(filter(lambda x: x not in numerical_fea, list(data_train.columns)))
category_fea
data_train.grade
# 数值型变量分析:
# 1. 划分数值型变量中的连续型变量和和离散型变量
# 过滤数值型类别特征:如果特征下面的不同取值小于10种,则认为是离散型数值变量,否则认为是连续型数值变量
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)continuenumerical_serial_fea.append(fea)return numerical_serial_fea, numerical_noserial_feanumerical_serial_fea, numerical_noserial_fea = get_numerical_serial_fea(data_train,numerical_fea)
print(numerical_serial_fea)
print(numerical_noserial_fea)
# 离散型数值变量分析
data_train['term'].value_counts()
data_train['homeOwnership'].value_counts()
data_train['isDefault'].value_counts()
data_train['initialListStatus'].value_counts()
data_train['applicationType'].value_counts()
data_train['policyCode'].value_counts() # 无用变量,全部一个值
data_train['n11'].value_counts() # 相差悬殊,不用再分析
data_train['n12'].value_counts() # 相差悬殊,不用再分析
# 连续型数值变量分析
# 每个数值特征的分布可视化
f = pd.melt(data_train, value_vars=numerical_serial_fea)
g = sns.FacetGrid(f, col='variable', col_wrap=2, sharex=False, sharey=False) # 结构化多绘图网格,初始化FacetGrid对象
g = g.map(sns.distplot, 'value') # 将sns.distplot应用到每个数据子集
# 参考资料:https://blog.csdn.net/weixin_43618989/article/details/105613021
# https://zhuanlan.zhihu.com/p/484363632
"""
1. 查看某一个数值型变量的分布,查看变量是否符合正态分布,如果不符合正态分布的变量可以log化后再观察下是否符合正态分布。
2. 如果想统一处理一批数据变标准化 必须把这些之前已经正态化的数据提出
"""
# 绘制交易数据(loanAmnt)分布
plt.figure(figsize=(16, 12)) # 画布大小,宽为16,高为12
plt.suptitle('Transaction Values Distribution', fontsize=22) # 标题
plt.subplot(221) # 整个画布分为两行两列,当前子图位于第一个位置
sub_plot_1 = sns.distplot(data_train['loanAmnt']) # 该子图上绘制直方图,默认包含核密度曲线
sub_plot_1.set_title('loanAmnt Distribution', 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(data_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)
# 类别型特征分析
category_fea
data_train['grade'].value_counts() # 默认
data_train['subGrade'].value_counts()
data_train['employmentLength'].value_counts()
data_train['issueDate'].value_counts()
data_train['earliesCreditLine'].value_counts()
分类,还是A优于其他要结合业务判断。
# 数值型特征
numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns)
print(numerical_fea)
# 类别型特征
category_fea = list(filter(lambda x: x not in numerical_fea, list(data_train.columns)))
category_fea
data_train.grade
# 数值型变量分析:
# 1. 划分数值型变量中的连续型变量和和离散型变量
# 过滤数值型类别特征:如果特征下面的不同取值小于10种,则认为是离散型数值变量,否则认为是连续型数值变量
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)continuenumerical_serial_fea.append(fea)return numerical_serial_fea, numerical_noserial_feanumerical_serial_fea, numerical_noserial_fea = get_numerical_serial_fea(data_train,numerical_fea)
print(numerical_serial_fea)
print(numerical_noserial_fea)
# 离散型数值变量分析
data_train['term'].value_counts()
data_train['homeOwnership'].value_counts()
data_train['isDefault'].value_counts()
data_train['initialListStatus'].value_counts()
data_train['applicationType'].value_counts()
data_train['policyCode'].value_counts() # 无用变量,全部一个值
data_train['n11'].value_counts() # 相差悬殊,不用再分析
data_train['n12'].value_counts() # 相差悬殊,不用再分析
# 连续型数值变量分析
# 每个数值特征的分布可视化
f = pd.melt(data_train, value_vars=numerical_serial_fea)
g = sns.FacetGrid(f, col='variable', col_wrap=2, sharex=False, sharey=False) # 结构化多绘图网格,初始化FacetGrid对象
g = g.map(sns.distplot, 'value') # 将sns.distplot应用到每个数据子集
# 参考资料:https://blog.csdn.net/weixin_43618989/article/details/105613021
# https://zhuanlan.zhihu.com/p/484363632
"""
1. 查看某一个数值型变量的分布,查看变量是否符合正态分布,如果不符合正态分布的变量可以log化后再观察下是否符合正态分布。
2. 如果想统一处理一批数据变标准化 必须把这些之前已经正态化的数据提出
"""
# 绘制交易数据(loanAmnt)分布
plt.figure(figsize=(16, 12)) # 画布大小,宽为16,高为12
plt.suptitle('Transaction Values Distribution', fontsize=22) # 标题
plt.subplot(221) # 整个画布分为两行两列,当前子图位于第一个位置
sub_plot_1 = sns.distplot(data_train['loanAmnt']) # 该子图上绘制直方图,默认包含核密度曲线
sub_plot_1.set_title('loanAmnt Distribution', 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(data_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)
# 类别型特征分析
category_fea
data_train['grade'].value_counts() # 默认
data_train['subGrade'].value_counts()
data_train['employmentLength'].value_counts()
data_train['issueDate'].value_counts()
data_train['earliesCreditLine'].value_counts()
总结:
- 上面我们用value_counts()等函数看了特征属性的分布,但是图表是概括原始信息最便捷的方式。
- 数无形时少直觉。
- 同一份数据集,在不同的尺度刻画上显示出来的图形反映的规律是不一样的。python将数据转化成图表,但结论是否正确需要由你保证。
plt.figure(figsize=(8, 8))
sns.barplot(data_train['employmentLength'].value_counts(dropna=False)[:20], # .values可加可不加data_train['employmentLength'].value_counts(dropna=False).keys()[:20])
plt.show()
plt.figure(figsize=(8, 8))
sns.barplot(data_train['employmentLength'].value_counts(dropna=False)[:20], # .values可加可不加data_train['employmentLength'].value_counts(dropna=False).keys()[:20])
plt.show()
# 1.首先查看类别型变量在不同y值上的分布
train_loan_fr = data_train[data_train['isDefault'] == 1] # .loc可加可不加
train_loan_nofr = data_train.loc[data_train['isDefault'] == 0]
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 8))
# 优化:对索引排序
# 欺诈用户的不同等级的数量分布
train_loan_fr.groupby('grade').size().sort_index(ascending=False).plot(kind='barh', ax=ax1, title='Count of grade fraud')
# 非欺诈用户的不同等级的数量分布
train_loan_nofr.groupby('grade').size().sort_index(ascending=False).plot(kind='barh', ax=ax2, title='Count of grade non-fraud')
# 欺诈用户的就业年限的数量分布
employmentLength_index = train_loan_fr.groupby('employmentLength').size().index
b = np.arange(11)
b[0], b[1], b[10] = b[10], b[0], b[1]
train_loan_fr.groupby('employmentLength').size().reindex(employmentLength_index[b][::-1]).plot(kind='barh', ax=ax3, title='Count of employmentLength fraud')
# 非欺诈用户的就业年限的数量分布
train_loan_nofr.groupby('employmentLength').size().reindex(employmentLength_index[b][::-1]).plot(kind='barh', ax=ax4, title='Count of employmentLength non-fraud')
plt.show()
# 2.查看连续型变量在不同y值上的分布
fig, axes = plt.subplots(1, 2, figsize=(15, 6))
data_train[data_train['isDefault'] == 1]['loanAmnt'].apply(np.log).plot(kind='hist',bins = 100,title = 'Log Loan Amt - Fraud',color = 'r',xlim = (-3, 10),ax = axes[0])
data_train[data_train['isDefault'] == 0]['loanAmnt'].apply(np.log).plot(kind='hist',bins = 100,title = 'Log Loan Amt - Not Fraud',color = 'b',xlim = (-3, 10),ax = axes[1])
"""
图一:绘制欺诈用户和非欺诈用户对应的数量,并在图上标注百分比
"""
total = len(data_train)
print(total_amt)
plt.figure(figsize=(12, 5))
plt.subplot(121) # 下面的图画在第一个坐标轴上
plot_tr = sns.countplot(x='isDefault', data=data_train) #
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,'{:1.2f}%'.format(height/total*100),ha="center", fontsize=15) # ha是horizontal alignment(水平对齐)的含义"""
图二:绘制欺诈用户和非欺诈用户对应的贷款金额总数,并在图上标注百分比
"""
total_amt = data_train['loanAmnt'].sum() # 总的loanAmnt
percent_amt = (data_train.groupby(['isDefault'])['loanAmnt'].sum())
percent_amt = percent_amt.reset_index()
plt.subplot(122) # 下面的图画在第二个坐标轴上
plot_tr_2 = sns.barplot(x='isDefault', y='loanAmnt', 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,'{:1.2f}%'.format(height/total_amt * 100),ha="center", fontsize=15)
# 1.首先查看类别型变量在不同y值上的分布
train_loan_fr = data_train[data_train['isDefault'] == 1] # .loc可加可不加
train_loan_nofr = data_train.loc[data_train['isDefault'] == 0]
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 8))
# 优化:对索引排序
# 欺诈用户的不同等级的数量分布
train_loan_fr.groupby('grade').size().sort_index(ascending=False).plot(kind='barh', ax=ax1, title='Count of grade fraud')
# 非欺诈用户的不同等级的数量分布
train_loan_nofr.groupby('grade').size().sort_index(ascending=False).plot(kind='barh', ax=ax2, title='Count of grade non-fraud')
# 欺诈用户的就业年限的数量分布
employmentLength_index = train_loan_fr.groupby('employmentLength').size().index
b = np.arange(11)
b[0], b[1], b[10] = b[10], b[0], b[1]
train_loan_fr.groupby('employmentLength').size().reindex(employmentLength_index[b][::-1]).plot(kind='barh', ax=ax3, title='Count of employmentLength fraud')
# 非欺诈用户的就业年限的数量分布
train_loan_nofr.groupby('employmentLength').size().reindex(employmentLength_index[b][::-1]).plot(kind='barh', ax=ax4, title='Count of employmentLength non-fraud')
plt.show()
# 2.查看连续型变量在不同y值上的分布
fig, axes = plt.subplots(1, 2, figsize=(15, 6))
data_train[data_train['isDefault'] == 1]['loanAmnt'].apply(np.log).plot(kind='hist',bins = 100,title = 'Log Loan Amt - Fraud',color = 'r',xlim = (-3, 10),ax = axes[0])
data_train[data_train['isDefault'] == 0]['loanAmnt'].apply(np.log).plot(kind='hist',bins = 100,title = 'Log Loan Amt - Not Fraud',color = 'b',xlim = (-3, 10),ax = axes[1])
"""
图一:绘制欺诈用户和非欺诈用户对应的数量,并在图上标注百分比
"""
total = len(data_train)
print(total_amt)
plt.figure(figsize=(12, 5))
plt.subplot(121) # 下面的图画在第一个坐标轴上
plot_tr = sns.countplot(x='isDefault', data=data_train) #
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,'{:1.2f}%'.format(height/total*100),ha="center", fontsize=15) # ha是horizontal alignment(水平对齐)的含义"""
图二:绘制欺诈用户和非欺诈用户对应的贷款金额总数,并在图上标注百分比
"""
total_amt = data_train['loanAmnt'].sum() # 总的loanAmnt
percent_amt = (data_train.groupby(['isDefault'])['loanAmnt'].sum())
percent_amt = percent_amt.reset_index()
plt.subplot(122) # 下面的图画在第二个坐标轴上
plot_tr_2 = sns.barplot(x='isDefault', y='loanAmnt', 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,'{:1.2f}%'.format(height/total_amt * 100),ha="center", fontsize=15)
# 训练集:利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列
# issueDate: 贷款发放日期
data_train['issueDate'] = pd.to_datetime(data_train['issueDate'], format='%Y-%m-%d') # 将数据类型转化为datetime类型
startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d') # 设置起始日期,将字符串变成datetime类型
data_train['issueDateDT'] = data_train['issueDate'].apply(lambda x: x-startdate).dt.days # 计算距离起始日期的天数,添加到新的一列中
# 测试集:利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列
data_test_a['issueDate'] = pd.to_datetime(data_train['issueDate'],format='%Y-%m-%d')
startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d')
data_test_a['issueDateDT'] = data_test_a['issueDate'].apply(lambda x: x-startdate).dt.days
plt.hist(data_train['issueDateDT'], label='train') # 根据数据绘制直方图
plt.hist(data_test_a['issueDateDT'], label='train')
plt.legend() # 添加图例
plt.title('Distribution of issueDateDT dates');
# train 和 test issueDateDT 日期有重叠 所以使用基于时间的分割进行验证是不明智的
# 训练集:利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列
# issueDate: 贷款发放日期
data_train['issueDate'] = pd.to_datetime(data_train['issueDate'], format='%Y-%m-%d') # 将数据类型转化为datetime类型
startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d') # 设置起始日期,将字符串变成datetime类型
data_train['issueDateDT'] = data_train['issueDate'].apply(lambda x: x-startdate).dt.days # 计算距离起始日期的天数,添加到新的一列中
# 测试集:利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列
data_test_a['issueDate'] = pd.to_datetime(data_train['issueDate'],format='%Y-%m-%d')
startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d')
data_test_a['issueDateDT'] = data_test_a['issueDate'].apply(lambda x: x-startdate).dt.days
plt.hist(data_train['issueDateDT'], label='train') # 根据数据绘制直方图
plt.hist(data_test_a['issueDateDT'], label='train')
plt.legend() # 添加图例
plt.title('Distribution of issueDateDT dates');
# train 和 test issueDateDT 日期有重叠 所以使用基于时间的分割进行验证是不明智的
# 透视图 索引可以有多个,“columns(列)”是可选的,聚合函数aggfunc最后是被应用到了变量“values”中你所列举的项目上。
# 透视图 行索引是'grade'(贷款等级),列索引是"issueDateDT"(贷款发放距离初始日期),值是'loanAmnt'(贷款金额)经过np.sum聚合的结果
pivot = pd.pivot_table(data_train, index=['grade'], columns=['issueDateDT'], values=['loanAmnt'], aggfunc=np.sum)
pivot
# 透视图 索引可以有多个,“columns(列)”是可选的,聚合函数aggfunc最后是被应用到了变量“values”中你所列举的项目上。
# 透视图 行索引是'grade'(贷款等级),列索引是"issueDateDT"(贷款发放距离初始日期),值是'loanAmnt'(贷款金额)经过np.sum聚合的结果
pivot = pd.pivot_table(data_train, index=['grade'], columns=['issueDateDT'], values=['loanAmnt'], aggfunc=np.sum)
pivot
import pandas_profiling
pfr = pandas_profiling.ProfileReport(data_train)
pfr.to_file('./example.html')
import pandas_profiling
pfr = pandas_profiling.ProfileReport(data_train)
pfr.to_file('./example.html')
数据探索性分析是我们初步了解数据,熟悉数据为特征工程做准备的阶段,甚至很多时候EDA阶段提取出来的特征可以直接当作规则来用。可见EDA的重要性,这个阶段的主要工作还是借助于各个简单的统计量来对数据整体的了解,分析各个类型变量相互之间的关系,以及用合适的图形可视化出来直观观察。希望本节内容能给初学者带来帮助,更期待各位学习者对其中的不足提出建议。
- 学习特征预处理、缺失值、异常值处理、数据分桶等特征处理方法
- 学习特征交互、编码、选择的相应方法
- 完成相应学习打卡任务,两个选做的作业不做强制性要求,供学有余力同学自己探索
- 数据预处理:
a. 缺失值的填充
b. 时间格式处理
c. 对象类型特征转换到数值
- 异常值处理:
a. 基于3sigma原则
b. 基于箱型图
- 数据分箱
a. 固定宽度分箱
b. 分位数分箱
- 离散数值型数据分箱
- 连续数值型数据分箱
a. 缺失值的填充
b. 时间格式处理
c. 对象类型特征转换到数值
a. 基于3sigma原则
b. 基于箱型图
a. 固定宽度分箱
b. 分位数分箱
- 离散数值型数据分箱
- 连续数值型数据分箱
c. 卡方分箱(选做作业)
4. 特征交互
a. 特征和特征之间组合
b. 特征和特征之间衍生
c. 其他特征衍生的尝试(选做作业)
5. 特征编码
a. one-hot编码
b. label-encode编码
6. 特征选择
a. 1 Filter
b. 2 Wrapper (RFE)
c. 3 Embedded
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
from tqdm import tqdm
from sklearn.preprocessing import LabelEncoder
# from sklearn.feature_selection import SelectKBest
# from sklearn.feature_selection import chi2
# from sklearn.preprocessing import MinMaxScaler
# import xgboost as xgb
# import lightgbm as lgb
# from catboost import CatBOostRegressor
# from sklearn.model_selection import StratifiedKFold, KFold
# from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss
import warnings
warnings.filterwarnings('ignore')
data_train =pd.read_csv('./train.csv')
data_test_a = pd.read_csv('./testA.csv')
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
from tqdm import tqdm
from sklearn.preprocessing import LabelEncoder
# from sklearn.feature_selection import SelectKBest
# from sklearn.feature_selection import chi2
# from sklearn.preprocessing import MinMaxScaler
# import xgboost as xgb
# import lightgbm as lgb
# from catboost import CatBOostRegressor
# from sklearn.model_selection import StratifiedKFold, KFold
# from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss
import warnings
warnings.filterwarnings('ignore')
data_train =pd.read_csv('./train.csv')
data_test_a = pd.read_csv('./testA.csv')
- 数据EDA部分我们已经对数据的大概和某些特征分布有了了解,数据预处理部分一般我们要处理一些EDA阶段分析出来的问题,这里介绍了数据缺失值的填充,时间格式特征的转化处理,某些对象类别特征的处理。
在比赛中数据预处理是必不可少的一部分,对于缺失值的填充往往会影响比赛的结果,在比赛中不妨尝试多种填充然后比较结果选择结果最优的一种;
比赛数据相比真实场景的数据相对要“干净”一些,但是还是会有一定的“脏”数据存在,清洗一些异常值往往会获得意想不到的效果。
# 找出数据中的object类型特征和非object类型特征
numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns) # 非object类型特征
category_fea = list(filter(lambda x: x not in numerical_fea,list(data_train.columns))) # object类型特征
# 也可以category_fea = list(data_train.select_dtypes(include=['object']).columns)
label = 'isDefault'
numerical_fea.remove(label) # 剔除label标签
-
把所有缺失值替换为指定的值0
data_train = data_train.fillna(0)
-
纵向用缺失值上面的值替换缺失值
data_train = data_train.fillna(axis=0,method=‘ffill’)
-
纵向用缺失值下面的值替换缺失值,且设置最多只填充两个连续的缺失值
data_train = data_train.fillna(axis=0,method=‘bfill’,limit=2)
把所有缺失值替换为指定的值0
data_train = data_train.fillna(0)
纵向用缺失值上面的值替换缺失值
data_train = data_train.fillna(axis=0,method=‘ffill’)
纵向用缺失值下面的值替换缺失值,且设置最多只填充两个连续的缺失值
data_train = data_train.fillna(axis=0,method=‘bfill’,limit=2)
参考资料:https://www.jb51.net/article/255677.htm
# 查看缺失值情况
data_train.isnull().sum()
# 按照平均数填充数值型特征
data_train.fillna(data_train[numerical_fea].median(), inplace=True) # 优化
data_test_a.fillna(data_train[numerical_fea].median(), inplace=True)
# 按照众数填充类别型特征
data_train.fillna(data_train[category_fea].mode().iloc[0], inplace=True) # 勘误
data_test_a.fillna(data_train[category_fea].mode().iloc[0], inplace=True)
data_train.isnull().sum()
# 利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列
for data in [data_train, data_test_a]: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
# 利用datetime类型,计算贷款发放日期距离设定日期的天数,并添加新的列
for data in [data_train, data_test_a]: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_train['employmentLength'].value_counts(dropna=False).sort_index()
def employmentLength_to_int(s):if pd.isnull(s):return selse:return np.int(s.split()[0])for data in [data_train, data_test_a]: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)
data['employmentLength'].value_counts(dropna=False).sort_index()
# 对earliesCreditLine(借款人最早报告的信用额度开立的月份)进行预处理
data_train['earliesCreditLine'].sample(5) # 采五个样本
for data in [data_train, data_test_a]:data['earliesCreditLine'] = data['earliesCreditLine'].apply(lambda s: int(s[-4:])) # 截取年份
data_train['earliesCreditLine'].sample(5) # 采五个样本
# cate_features
category_fea
data_train['employmentLength'].value_counts(dropna=False).sort_index()
def employmentLength_to_int(s):if pd.isnull(s):return selse:return np.int(s.split()[0])for data in [data_train, data_test_a]: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)
data['employmentLength'].value_counts(dropna=False).sort_index()
# 对earliesCreditLine(借款人最早报告的信用额度开立的月份)进行预处理
data_train['earliesCreditLine'].sample(5) # 采五个样本
for data in [data_train, data_test_a]:data['earliesCreditLine'] = data['earliesCreditLine'].apply(lambda s: int(s[-4:])) # 截取年份
data_train['earliesCreditLine'].sample(5) # 采五个样本
# cate_features
category_fea
# 部分类别特征
cate_features = ['grade', 'subGrade', \'employmentTitle', 'homeOwnership', 'verificationStatus', 'purpose', 'postCode', 'regionCode', \'applicationType', 'initialListStatus', 'title', 'policyCode']
for f in cate_features:print(f, '类型数:', data[f].nunique())
# 像等级这种类别特征,是有优先级的可以用map映射成数值
for data in [data_train, data_test_a]:data['grade'] = data['grade'].map({'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7})
# 类型数在2之上,又不是高维稀疏的,且纯分类的特征(没有优先级比较),可以用get_dummies转化为one-hot编码
# 参考: https://blog.csdn.net/u010712012/article/details/83002388
# https://zhuanlan.zhihu.com/p/139144355
# 勘误:
data_train = pd.get_dummies(data_train, columns=['homeOwnership', 'verificationStatus', 'purpose', 'regionCode'])
data_test_a = pd.get_dummies(data_test_a, columns=['homeOwnership', 'verificationStatus', 'purpose', 'regionCode'])
# 部分类别特征
cate_features = ['grade', 'subGrade', \'employmentTitle', 'homeOwnership', 'verificationStatus', 'purpose', 'postCode', 'regionCode', \'applicationType', 'initialListStatus', 'title', 'policyCode']
for f in cate_features:print(f, '类型数:', data[f].nunique())
# 像等级这种类别特征,是有优先级的可以用map映射成数值
for data in [data_train, data_test_a]:data['grade'] = data['grade'].map({'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7})
# 类型数在2之上,又不是高维稀疏的,且纯分类的特征(没有优先级比较),可以用get_dummies转化为one-hot编码
# 参考: https://blog.csdn.net/u010712012/article/details/83002388
# https://zhuanlan.zhihu.com/p/139144355
# 勘误:
data_train = pd.get_dummies(data_train, columns=['homeOwnership', 'verificationStatus', 'purpose', 'regionCode'])
data_test_a = pd.get_dummies(data_test_a, columns=['homeOwnership', 'verificationStatus', 'purpose', 'regionCode'])
- 当你发现异常值后,一定要先分清是什么原因导致的异常值,然后再考虑如何处理。首先,如果这一异常值并不代表一种规律性的,而是极其偶然的现象,或者说你并不想研究这种偶然的现象,这时可以将其删除。其次,如果异常值存在且代表了一种真实存在的现象,那就不能随便删除。在现有的欺诈场景中很多时候欺诈数据本身相对于正常数据来说就是异常的,我们要把这些异常点纳入,重新拟合模型,研究其规律。能用监督的用监督模型,不能用的还可以考虑用异常检测的算法来做。
- 注意test的数据不能删。
在统计学中,如果一个数据分布近似正态,那么大约 68% 的数据值会在均值的一个标准差范围内,大约 95% 会在两个标准差范围内,大约 99.7% 会在三个标准差范围内。
def find_outliers_by_3sigma(data,fea):data_std = np.std(data[fea])data_mean = np.mean(data[fea])outliers_cut_off = data_std * 3lower_rule = data_mean - outliers_cut_offupper_rule = data_mean + outliers_cut_offdata[fea+'_outliers'] = data[fea].apply(lambda x: '异常值' if x > upper_rule or x < lower_rule else '正常值')return data
# data_train = data_train.copy()
for item in ['homeOwnership', 'verificationStatus', 'purpose', 'regionCode']:numerical_fea.remove(item)
for fea in numerical_fea:data_train = find_outliers_by_3sigma(data_train,fea)print(data_train[fea+'_outliers'].value_counts())print(data_train.groupby(fea+'_outliers')['isDefault'].sum()) # 当前特则为正常值(异常值)时,欺诈用户的数量print('*'*10)
# 删除异常值
for fea in numerical_fea:data_train = data_train[data_train[fea+'_outliers']=='正常值'] # 保留正常值对应的行
data_train = data_train.reset_index(drop=True) # 勘误
总结一句话:四分位数会将数据分为三个点和四个区间,IQR = Q3 -Q1,下触须=Q1 − 1.5x IQR,上触须=Q3 + 1.5x IQR;
-
特征分箱的目的:
a. 从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
-
数据分桶的对象:
a. 将连续变量离散化
b. 将多状态的离散变量合并成少状态
-
分箱的原因:
a. 数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧氏距离作为相似度函数来
测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间量化即数据
分桶也叫做数据分箱,然后使用量化后的结果。
-
分箱的优点:
a. 处理缺失值:当数据源可能存在缺失值,此时可以把null单独作为一个分箱。
b. 处理异常值:当数据中存在离群点时,可以把其通过分箱离散化处理,从而提高变量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。
c. 业务解释性:我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但实际x与y之间经常存在
着非线性关系,此时可经过WOE变换。
-
特别要注意一下分箱的基本原则:
a. (1)最小分箱占比不低于5%
b. (2)箱内不能全部是好客户
c. (3)连续箱单调
"""
1. 固定宽度分箱
当数值横跨多个数量级时,最好按照 10 的幂(或任何常数的幂)来进行分组:0-9、10-99、100-999、1000-9999,等
等。固定宽度分箱非常容易计算,但如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子。
"""
# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是loanAmnt/1000
data['loanAmnt_bin1'] = np.floor_divide(data['loanAmnt'], 1000) # 向下取整
# 通过对数函数映射到指数宽度分箱
data['loanAmnt_bin2'] = np.floor(np.log10(data['loanAmnt']))
"""
2.分位数分箱
"""
data['loanAmnt_bin3'] = pd.qcut(data['loanAmnt'], 10, labels=False) # 根据分位数分10箱,且只显示第几箱(不显示箱的范围如(xxx,xxx]))
"""
3.卡方分箱及其他分箱方法的尝试(略)
"""
特征分箱的目的:
a. 从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
数据分桶的对象:
a. 将连续变量离散化
b. 将多状态的离散变量合并成少状态
分箱的原因:
a. 数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧氏距离作为相似度函数来
测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间量化即数据
分桶也叫做数据分箱,然后使用量化后的结果。
分箱的优点:
a. 处理缺失值:当数据源可能存在缺失值,此时可以把null单独作为一个分箱。
b. 处理异常值:当数据中存在离群点时,可以把其通过分箱离散化处理,从而提高变量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。
c. 业务解释性:我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但实际x与y之间经常存在
着非线性关系,此时可经过WOE变换。
特别要注意一下分箱的基本原则:
a. (1)最小分箱占比不低于5%
b. (2)箱内不能全部是好客户
c. (3)连续箱单调
"""
1. 固定宽度分箱
当数值横跨多个数量级时,最好按照 10 的幂(或任何常数的幂)来进行分组:0-9、10-99、100-999、1000-9999,等
等。固定宽度分箱非常容易计算,但如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子。
"""
# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是loanAmnt/1000
data['loanAmnt_bin1'] = np.floor_divide(data['loanAmnt'], 1000) # 向下取整
# 通过对数函数映射到指数宽度分箱
data['loanAmnt_bin2'] = np.floor(np.log10(data['loanAmnt']))
"""
2.分位数分箱
"""
data['loanAmnt_bin3'] = pd.qcut(data['loanAmnt'], 10, labels=False) # 根据分位数分10箱,且只显示第几箱(不显示箱的范围如(xxx,xxx]))
"""
3.卡方分箱及其他分箱方法的尝试(略)
"""
- 交互特征的构造非常简单,使用起来却代价不菲。如果线性模型中包含有交互特征对,那它的训练时间和评
分时间就会从 O(n) 增加到 O(n2n^2n2),其中 n 是单一特征的数量。
# 获得grade, subGrade和isDefault的均值的映射关系,并用均值创建新的列
for col in ['grade', 'subGrade']:# temp_dict:获得每个col元素对应的isDefault的均值的映射关系# temp_dict = data_train.groupby([col])['isDefault'].agg(['mean']).reset_index().rename(columns={'mean': col + '_target_mean'})# temp_dict.index = temp_dict[col].values# temp_dict = temp_dict[col + '_target_mean'].to_dict()# 上面的代码可优化为temp_dict = data_train.groupby([col])['isDefault'].agg('mean').to_dict()data_train[col + '_target_mean'] = data_train[col].map(temp_dict)data_test_a[col + '_target_mean'] = data_test_a[col].map(temp_dict)# 其他衍生变量 mean 和 std
for df in [data_train, data_test_a]:for item in ['n0','n1','n2','n3','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']:df['grade_to_mean_' + item] = df['grade'] / df.groupby([item])['grade'].transform('mean')df['grade_to_std_' + item] = df['grade'] / df.groupby([item])['grade'].transform('std')
分时间就会从 O(n) 增加到 O(n2n^2n2),其中 n 是单一特征的数量。
# 获得grade, subGrade和isDefault的均值的映射关系,并用均值创建新的列
for col in ['grade', 'subGrade']:# temp_dict:获得每个col元素对应的isDefault的均值的映射关系# temp_dict = data_train.groupby([col])['isDefault'].agg(['mean']).reset_index().rename(columns={'mean': col + '_target_mean'})# temp_dict.index = temp_dict[col].values# temp_dict = temp_dict[col + '_target_mean'].to_dict()# 上面的代码可优化为temp_dict = data_train.groupby([col])['isDefault'].agg('mean').to_dict()data_train[col + '_target_mean'] = data_train[col].map(temp_dict)data_test_a[col + '_target_mean'] = data_test_a[col].map(temp_dict)# 其他衍生变量 mean 和 std
for df in [data_train, data_test_a]:for item in ['n0','n1','n2','n3','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']:df['grade_to_mean_' + item] = df['grade'] / df.groupby([item])['grade'].transform('mean')df['grade_to_std_' + item] = df['grade'] / df.groupby([item])['grade'].transform('std')
#label-encode:subGrade,postCode,title
# 高维类别特征需要进行转换
for col in tqdm(['employmentTitle', 'postCode', 'title','subGrade']): # tqdm用于展示进度条le = LabelEncoder()le.fit(list(data_train[col].astype(str).values) + list(data_test_a[col].astype(str).values))data_train[col] = le.transform(list(data_train[col].astype(str).values))data_test_a[col] = le.transform(list(data_test_a[col].astype(str).values))
print('Label Encoding 完成')
# 参考资料:
# Python数据预处理中的LabelEncoder与OneHotEncoder: https://blog.csdn.net/quintind/article/details/79850455
#label-encode:subGrade,postCode,title
# 高维类别特征需要进行转换
for col in tqdm(['employmentTitle', 'postCode', 'title','subGrade']): # tqdm用于展示进度条le = LabelEncoder()le.fit(list(data_train[col].astype(str).values) + list(data_test_a[col].astype(str).values))data_train[col] = le.transform(list(data_train[col].astype(str).values))data_test_a[col] = le.transform(list(data_test_a[col].astype(str).values))
print('Label Encoding 完成')
# 参考资料:
# Python数据预处理中的LabelEncoder与OneHotEncoder: https://blog.csdn.net/quintind/article/details/79850455
- 对特征做归一化,去除相关性高的特征
- 归一化目的是让训练过程更好更快的收敛,避免特征大吃小的问题
- 去除相关性是增加模型的可解释性,加快预测过程。
# 举例归一化过程
# 伪代码
# for fea in [要归一化的特征列表]:
# data[fea] = ((data[fea] - np.min(data[fea])) / (np.max(data[fea]) - np.min(data[fea])))
# 举例归一化过程
# 伪代码
# for fea in [要归一化的特征列表]:
# data[fea] = ((data[fea] - np.min(data[fea])) / (np.max(data[fea]) - np.min(data[fea])))
- 特征选择技术可以精简掉无用的特征,以降低最终模型的复杂性,它的最终目的是得到一个简约模型,在不降低预测准确率或对预测准确率影响不大的情况下提高计算速度。特征选择不是为了减少训练时间(实际上,一些技术会增加总体训练时间),而是为了减少模型评分时间。
特征选择的方法:
-
1 Filter
a. 方差选择法
b. 相关系数法(pearson 相关系数)
c. 卡方检验
d. 互信息法
-
2 Wrapper (RFE)
a. 递归特征消除法
-
3 Embedded
a. 基于惩罚项的特征选择法
b. 基于树模型的特征选择
- 基于特征间的关系进行筛选
['a', 'b'] + ['c']
"""
方差选择法
1. 方差选择法中,先要计算各个特征的方差,然后根据设定的阈值,选择方差大于阈值的特征
"""
from sklearn.feature_selection import VarianceThreshold
#其中参数threshold为方差的阈值
VarianceThreshold(threshold=3).fit_transform(train,target_train)
"""
相关系数法
1. Pearson 相关系数
皮尔森相关系数是一种最简单的,可以帮助理解特征和响应变量之间关系的方法,该方法衡量的是变量之间的线性相关性。
结果的取值区间为 [-1,1] , -1 表示完全的负相关, +1表示完全的正相关,0 表示没有线性相关。
"""
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr
#选择K个最好的特征,返回选择特征后的数据
#第一个参数为计算评估特征是否好的函数,该函数输入特征矩阵和目标向量,
#输出二元组(评分,P值)的数组,数组第i项为第i个特征的评分和P值。在此定义为计算相关系数
#参数k为选择的特征个数SelectKBest(k=5).fit_transform(train,target_train)
"""
卡方检验
1. 经典的卡方检验是用于检验自变量对因变量的相关性。 假设自变量有N种取值,因变量有M种取值,考虑自变
量等于i且因变量等于j的样本频数的观察值与期望的差距。 其统计量如下: χ2=Σ(A−T)2T,其中A为实际值,
T为理论值
2. (注:卡方只能运用在正定矩阵上,否则会报错Input X must be non-negative)
"""
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#参数k为选择的特征个数
SelectKBest(chi2, k=5).fit_transform(train,target_train)
"""
互信息法
1. 经典的互信息也是评价自变量对因变量的相关性的。 在feature_selection库的SelectKBest类结合最大信息系数
法可以用于选择特征,相关代码如下:
"""
from sklearn.feature_selection import SelectKBest
from minepy import MINE
#由于MINE的设计不是函数式的,定义mic方法将其为函数式的,
#返回一个二元组,二元组的第2项设置成固定的P值0.5
def mic(x, y):m = MINE()m.compute_score(x, y)return (m.mic(), 0.5)
#参数k为选择的特征个数
SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T, k=2).fit_transform(train,target_train)
['a', 'b'] + ['c']
"""
方差选择法
1. 方差选择法中,先要计算各个特征的方差,然后根据设定的阈值,选择方差大于阈值的特征
"""
from sklearn.feature_selection import VarianceThreshold
#其中参数threshold为方差的阈值
VarianceThreshold(threshold=3).fit_transform(train,target_train)
"""
相关系数法
1. Pearson 相关系数
皮尔森相关系数是一种最简单的,可以帮助理解特征和响应变量之间关系的方法,该方法衡量的是变量之间的线性相关性。
结果的取值区间为 [-1,1] , -1 表示完全的负相关, +1表示完全的正相关,0 表示没有线性相关。
"""
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr
#选择K个最好的特征,返回选择特征后的数据
#第一个参数为计算评估特征是否好的函数,该函数输入特征矩阵和目标向量,
#输出二元组(评分,P值)的数组,数组第i项为第i个特征的评分和P值。在此定义为计算相关系数
#参数k为选择的特征个数SelectKBest(k=5).fit_transform(train,target_train)
"""
卡方检验
1. 经典的卡方检验是用于检验自变量对因变量的相关性。 假设自变量有N种取值,因变量有M种取值,考虑自变
量等于i且因变量等于j的样本频数的观察值与期望的差距。 其统计量如下: χ2=Σ(A−T)2T,其中A为实际值,
T为理论值
2. (注:卡方只能运用在正定矩阵上,否则会报错Input X must be non-negative)
"""
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#参数k为选择的特征个数
SelectKBest(chi2, k=5).fit_transform(train,target_train)
"""
互信息法
1. 经典的互信息也是评价自变量对因变量的相关性的。 在feature_selection库的SelectKBest类结合最大信息系数
法可以用于选择特征,相关代码如下:
"""
from sklearn.feature_selection import SelectKBest
from minepy import MINE
#由于MINE的设计不是函数式的,定义mic方法将其为函数式的,
#返回一个二元组,二元组的第2项设置成固定的P值0.5
def mic(x, y):m = MINE()m.compute_score(x, y)return (m.mic(), 0.5)
#参数k为选择的特征个数
SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T, k=2).fit_transform(train,target_train)
"""
1. 递归特征消除法 递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,
再基于新的特征集进行下一轮训练。 在feature_selection库的RFE类可以用于选择特征,相关代码如下(以逻辑
回归为例):
"""
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数
RFE(estimator=LogisticRegression(),
n_features_to_select=2).fit_transform(train,target_train)
"""
1. 递归特征消除法 递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,
再基于新的特征集进行下一轮训练。 在feature_selection库的RFE类可以用于选择特征,相关代码如下(以逻辑
回归为例):
"""
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数
RFE(estimator=LogisticRegression(),
n_features_to_select=2).fit_transform(train,target_train)
"""
1. 基于惩罚项的特征选择法 使用带惩罚项的基模型,除了筛选出特征外,同时也进行了降维。 在
feature_selection库的SelectFromModel类结合逻辑回归模型可以用于选择特征,相关代码如下:
"""
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
#带L1惩罚项的逻辑回归作为基模型的特征选择
SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(train,target_train)
"""
基于树模型的特征选择 树模型中GBDT也可用来作为基模型进行特征选择。 在feature_selection库的
SelectFromModel类结合GBDT模型可以用于选择特征,相关代码如下:
"""
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
#GBDT作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(train,target_train)
"""
1. 基于惩罚项的特征选择法 使用带惩罚项的基模型,除了筛选出特征外,同时也进行了降维。 在
feature_selection库的SelectFromModel类结合逻辑回归模型可以用于选择特征,相关代码如下:
"""
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
#带L1惩罚项的逻辑回归作为基模型的特征选择
SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(train,target_train)
"""
基于树模型的特征选择 树模型中GBDT也可用来作为基模型进行特征选择。 在feature_selection库的
SelectFromModel类结合GBDT模型可以用于选择特征,相关代码如下:
"""
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
#GBDT作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(train,target_train)
本数据集中我们删除非入模特征后,并对缺失值填充,然后用计算协方差的方式看一下特征间相关性,然后进行模型训练
# 删除不需要的数据
for data in [data_train, data_test_a]:data.drop(['issueDate','id'], axis=1,inplace=True)# 略
特征工程是机器学习,甚至是深度学习中最为重要的一部分,在实际应用中往往也是所花费时间最多的一步。各
种算法书中对特征工程部分的讲解往往少得可怜,因为特征工程和具体的数据结合的太紧密,很难系统地覆盖所
有场景。本章主要是通过一些常用的方法来做介绍,例如缺失值异常值的处理方法详细对任何数据集来说都是适
用的。但对于分箱等操作本章给出了具体的几种思路,需要读者自己探索。在特征工程中比赛和具体的应用还是
有所不同的,在实际的金融风控评分卡制作过程中,由于强调特征的可解释性,特征分箱尤其重要。学有余力同
学可以自行多尝试,希望大家在本节学习中有所收获。
-
学习在金融分控领域常用的机器学习模型
-
学习机器学习模型的建模过程与调参流程
-
完成相应学习打卡任务
学习在金融分控领域常用的机器学习模型
学习机器学习模型的建模过程与调参流程
完成相应学习打卡任务
https://blog.csdn.net/han_xiaoyang/article/details/49123419
https://blog.csdn.net/c406495762/article/details/76262487
https://zhuanlan.zhihu.com/p/45145899
https://blog.csdn.net/wuzhongqiang/article/details/104854890
https://blog.csdn.net/wuzhongqiang/article/details/105350579
https://mp.weixin.qq.com/s/xloTLr5NJBgBspMQtxPoFA
RNN:https://zhuanlan.zhihu.com/p/45289691
LSTM:https://zhuanlan.zhihu.com/p/83496936
《机器学习》 https://book.douban.com/subject/26708119/
《统计学习方法》 https://book.douban.com/subject/10590856/
《面向机器学习的特征工程》 https://book.douban.com/subject/26826639/
《信用评分模型技术与应用》https://book.douban.com/subject/1488075/
《数据化风控》https://book.douban.com/subject/30282558/
优缺点见pdf
优缺点见pdf
数据集划分总结
- 对于数据量充足的时候,通常采用留出法或者k折交叉验证法来进行训练/测试集的划分;
- 对于数据集小且难以有效划分训练/测试集时使用自助法;
- 对于数据集小且可有效划分的时候最好使用留一法来进行划分,因为这种方法最为准确
AUC
import pandas as pd
import numpy as np
import warnings
import os
import seaborn as sns
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
"""
sns 相关设置
"""
# 声明使用 Seaborn 样式
sns.set()
# 有五种seaborn的绘图风格,它们分别是:darkgrid, whitegrid, dark, white, ticks。默认的主题是darkgrid。
sns.set_style("whitegrid")
# 有四个预置的环境,按大小从小到大排列分别为:paper, notebook, talk, poster。其中,notebook是默认的。
sns.set_context('talk')
# 中文字体设置-黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决保存图像是负号'-'显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 解决Seaborn中文显示问题并调整字体大小
sns.set(font='SimHei')
import pandas as pd
import numpy as np
import warnings
import os
import seaborn as sns
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
"""
sns 相关设置
"""
# 声明使用 Seaborn 样式
sns.set()
# 有五种seaborn的绘图风格,它们分别是:darkgrid, whitegrid, dark, white, ticks。默认的主题是darkgrid。
sns.set_style("whitegrid")
# 有四个预置的环境,按大小从小到大排列分别为:paper, notebook, talk, poster。其中,notebook是默认的。
sns.set_context('talk')
# 中文字体设置-黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决保存图像是负号'-'显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 解决Seaborn中文显示问题并调整字体大小
sns.set(font='SimHei')
# reduce_mem_usage 函数通过调整数据类型,帮助我们减少数据在内存中占用的空间
def reduce_mem_usage(df):start_mem = df.memory_usage().sum()/1024/1024print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))for col in df.columns:col_type = df[col].dtype# 非object类型的对象(数值类型对象)if col_type != object:c_min = df[col].min()c_max = df[col].max()if str(col_type)[:3] == 'int':if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:df[col] = df[col].astype(np.int8)elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:df[col] = df[col].astype(np.int16)elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:df[col] = df[col].astype(np.int32)elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:df[col] = df[col].astype(np.int64)else:if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:df[col] = df[col].astype(np.float16)elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:df[col] = df[col].astype(np.float32)else:df[col] = df[col].astype(np.float64)else:df[col] = df[col].astype('category')end_mem = df.memory_usage().sum()/1024/1024print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))return df# 读取数据
train = pd.read_csv('./train.csv')
train = reduce_mem_usage(train)
test = pd.read_csv('./testA.csv')
test = reduce_mem_usage(test)
# reduce_mem_usage 函数通过调整数据类型,帮助我们减少数据在内存中占用的空间
def reduce_mem_usage(df):start_mem = df.memory_usage().sum()/1024/1024print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))for col in df.columns:col_type = df[col].dtype# 非object类型的对象(数值类型对象)if col_type != object:c_min = df[col].min()c_max = df[col].max()if str(col_type)[:3] == 'int':if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:df[col] = df[col].astype(np.int8)elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:df[col] = df[col].astype(np.int16)elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:df[col] = df[col].astype(np.int32)elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:df[col] = df[col].astype(np.int64)else:if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:df[col] = df[col].astype(np.float16)elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:df[col] = df[col].astype(np.float32)else:df[col] = df[col].astype(np.float64)else:df[col] = df[col].astype('category')end_mem = df.memory_usage().sum()/1024/1024print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))return df# 读取数据
train = pd.read_csv('./train.csv')
train = reduce_mem_usage(train)
test = pd.read_csv('./testA.csv')
test = reduce_mem_usage(test)
Tips1:金融风控的实际项目多涉及到信用评分,因此需要模型特征具有较好的可解释性,所以目前在实际项目
中多还是以逻辑回归作为基础模型。但是在比赛中以得分高低为准,不需要严谨的可解释性,所以大多基于集
成算法进行建模。
Tips2:因为逻辑回归的算法特性,需要提前对异常值、缺失值数据进行处理【参考task3部分】
Tips3:基于树模型的算法特性,异常值、缺失值处理可以跳过,但是对于业务较为了解的同学也可以自己对缺
失异常值进行处理,效果可能会更优于模型处理的结果。
注:以下建模的源数据参考baseline进行了相应的特征工程,对于异常缺失值未进行相应的处理操作。
# 建模之前的预操作
from sklearn.model_selection import KFold
# 分离数据集,方便进行交叉验证
X_train = train.drop(['id', 'issueDate', 'isDefault'], axis=1)
X_test = test.drop(['id', 'issueDate'], axis=1)
y_train = train.loc[:, 'isDefault']
# 使用Lightgbm进行建模
from sklearn.model_selection import train_test_split
import lightgbm as lgb
# 数据集划分
X_train_split, X_val, y_train_split, y_val = train_test_split(X_train, y_train, test_size=0.2) # 训练集划分为训练集和验证集
train_matrix = lgb.Dataset(X_train_split, label=y_train_split)
valid_matrix = lgb.Dataset(X_val, label=y_val)params = {'boosting_type': 'gbdt', # gbdt'objective': 'binary', # 二分类'learning_rate': 0.1, # 学习率'metric': 'auc', # 评价指标'min_child_weight': 1e-3,'num_leaves': 31, # 叶子节点数量'max_depth': -1,'reg_lambda': 0, # 目标函数lambda'reg_alpha': 0, # 目标函数alpha'feature_fraction': 1, # 建树的特征选择比例'bagging_fraction': 1, # 建树的样本采样比例'bagging_freq': 0, # k 意味着每 k 次迭代执行bagging'seed': 2020,'nthread': 8,
# 'silent': True,'verbose': -1, # <0 显示致命的, =0 显示错误 (警告), >0 显示信息
} """使用训练集数据进行模型训练"""
model = lgb.train(params, train_set=train_matrix, valid_sets=valid_matrix, \num_boost_round=20000, verbose_eval=1000, early_stopping_rounds=200) # verbose_eval是打印log的间隔
# 对验证集进行预测
from sklearn import metrics
from sklearn.metrics import roc_auc_score"""预测并计算roc的相关指标"""
val_pre_lgb = model.predict(X_val, num_iteration=model.best_iteration) # val的预测值
fpr, tpr, threshold = metrics.roc_curve(y_val, val_pre_lgb) # 绘制roc曲线
roc_auc = metrics.auc(fpr, tpr)
print('未调参前lightgbm单模型在验证集上的AUC:{}'.format(roc_auc))
"""画出roc曲线图"""
plt.figure(figsize=(8, 8))
plt.title('Validation ROC')
plt.plot(fpr, tpr, 'b', label = 'Val AUC = %0.4f' % roc_auc)
plt.ylim(0,1)
plt.xlim(0,1)
plt.legend(loc='best')
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
# 画出对角线
plt.plot([0,1],[0,1],'r--')
plt.show()
# 更进一步的,使用5折交叉验证进行模型性能评估
"""使用lightgbm 5折交叉验证进行建模预测"""
cv_scores = []# 5折交叉验证
folds = 5
seed = 2020
kf = KFold(n_splits=folds, shuffle=True, random_state=seed)for i, (train_index, valid_index) in enumerate(kf.split(X_train, y_train)):print('************************************ {} \************************************'.format(str(i+1)))X_train_split, y_train_split, X_val, y_val = X_train.iloc[train_index], \y_train[train_index], X_train.iloc[valid_index], y_train[valid_index]train_matrix = lgb.Dataset(X_train_split, label=y_train_split)valid_matrix = lgb.Dataset(X_val, label=y_val)params = {'boosting_type': 'gbdt','objective': 'binary','learning_rate': 0.1,'metric': 'auc','min_child_weight': 1e-3,'num_leaves': 31,'max_depth': -1,'reg_lambda': 0,'reg_alpha': 0,'feature_fraction': 1,'bagging_fraction': 1,'bagging_freq': 0,'seed': 2020,'nthread': 8,'verbose': -1,}model = lgb.train(params, train_set=train_matrix, num_boost_round=20000, \valid_sets=valid_matrix, verbose_eval=1000, early_stopping_rounds=200)val_pred = model.predict(X_val, num_iteration=model.best_iteration)cv_scores.append(roc_auc_score(y_val, val_pred))print("lgb_scotrainre_list:{}".format(cv_scores))
print("lgb_score_mean:{}".format(np.mean(cv_scores)))
print("lgb_score_std:{}".format(np.std(cv_scores)))
- 贪心调参
先使用当前对模型影响最大的参数进行调优,达到当前参数下的模型最优化,再使用对模型影响次之的参数进行调优,如此下去,直到所有的参数调整完毕。
这个方法的缺点就是可能会调到局部最优而不是全局最优,但是只需要一步一步的进行参数最优化调试即可,容易理解。
需要注意的是在树模型中参数调整的顺序,也就是各个参数对模型的影响程度,这里列举一下日常调参过程中常用的参数和调参顺序:
- ①:max_depth、num_leaves
- ②:min_data_in_leaf、min_child_weight
- ③:bagging_fraction、 feature_fraction、bagging_freq
- ④:reg_lambda、reg_alpha
- ⑤:min_split_gain
from sklearn.model_selection import cross_val_score# 调objective
best_obj = dict()
for obj in objective:model = LGBMRegressor(objective=obj)"""预测并计算roc的相关指标"""score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()best_obj[obj] = score# num_leaves
best_leaves = dict()
for leaves in num_leaves:
model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0],
num_leaves=leaves)
"""预测并计算roc的相关指标"""
score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()
best_leaves[leaves] = score# max_depth
best_depth = dict()
for depth in max_depth:
model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0],
num_leaves=min(best_leaves.items(), key=lambda x:x[1])[0],
max_depth=depth)
"""预测并计算roc的相关指标"""
score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()
best_depth[depth] = score"""
可依次将模型的参数通过上面的方式进行调整优化,并且通过可视化观察在每一个最优参数下模型的得分情况
"""
查看全文
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/2279046.html
如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!
[学习笔记]金融风控实战
参考资料: 零基础入门金融风控-贷款违约预测
导包
import pandas as pd
import matplotlib.pyplot as plt# 读取数据
train pd.read_csv(train.csv)
testA pd.read_csv(testA.csv)
print(Train data shape:, train.shape)
print(testA data shape:, testA.shape……
【C++】vector的使用 以及 迭代器失效问题
前言 经过前面string的学习,我们已经掌握了许多string的类函数,vector中许多类函数与string中的类函数使用起来相似,例如迭代器的使用在所有的容器中使用都一样,这里我们不再介绍,下面我们学习一些vector类的一些常用的……
一文吃透Http协议
Http 协议
1. 初始 Http
Http 协议 , 是应用层最为广泛使用的协议 , Http 就是浏览器和服务器之间的桥梁. Http 是基于 TCP 协议实现的 , 通常我们输入搜索框中的网址 (URL) , 浏览器就会根据这个 URL 构造出一个 Http 请求 , 发送给服务器. 服务器就会返回一个 Http 响应(包……
观察者模式及其应用(多种语言实现)
软件设计模式发布-订阅模式 及其应用作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263?spm1001.2101.3001.5343 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/de……
【软考备战·希赛网每日一练】2023年4月11日
文章目录一、今日成绩二、错题总结第一题第二题第三题第四题第五题三、知识查缺题目及解析来源:2023年04月11日软件设计师每日一练 一、今日成绩 二、错题总结
第一题 解析: 策略模式:定义一系列算法,把它们一个个封装起来&#……
工程机械车辆环保数据监控平台
软件架构文档
版本 <1.1> 目的
本文档将从架构方面对系统进行综合概述,其中会使用多种不同的架构视图来描述系统的各个方面。它用于记录并表述已对系统的架构方面作出的重要决策。
架构目标和约束
系统在开发过程中有如下设计约束:开发语言为J……
[ 汇编语言 (一) ] —— 踩着硬件的鼓点,掌握计算机的精髓
🙌秋名山码民的主页 😂一个打过一年半的oier,写过一年多的Java,现在致力于学习iot应用的普通本科生 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 🙏作者水平有限,如发现……
对目前的人工智能的一些思考
一个标题
也许写得过于沉重和严肃了吧w感觉这个活动蛮有趣的 近期AI成为热点话题, GPT, new bing, bard,AI 绘画等 AI 编程工具引发大量讨论。请结合自身学习与工作经历,一起来聊聊你对AI技术以及其今后发展的看法吧。……
十种方式|||程序员业余时间副业技术变现赚钱的10个私藏方法,悄悄告诉你!
今天说一下 程序员技术人是 如何快速的能够实现财富自由 就是让大家了解一下这一块的知识 说一个简单的案例 就是如果你开发一套代码 这个代码的话一套卖5万块钱 那么你一年的话你卖十套的话 就是50万 这个其实都是税后收入 有没有后期的话都不需要什么成本了 在13-15年的时候的……
代码随想录算法训练营第五十三天 | 1143. 最长公共子序列、1035. 不相交的线、53. 最大子数组和
1143. 最长公共子序列
动规五部曲
1、确定dp数组(dp table)以及下标的含义
dp[i][j]:长度为[0, i – 1]的字符串text1与长度为[0, j – 1]的字符串text2的最长公共子序列为dp[i][j]
2、确定递推公式
主要就是两大情况: text1[i……
反序列化渗透与攻防(五)之shiro反序列化漏洞
Shiro反序列化漏洞
Shiro介绍
Apache Shiro是一款开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性
Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使用Shiro的默……
vue2+vue3
vue2vue3尚硅谷vue2vue2 课程简介【02:24】vue2 Vue简介【17:59】vue2 Vue官网使用指南【14:07】vue2 搭建Vue开发环境【13:54】vue2 Hello小案例【22:25】了解: 不常用常用:id 更常用 简单class差值总结vue 实例vue 模板 : 先 取 ࿰……
【hello Linux】环境变量
目录 1. 环境变量的概念 2. 常见的环境变量 3. 查看环境变量 4. 和环境变量相关的命令 5. 环境变量的组织方式 6. 通过代码获取环境变量 7. 通过系统调用获取环境变量 Linux🌷 在开始今天的内容之前,先来看一幅图片吧! 不知道你们是否和我一……
【Linux基础】常用命令整理
ls命令
-a选项,可以展示隐藏的文件和文件夹-l选项,以列表形式展示内容-h,需要和-l搭配使用,可以展示文件的大小单位ls -lah等同于la -a -l -h
cd命令(change directory)
语法:cd [Linux路径]……
客快物流大数据项目(一百一十二):初识Spring Cloud
文章目录
初识Spring Cloud
一、Spring Cloud简介
二、SpringCloud 基础架构图…
C和C++中的struct有什么区别
区别一: C语言中: Struct是用户自定义数据类型(UDT)。 C语言中: Struct是抽象数据类型(ADT),支持成员函数的定义。
区别二:
C中的struct是没有权限设置的,……
docker的数据卷详解
数据卷 数据卷是宿主机中的一个目录或文件,当容器目录和数据卷目录绑定后,对方修改会立即同步
一个数据卷可以同时被多个容器同时挂载,一个容器也可以被挂载多个数据卷
数据卷作用:容器数据持久化 /外部机器和容器间接通信 /容器……
13、Qt生成dll-QLibrary方式使用
Qt创建dll,使用QLibrary类方式调用dll
一、创建项目
1、新建项目->其他项目->Empty qmake Project->Choose 2、输入项目名,选择项目位置,下一步 3、选择MinGW,下一步 4、完成 5、.pro中添加TEMPLATE subdirsÿ……
基于mapreduce 的 minHash 矩阵压缩
Minhash作用: 对大矩阵进行降维处理,在进行计算俩个用户之间的相似度。
比如: 俩个用户手机下载的APP的相似度,在一个矩阵中会有很多很多的用户要比较没俩个用户之间的相似度是一个很大的计算任务 如果首先对这个矩阵降维处理&am……
关于hashmap使用迭代器的问题
keySet获得的只是key值的集合,valueSet获得的是value集合,entryset获得的是键值对的集合。 package com.test2.test;import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;public class mapiterator……
编程日记2023/4/16 14:50:37














发表回复