金融风控训练营-Task03学习笔记
金融风控训练营-Task03
第一部分 学习知识点概述
第二部分 学习内容
第一部分 包括以下三个主要模块
第二个模块 准备工作
第三个模块 数据预处理
第三部分 包含以下三个关键环节
第三部分的第一环节 缺失值填充
第三部分的第二环节 时间格式转换
第三部分的第三环节 类别特征提取
* 4、异常值处理
* * 4.1 基于3segama原则(均方差)
* 4.2 基于箱型图
* 5、数据分箱
* * 5.1 固定宽度分享
* 5.2 分位数分箱
-
- 特征交互
-
- 特征编码
-
- 特征选择
-
-
8.1 Filter: 基于特征间关系的筛选方法
-
- 8.1.1 方差选择法:基于样本方差的特征筛选方法
-
- 8.1.2 相关系数法:基于皮尔逊相关系数的特征筛选方法
-
- 8.1.3 卡方检验:基于卡方统计量的独立性检验方法
-
-
8.1.4 互信息法:基于信息论中互信息度量的特征重要性评估方法
-
8.2 Wrapper (Recursive Feature Elimination)
-
- 递归特征消除法
-
8.3 Embedded
-
- 8.3.1 基于惩罚项的特征选择法
-
- 8.3.2 基于树模型的特征选择
-
-
三、学习问题与解答
-
四、学习思考与总结
-
这份学习笔记源自阿里云天池龙珠计划金融风控训练营的学习内容
一、学习知识点概要
学习目标
数据预处理
异常值处理
数据分箱
特征交互
特征编码
特征选择
二、学习内容
1、学习目标
- 掌握并应用特征预处理的方法
- 探索并运用特征交互技术
- 研究并采用相应的缺失值解决方案
- 了解如何处理异常值问题以及选择合适的处理策略
- 学习数据分桶技术以改善模型性能
- 掌握并应用特征编码方法
- 选择合适的编码策略以提高模型效果
2、准备工作
- 导包
#提前安装的包
!pip install catboost --user
#导包
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
import warnings
from sklearn.model_selection import StratifiedKFold, KFold
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss
warnings.filterwarnings('ignore')
#读取
data_train =pd.read_csv('train.csv')
data_test_a = pd.read_csv('testA.csv')
3、数据预处理
首先查找出对象特征和数值特征
numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns)
category_fea = list(filter(lambda x: x not in numerical_fea,list(data_train.columns)))
label = 'isDefault'
numerical_fea.remove(label)
3.1 缺失值的填充
- 查看缺失值情况---->填充缺失值---->再次查看缺失值情况
#查看缺失值情况
data_train.isnull().sum()
#按照平均数填充数值型特征
data_train[numerical_fea] = data_train[numerical_fea].fillna(data_train[numerical_fea].median())
data_test_a[numerical_fea] = data_test_a[numerical_fea].fillna(data_train[numerical_fea].median())
#按照众数填充类别型特征
data_train[category_fea] = data_train[category_fea].fillna(data_train[category_fea].mode())
data_test_a[category_fea] = data_test_a[category_fea].fillna(data_train[category_fea].mode())
#缺失值替换
#把所有缺失值替换为指定的值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)
#查看类别特征
category_fea #对象型类别特征需要进行预处理,其中['issueDate']为时间格式特征
#['grade', 'subGrade', 'employmentLength', 'issueDate', 'earliesCreditLine']
3.2 时间格式处理
#转化成时间格式
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 s
else:
return np.int8(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:]))
3.3 对象类型特征
# 部分类别特征
cate_features = ['grade', 'subGrade', 'employmentTitle', 'homeOwnership', 'verificationStatus', 'purpose', 'postCode', 'regionCode', \
'applicationType', 'initialListStatus', 'title', 'policyCode']
for f in cate_features:
print(f, '类型数:', data[f].nunique())

