金融风险预测赛-task3-特征工程
比赛地址:https://tianchi.aliyun.com/competition/entrance/531830/introduction
1 主要内容
- 数据预处理
- 缺失值的填充
- 时间格式处理
- 对象类型特征转换到数值
- 异常值处理
- 基于3segama原则
- 基于箱型图
- 数据分箱
- 固定宽度分箱
- 分位数分箱
- 离散数值型数据分箱
- 连续数值型数据分箱
- 卡方分箱(选做作业)
- 特征交互
- 特征和特征之间组合
- 特征和特征之间衍生
- 其他特征衍生的尝试(选做作业)
- 特征编码
- one-hot编码
- label-encode编码
- 特征选择
- 1 Filter
- 2 Wrapper (RFE)
- 3 Embedded
2 数据预处理
导入包并读取数据
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('data/train.csv')
data_test_a = pd.read_csv('data/testA.csv')
特征预处理
数据EDA部分我们已经对数据的大概和某些特征分布有了了解,数据预处理部分一般我们要处理一些EDA阶段分析出来的问题,这里介绍了数据缺失值的填充,时间格式特征的转化处理,某些对象类别特征的处理。
首先我们查找出数据中的对象特征和数值特征
# numerical_fea 是一个列表,包含 data_train 中所有非 object 类型的列名(即数值型列) numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns) # 使用 filter 函数和 lambda 表达式来筛选出那些不在 numerical_fea 列表中的列名。 category_fea = list(filter(lambda x: x not in numerical_fea, list(data_train.columns))) label = 'isDefault' numerical_fea.remove(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)
# 按照中位数填充数值型特征
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_test_a[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())
#查看类别特征
category_fea
['grade', 'subGrade', 'employmentLength', 'issueDate', 'earliesCreditLine']
对象型类别特征需要进行预处理,其中[‘issueDate’]为时间格式特征
时间格式处理
#转化成时间格式
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
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)

- 对earliesCreditLine进行预处理
data_train['earliesCreditLine'].sample(5)

for data in [data_train,data_test_a]:
data['earliesCreditLine'] = data['earliesCreditLine'].apply(lambda s: int(s[-4:])) #取字符串s的最后四个字符(假定这四个字符代表年份),并将其转换为整数。
类别特征处理
# 部分类别特征
cate_features = ['grade', 'subGrade', 'employmentTitle', 'homeOwnership', 'verificationStatus', 'purpose', 'postCode', 'regionCode', \
'applicationType', 'initialListStatus', 'title', 'policyCode']
for f in cate_features:
print(f, '类型数:', data[f].nunique())
grade 类型数: 7
subGrade 类型数: 35
employmentTitle 类型数: 79282
homeOwnership 类型数: 6
verificationStatus 类型数: 3
purpose 类型数: 14
postCode 类型数: 889
regionCode 类型数: 51
applicationType 类型数: 2
initialListStatus 类型数: 2
title 类型数: 12058
policyCode 类型数: 1
像等级这种类别特征,是有优先级的可以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)
pd.get_dummies 是 Pandas 库中的一个函数,用于将分类变量转换为哑变量(也称为虚拟变量或指示变量)。哑变量是通过将分类变量的每个可能值转换为一个新的二进制列(0 或 1)来表示该值是否出现在原始数据中。这种方法常用于机器学习和统计建模,因为大多数算法需要数值输入而不是分类输入。(类似于独热编码)
异常值处理
- 当你发现异常值后,一定要先分清是什么原因导致的异常值,然后再考虑如何处理。首先,如果这一异常值并不代表一种规律性的,而是极其偶然的现象,或者说你并不想研究这种偶然的现象,这时可以将其删除。其次,如果异常值存在且代表了一种真实存在的现象,那就不能随便删除。在现有的欺诈场景中很多时候欺诈数据本身相对于正常数据勒说就是异常的,我们要把这些异常点纳入,重新拟合模型,研究其规律。能用监督的用监督模型,不能用的还可以考虑用异常检测的算法来做。
- 注意test的数据不能删。
检测异常的方法一:均方差
在统计学中,如果一个数据分布近似正态,那么大约 68% 的数据值会在均值的一个标准差范围内,大约 95% 会在两个标准差范围内,大约 99.7% 会在三个标准差范围内。
def find_outliers_by_3segama(data,fea):
# 计算指定列的标准差
data_std = np.std(data[fea])
# 计算指定列的均值。
data_mean = np.mean(data[fea])
# 定义了异常值的边界范围,通常为3倍标准差
outliers_cut_off = data_std * 3
# 下限,低于该值的数据被视为异常值。
lower_rule = data_mean - outliers_cut_off
# 上限,高于该值的数据被视为异常值。
upper_rule = data_mean + outliers_cut_off
# 标记异常值 如果数据点大于 upper_rule 或小于 lower_rule,则标记为 '异常值'。 否则,标记为 '正常值'
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)
id_outliers
正常值 800000
Name: count, dtype: int64
id_outliers
正常值 159610
Name: isDefault, dtype: int64
**********
loanAmnt_outliers
正常值 800000
Name: count, dtype: int64
loanAmnt_outliers
正常值 159610
Name: isDefault, dtype: int64
**********
term_outliers
正常值 800000
Name: count, dtype: int64
term_outliers
正常值 159610
Name: isDefault, dtype: int64
**********
interestRate_outliers
正常值 794259
异常值 5741
Name: count, dtype: int64
interestRate_outliers
异常值 2916
正常值 156694
Name: isDefault, dtype: int64
**********
installment_outliers
正常值 792046
异常值 7954
Name: count, dtype: int64
installment_outliers
异常值 2152
正常值 157458
Name: isDefault, dtype: int64
**********
employmentTitle_outliers
正常值 800000
Name: count, dtype: int64
employmentTitle_outliers
正常值 159610
Name: isDefault, dtype: int64
**********
homeOwnership_outliers
正常值 799701
异常值 299
Name: count, dtype: int64
homeOwnership_outliers
异常值 62
正常值 159548
Name: isDefault, dtype: int64
**********
annualIncome_outliers
正常值 793973
异常值 6027
Name: count, dtype: int64
annualIncome_outliers
异常值 756
正常值 158854
Name: isDefault, dtype: int64
**********
verificationStatus_outliers
正常值 800000
Name: count, dtype: int64
verificationStatus_outliers
正常值 159610
Name: isDefault, dtype: int64
**********
purpose_outliers
正常值 783003
异常值 16997
Name: count, dtype: int64
purpose_outliers
异常值 3635
正常值 155975
Name: isDefault, dtype: int64
**********
postCode_outliers
正常值 798931
异常值 1069
Name: count, dtype: int64
postCode_outliers
异常值 221
正常值 159389
Name: isDefault, dtype: int64
**********
regionCode_outliers
正常值 799994
异常值 6
Name: count, dtype: int64
regionCode_outliers
异常值 1
正常值 159609
Name: isDefault, dtype: int64
**********
dti_outliers
正常值 798440
异常值 1560
Name: count, dtype: int64
dti_outliers
异常值 466
正常值 159144
Name: isDefault, dtype: int64
**********
delinquency_2years_outliers
正常值 778245
异常值 21755
Name: count, dtype: int64
delinquency_2years_outliers
异常值 5089
正常值 154521
Name: isDefault, dtype: int64
**********
ficoRangeLow_outliers
正常值 788261
异常值 11739
Name: count, dtype: int64
ficoRangeLow_outliers
异常值 778
正常值 158832
Name: isDefault, dtype: int64
**********
ficoRangeHigh_outliers
正常值 788261
异常值 11739
Name: count, dtype: int64
ficoRangeHigh_outliers
异常值 778
正常值 158832
Name: isDefault, dtype: int64
**********
openAcc_outliers
正常值 790889
异常值 9111
Name: count, dtype: int64
openAcc_outliers
异常值 2195
正常值 157415
Name: isDefault, dtype: int64
**********
pubRec_outliers
正常值 792471
异常值 7529
Name: count, dtype: int64
pubRec_outliers
异常值 1701
正常值 157909
Name: isDefault, dtype: int64
**********
pubRecBankruptcies_outliers
正常值 794120
异常值 5880
Name: count, dtype: int64
pubRecBankruptcies_outliers
异常值 1423
正常值 158187
Name: isDefault, dtype: int64
**********
revolBal_outliers
正常值 790001
异常值 9999
Name: count, dtype: int64
revolBal_outliers
异常值 1359
正常值 158251
Name: isDefault, dtype: int64
**********
revolUtil_outliers
正常值 799948
异常值 52
Name: count, dtype: int64
revolUtil_outliers
异常值 23
正常值 159587
Name: isDefault, dtype: int64
**********
totalAcc_outliers
正常值 791663
异常值 8337
Name: count, dtype: int64
totalAcc_outliers
异常值 1668
正常值 157942
Name: isDefault, dtype: int64
**********
initialListStatus_outliers
正常值 800000
Name: count, dtype: int64
initialListStatus_outliers
正常值 159610
Name: isDefault, dtype: int64
**********
applicationType_outliers
正常值 784586
异常值 15414
Name: count, dtype: int64
applicationType_outliers
异常值 3875
正常值 155735
Name: isDefault, dtype: int64
**********
title_outliers
正常值 775134
异常值 24866
Name: count, dtype: int64
title_outliers
异常值 3900
正常值 155710
Name: isDefault, dtype: int64
**********
policyCode_outliers
正常值 800000
Name: count, dtype: int64
policyCode_outliers
正常值 159610
Name: isDefault, dtype: int64
**********
n0_outliers
正常值 782773
异常值 17227
Name: count, dtype: int64
n0_outliers
异常值 3485
正常值 156125
Name: isDefault, dtype: int64
**********
n1_outliers
正常值 790500
异常值 9500
Name: count, dtype: int64
n1_outliers
异常值 2491
正常值 157119
Name: isDefault, dtype: int64
**********
n2_outliers
正常值 789067
异常值 10933
Name: count, dtype: int64
n2_outliers
异常值 3205
正常值 156405
Name: isDefault, dtype: int64
**********
n3_outliers
正常值 789067
异常值 10933
Name: count, dtype: int64
n3_outliers
异常值 3205
正常值 156405
Name: isDefault, dtype: int64
**********
n4_outliers
正常值 788660
异常值 11340
Name: count, dtype: int64
n4_outliers
异常值 2476
正常值 157134
Name: isDefault, dtype: int64
**********
n5_outliers
正常值 790355
异常值 9645
Name: count, dtype: int64
n5_outliers
异常值 1858
正常值 157752
Name: isDefault, dtype: int64
**********
n6_outliers
正常值 786006
异常值 13994
Name: count, dtype: int64
n6_outliers
异常值 3182
正常值 156428
Name: isDefault, dtype: int64
**********
n7_outliers
正常值 788430
异常值 11570
Name: count, dtype: int64
n7_outliers
异常值 2746
正常值 156864
Name: isDefault, dtype: int64
**********
n8_outliers
正常值 789625
异常值 10375
Name: count, dtype: int64
n8_outliers
异常值 2131
正常值 157479
Name: isDefault, dtype: int64
**********
n9_outliers
正常值 786384
异常值 13616
Name: count, dtype: int64
n9_outliers
异常值 3953
正常值 155657
Name: isDefault, dtype: int64
**********
n10_outliers
正常值 788979
异常值 11021
Name: count, dtype: int64
n10_outliers
异常值 2639
正常值 156971
Name: isDefault, dtype: int64
**********
n11_outliers
正常值 799434
异常值 566
Name: count, dtype: int64
n11_outliers
异常值 112
正常值 159498
Name: isDefault, dtype: int64
**********
n12_outliers
正常值 797585
异常值 2415
Name: count, dtype: int64
n12_outliers
异常值 545
正常值 159065
Name: isDefault, dtype: int64
**********
n13_outliers
正常值 788907
异常值 11093
Name: count, dtype: int64
n13_outliers
异常值 2482
正常值 157128
Name: isDefault, dtype: int64
**********
n14_outliers
正常值 788884
异常值 11116
Name: count, dtype: int64
n14_outliers
异常值 3364
正常值 156246
Name: isDefault, dtype: int64
**********
例如可以看到异常值在两个变量上的分布几乎复合整体的分布,如果异常值都属于为1的用户数据里面代表什么呢? 可能意味着异常值与较高的违约率之间存在某种关联
#删除异常值
for fea in numerical_fea:
data_train = data_train[data_train[fea+'_outliers']=='正常值']
# 在每次过滤操作后,调用 reset_index(drop=True) 方法重新设置 DataFrame 的索引,并丢弃旧的索引
data_train = data_train.reset_index(drop=True)
检测异常的方法二:箱型图
- 总结一句话:四分位数会将数据分为三个点和四个区间,IQR = Q3 -Q1,下触须=Q1 − 1.5 * IQR,上触须=Q3 + 1.5 * IQR;
四分位数(Quartiles)
四分位数是将数据集分成四个等份的三个点:
- Q1(第一四分位数):表示第25百分位数,即有25%的数据小于或等于这个值。
- Q2(第二四分位数,中位数):表示第50百分位数,即有50%的数据小于或等于这个值。
- Q3(第三四分位数):表示第75百分位数,即有75%的数据小于或等于这个值。
IQR(Interquartile Range,四分位距)定义为 Q3 和 Q1 之间的差值:IQR=Q3−Q1
箱型图的上下触须
在箱型图中,通常使用以下公式来确定“触须”的范围(也就是正常值的范围):
- 下触须(Lower Whisker): 下触须=Q1−1.5×IQR下触须=Q1−1.5×IQR
- 上触须(Upper Whisker): 上触须=Q3+1.5×IQR上触须=Q3+1.5×IQR
任何低于下触须或高于上触须的数据点都被认为是异常值(离群点)
这种方法简单且直观,适用于大多数单变量异常值检测任务。如果需要处理多变量异常值检测,可能需要更复杂的算法和技术。
3 数据分箱
- 特征分箱的目的:
- 从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
- 数据分桶的对象:
- 将连续变量离散化
- 将多状态的离散变量合并成少状态
- 分箱的原因:
- 数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧氏距离作为相似度函数来测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间量化即数据分桶也叫做数据分箱,然后使用量化后的结果。
- 分箱的优点:
- 处理缺失值:当数据源可能存在缺失值,此时可以把null单独作为一个分箱。
- 处理异常值:当数据中存在离群点时,可以把其通过分箱离散化处理,从而提高变量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。
- 业务解释性:我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但实际x与y之间经常存在着非线性关系,此时可经过WOE变换。
- 特别要注意一下分箱的基本原则:
- (1)最小分箱占比不低于5%
- (2)箱内不能全部是好客户
- (3)连续箱单调
固定宽度分箱
当数值横跨多个数量级时,最好按照 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']))
等频分箱
# 分位数分箱:指将数据分成若干个区间,每个区间的样本数量相同,但区间的宽度可能不同
data['loanAmnt_bin3'] = pd.qcut(data['loanAmnt'], 10, labels=False)
卡方分箱
卡方分箱是一种基于卡方检验的分箱方法,主要用于分类问题。它通过合并相邻区间来减少区间的数量,直到满足某种停止条件(如卡方统计量小于某个阈值)。这种方法能够有效地发现特征与目标变量之间的关系。
基本步骤
- 初始化:将所有数据点放入一个初始区间。
- 计算卡方统计量:计算每个相邻区间的卡方统计量。
- 合并区间:选择卡方统计量最小的一对相邻区间进行合并。
- 重复步骤2和3:直到满足停止条件(如最大区间数或最小卡方统计量)。
卡方统计量的定义:
1. 卡方统计量的定义
卡方统计量通常用符号 $\chi ^2$ 表示
总结
不同的分箱方法适用于不同的场景:
- 等宽分箱:适用于数据分布均匀的情况,简单易实现。
- 等频分箱:适用于数据分布不均匀的情况,确保每个区间包含大致相同的样本数量。
- 自定义分箱:适用于有特定业务需求或领域知识的情况。
- 卡方分箱:适用于分类问题,基于卡方检验合并相邻区间,能够有效发现特征与目标变量之间的关系。
- 决策树分箱:基于决策树算法自动找到最佳的分箱边界,适用于需要自动化分箱的情况。
4 特征交互
特征交互(Feature Interaction) 是指在机器学习模型中,两个或多个特征共同作用对目标变量产生影响的现象。简单来说,特征交互指的是特征之间的相互关系如何影响模型的预测结果,而不是单个特征独立地影响目标变量。
- 交互特征的构造非常简单,使用起来却代价不菲。如果线性模型中包含有交互特征对,那它的训练时间和评分时间就会从 O(n) 增加到 O(n2),其中 n 是单一特征的数量。
for col in ['grade', 'subGrade']:
# 对于每一个列名('grade' 和 'subGrade')
temp_dict = data_train.groupby([col])['isDefault'].agg(['mean']).reset_index().rename(columns={'mean': col + '_target_mean'})
# 1. 使用 groupby 按照当前列('grade' 或 'subGrade')进行分组。
# 2. 计算每组中 'isDefault' 列的平均值。
# 3. 使用 reset_index() 将分组结果转换为 DataFrame。
# 4. 重命名聚合后的列名为 col + '_target_mean' (例如 'grade_target_mean' 或 'subGrade_target_mean')。
temp_dict.index = temp_dict[col].values
# 将临时 DataFrame 的索引设置为当前列的值(即 'grade' 或 'subGrade' 的值)。
temp_dict = temp_dict[col + '_target_mean'].to_dict()
# 将包含目标均值的 Series 转换为字典,键是类别值,值是对应的 'isDefault' 平均值。
data_train[col + '_target_mean'] = data_train[col].map(temp_dict)
# 使用 map 函数将训练集中的 'grade' 或 'subGrade' 列映射到其对应的 'isDefault' 平均值,并创建新列存储结果。
data_test_a[col + '_target_mean'] = data_test_a[col].map(temp_dict)
# 同样地,对测试集进行相同的映射操作,创建新列存储结果。
# 对训练集和测试集进行循环处理
for df in [data_train, data_test_a]:
# 对每个指定的列名 'n0', 'n1', ..., 'n14' 进行循环
for item in ['n0','n1','n2','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']:
# 计算 'grade' 列相对于 'item' 列分组后 'grade' 的均值,并创建新列存储结果
df['grade_to_mean_' + item] = df['grade'] / df.groupby([item])['grade'].transform('mean')
# 计算 'grade' 列相对于 'item' 列分组后 'grade' 的标准差,并创建新列存储结果
df['grade_to_std_' + item] = df['grade'] / df.groupby([item])['grade'].transform('std')
这里给出一些特征交互的思路,但特征和特征间的交互衍生出新的特征还远远不止于此,抛砖引玉,希望大家多多探索。请学习者尝试其他的特征交互方法。
5 特征编码
标签编码(Label Encoding) 是一种将类别数据转换为数值形式的技术,常用于机器学习中处理分类变量。由于大多数机器学习算法只能处理数值型数据,因此需要将类别特征转换为数值型特征。标签编码是实现这一目标的一种简单而有效的方法。
#label-encode:subGrade,postCode,title
# 高维类别特征需要进行转换
# 对指定的列进行标签编码
for col in tqdm(['employmentTitle', 'postCode', 'title','subGrade']):
# 初始化 LabelEncoder
le = LabelEncoder()
# 合并训练集和测试集中的该列数据,并转换为字符串类型
combined_data = list(data_train[col].astype(str).values) + list(data_test_a[col].astype(str).values)
# 拟合 LabelEncoder
le.fit(combined_data)
# 对训练集和测试集中的该列进行转换
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 完成')
逻辑回归等模型要单独增加的特征工程
- 对特征做归一化,去除相关性高的特征
- 归一化目的是让训练过程更好更快的收敛,避免特征大吃小的问题
- 去除相关性是增加模型的可解释性,加快预测过程。
- 除以最大值:所有特征除以各自的范围最大值,使得特征值范围都在0~1之间。于是$0.15 \leq \frac {x_1}{2000} \leq 1$、 $0 \leq \frac{x_2}{5} \leq 1$
- 均值归一化(Mean normalization):使得特征值范围大致为-1~1。假设$x_1$ 的平均值为 $\mu_1 =600、x_2的平均值为 \mu_2 = 2.3 \ ,于是-0.18 \leq \frac{x_1-\mu_1}{2000-300} \leq 0.82、-0.46 \leq \frac{x_2 - \mu_2}{5-0} \leq 0.54$
- Z-score归一化(Z-score normalization)【推荐】:使得特征值服从标准正态分布。假设$x_1$的平均值和标准差分别为$\mu_1 = 600,\sigma_1 = 450 ,x_2 $ 的平均值和标准差分别为$\mu_2 = 2.3,\sigma_2 = 1.4 $ 于是 $-0.67 \leq \frac{x_1 - \mu_1}{\sigma_1} \leq 3.1 $ , $-1.6 \leq \frac{x_2- \mu_2}{\sigma_2 } \leq 1.9 $
6 特征选择
- 特征选择技术可以精简掉无用的特征,以降低最终模型的复杂性,它的最终目的是得到一个简约模型,在不降低预测准确率或对预测准确率影响不大的情况下提高计算速度。特征选择不是为了减少训练时间(实际上,一些技术会增加总体训练时间),而是为了减少模型评分时间。
特征选择的方法:
- 1 Filter
- 方差选择法
- 相关系数法(pearson 相关系数)
- 卡方检验
- 互信息法
- 2 Wrapper (RFE)
- 递归特征消除法
- 3 Embedded
- 基于惩罚项的特征选择法
- 基于树模型的特征选择
Filter
基于特征间的关系进行筛选
方差选择法
方差选择法中,先要计算各个特征的方差,然后根据设定的阈值,选择方差大于阈值的特征
from sklearn.feature_selection import VarianceThreshold
# 使用 VarianceThreshold 进行特征选择
selector = VarianceThreshold(threshold=3)
transformed_train = selector.fit_transform(train, target_train)
相关系数法
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)
卡方检验
- 经典的卡方检验是用于检验自变量对因变量的相关性。 假设自变量有N种取值,因变量有M种取值,考虑自变量等于i且因变量等于j的样本频数的观察值与期望的差距。 其统计量如下: χ2=∑(A−T)2/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)
互信息法
经典的互信息也是评价自变量对因变量的相关性的。 在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)
Wrapper(Recursive feature elimination,RFE)
- 递归特征消除法 递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。 在feature_selection库的RFE类可以用于选择特征,相关代码如下(以逻辑回归为例):
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型:常用的基模型包括 LogisticRegression、LinearSVC、RandomForestClassifier
#参数n_features_to_select为选择的特征个数
RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(train,target_train)
RFE 的核心思想是利用模型的特征重要性评分来决定哪些特征应该被保留或移除。
Embedded
- 基于惩罚项的特征选择法 使用带惩罚项的基模型,除了筛选出特征外,同时也进行了降维。 在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)
**GradientBoostingClassifier**:这是 scikit-learn 提供的一个梯度提升决策树分类器,适用于分类任务
本数据集中我们删除非入模特征后,并对缺失值填充,然后用计算协方差的方式看一下特征间相关性,然后进行模型训练
# 删除不需要的数据
for data in [data_train, data_test_a]:
data.drop(['issueDate'], axis=1,inplace=True)
"纵向用缺失值上面的值替换缺失值"
data_train = data_train.fillna(method='ffill')