有优先级的可以labelencode或者自映射
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之上,又不是高维稀疏的,且纯分类特征
for data in [data_train, data_test_a]:
data = pd.get_dummies(data, columns=['subGrade', 'homeOwnership', 'verificationStatus', 'purpose', 'regionCode'], drop_first=True)
4、异常值处理
思路:
- 首先识别出所有可能存在的异常值,并分析导致这些数值偏差的具体原因之后,在决定如何应对这些情况时,请特别注意区分两种情况:
- 其一为这些数值偏差可能源于随机事件或不属于当前研究领域;其二则可能反映出了某种潜在的真实现象。
- 在现有欺诈场景下经常出现的数据是与正常情况相比更为离群的数据点;我们需要将这些离群点包含在内并进行重新建模;从而揭示其内在规律。
- 对于无法使用监督学习的情况,则可以尝试采用基于聚类分析的方法。
- 测试数据必须保留不可替代的地位。
检测异常的方法↓
4.1 基于3segama原则(均方差)
若某数据集大致遵循正态分布,则其中约68%的数据点通常位于均值的一个标准差区间内;约95%的数据点一般会落在均值的两个标准差区间内;而约99.7%的数据点则通常位于均值的三个标准差区间内。
def find_outliers_by_3segama(data,fea):
data_std = np.std(data[fea])
data_mean = np.mean(data[fea])
outliers_cut_off = data_std
lower_rule = data_mean - outliers_cut_off
upper_rule = data_mean + outliers_cut_off
data[fea+'_outliers'] = data[fea].apply(lambda x:str('异常值') if x > upper_rule or x < lower_rule else '正常值')
return data
#得到特征的异常值后可以进一步分析变量异常值和目标变量的关系
data_train = data_train.copy()
for fea in numerical_fea:
data_train = find_outliers_by_3segama(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)
4.2 基于箱型图
主要将数据划分为三个关键点及四个区间段;其计算方法为IQR等于Q3减去Q1;下界线的位置则确定为Q₁减去IQR的1.5倍;上界线的位置则确定为Q₃加上IQR的1.5倍
5、数据分箱
-
特征分箱的目的
在提升模型效果方面,特征分箱的主要目的是降低变量的复杂度,并有效减少变量带来的噪音对模型的影响。通过这种方式,在提高自变量与因变量之间的关联程度的同时,能够使模型的稳定性得到增强。- 数据分桶的对象
将连续变量离散化
将多状态的离散变量合并成少状态
- 数据分桶的对象
-
分箱的目的
由于数据特征中的变量取值范围往往较大,在有监督学习与无监督学习中(例如k-均值聚类),采用欧氏距离这一指标来衡量数据点之间的相似性时都会导致"长尾"现象的存在。为了缓解这一问题,在统计学领域提出了一种解决方案:即将计数型变量进行区间划分或分组处理称为数据分箱法(亦称作数据分箱)。进而采用量化后的数值进行分析处理。 -
分箱的优点
应对缺失数据:当数据源可能出现缺失值时,可以将null单独设置为一个分箱。
在数据中出现离群点时,则可通过分箱将其离散化处理以增强变量的鲁棒性(抗干扰能力)。例如,在年龄字段中出现200这样的异常值时,则将其归入‘age > 60’这个分箱。
在实际应用中发现x随数值增大y也单调递增的方式较为常见。然而,在真实世界中x与y之间的关系往往并非线性而是非线性的,在这种情况下可以通过WOE变换进行建模分析。 -
分箱的原则
(1)最低比例不低于5%
(2)各箱子内部不应全是优质客户
(3)各相邻箱子的分布趋势应保持一致性
5.1 固定宽度分享
当数值分布跨越多个数量级时,则推荐采用以10为基数的幂次进行分组:即分为[0, 1), [1, 10), [10, 100), [1e2, 1e3),依此类推等区间段落;这种固定宽度区间分箱的操作相对简便;然而需要注意的是,在数据分布中若出现较大的空缺,则会导致大量空余区间出现
# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是loanAmnt/1000
data['loanAmnt_bin1'] = np.floor_divide(data['loanAmnt'], 1000)
## 通过对数函数映射到指数宽度分箱
data['loanAmnt_bin2'] = np.floor(np.log10(data['loanAmnt']))
5.2 分位数分箱
data['loanAmnt_bin3'] = pd.qcut(data['loanAmnt'], 10, labels=False)
6、特征交互
- 构造相对简便
- 当线性模型中包含交叉式特征时,其训练时间和评估时间将从O(n)提升至O(n²),其中n表示单个特征的数量。
for col in ['grade', 'subGrade']:
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()
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','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')
7、特征编码
- labelEncode 直接放入树模型中
#label-encode:subGrade,postCode,title
# 高维类别特征需要进行转换
for col in tqdm(['employmentTitle', 'postCode', 'title','subGrade']):
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 完成')
- 对用于逻辑回归等模型的特征进行必要的工程处理
- 对特征实施归一化处理,并剔除与之高度相关的特征
- 归一化目的是让训练过程更加高效地收敛,并防止因尺寸差异导致的影响
- 通过去除高度相关的变量来提升模型解释性和预测效率。
# 举例归一化过程
#伪代码
for fea in [要归一化的特征列表]:
data[fea] = ((data[fea] - np.min(data[fea])) / (np.max(data[fea]) - np.min(data[fea])))
8、特征选择
- 特征选择的技术可以通过剔除冗余的特征来精炼模型结构 * ,从而降低整体复杂度并获得更为紧凑的有效模型 。 * 特征选择的方法论: *
8.1 Filter------基于特征间的关系进行筛选
8.1.1 方差选择法
- 计算各个特征的方差---->根据设定的阈值,选择方差大于阈值的特征
from sklearn.feature_selection import VarianceThreshold
#其中参数threshold为方差的阈值
VarianceThreshold(threshold=3).fit_transform(train,target_train)
8.1.2 相关系数法(pearson 相关系数)
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)
8.1.3 卡方检验
-
传统的卡方检验用于评估自变量与因变量之间的关联程度。
-
基于假设情况下,在一个分类模型中存在多个类别时,
-
分析样本频数观察值与其理论期望值之间的差异,
-
计算公式如下:
χ² = ∑(A - T)² / T(其中A代表实际观察频数,
而T代表理论期望频数)- 卡方只能运用在正定矩阵上,否则会报错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)
8.1.4 互信息法
一种常用MutualInformation指标也被广泛应用于衡量自变量对因变量的影响程度。在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)
8.2 Wrapper (Recursive Feature Elimination)
递归特征消除法
通过迭代使用基础模型完成多轮训练,在每个阶段结束后删除若干权重系数对应的特征,并利用更新后的特征集合继续进行后续训练;在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)
8.3 Embedded
8.3.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)
8.3.2 基于树模型的特征选择
在树模型中使用GBDT也可以作为基模型来进行特征选择。 在feature_selection库中的SelectFromModel类结合GBDT模型可用于选择特征,并附有相关代码如下:
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
#GBDT作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(train,target_train)
三、学习问题与解答
最初遇到了一个问题,在按照提示进行升级后问题仍未解决。后来不知为何突然解决了(???)

……
四、学习思考与总结
任务03基于对数据的深入了解以及对特征工程的深入学习。这一部分涉及的特征工程与具体的数据紧密结合,并且占据了一个非常重要的位置。其中模型性能的好坏直接与所选择的特征质量密切相关;因此,在这一部分的学习中需要深入理解并切实掌握相关的知识。
