数据挖掘实践(金融风控-贷款违约预测)(三):特征工程
目录
1.引言
特征工程是机器学习领域的一个重要概念,目前并没有普遍接受的定义, 一般可以认为是为机器学习应用而设计特征集的相关工作。主要涉及两个方面的因素:
a) 了解要解决的问题和要使用的机器学习算法的优势和限制;
b) 进行实践,通过实验从而更好地掌握哪种特征更为合理,哪种特征并不符合所处理的问题或所选择的机器学习方法。
这两方面的影响因素可以是一个螺旋式迭代过程,对问题自顶而下的理解有助于实验工作的展开;同时在实验中所获得的自底而上的信息者会帮助更好地理解要解决的问题,揭示蕴涵的问题本质。
目标
• 学习特征预处理、缺失值、异常值处理、数据分桶等特征处理方法
• 学习特征交互、编码、选择的相应方法
项目地址:https://github.com/datawhalechina/team-learning-data-mining/tree/master/FinancialRiskControl
比赛地址:https://tianchi.aliyun.com/competition/entrance/531830/introduction
2.特征预处理
在比赛中数据预处理是必不可少的一部分,对于缺失值的填充往往会影响比赛的结果,在比赛中不妨尝试多种填充然后比较结果选择结果最优的一种, 比赛数据相比真实场景的数据相对要“干净”一些,但是还是会有一定的“脏”数据存在,清洗一些异常值往往会获得意想不到的效果。
2.1缺失值填充
由于很多数据分析过程不能直接处理缺失值,因此需要在预处理阶段“消除”缺失数据。常用的方法包括忽略含缺失项的记录、将缺失值视为特殊值或者插补缺失值。目前很多数据挖掘与统计分析的软件都提供了对缺失值的处理方法,例如WEKA"用最频繁值与均值替换缺失的离散变量与连续变量,而统计软件spssi1中不仅提供了缺失值的分析还给出了包括均值、中位数以及回归拟合等多种填充方法。下面对一些常用的缺失值处理方法进行简单介绍:
Listwise deletion:把含有缺失值的记录删除,尽管这种方法在MCAR 数据中能得到相对无偏的参数估计,但它降低了样本的有效大小,会增加估算误差。
Pairwise deletion:仅在需要进行配对的变量有缺失值时才将该条记录删除。这种方法虽然先比前者多保留了一些数据,但仍然不适用于MCAR之外的情况。由于不同变量的丢失率不同,其估算准确度相差也会很大。
Single imputation:最常用最简单的单一插补法有均值替换和hotdeck插补方法。前者多为设定默认值(如0)或者进行均值插补,后者考虑了属性间的依赖关系,用一组决策属性对数据进行分组,同组内在某些变量上的取值是一样的。复杂一点的统计学方法有回归方法和期望最大化算法等。而机器学习领域的单值填充法包括聚类、分类等方法。
Multiple imputation:用某一选定算法获得若干个填充值,形成若干完整数据集,然后用任意一个应用于完整数据集的方法来对它们进行分析,综合得到一个结果值。多值填充考虑了插补的不确定性,效果较单值填充好,但时间代价高。
常见的填充方法
(1)填充固定值
选取某个固定值/默认值填充缺失值。
train_data.fillna(0, inplace=True) # 填充 0
(2)填充均值
对每一列的缺失值,填充当列的均值。
train_data.fillna(train_data.mean(),inplace=True) # 填充均值
(3)填充中位数
对每一列的缺失值,填充当列的中位数。
train_data.fillna(train_data.median(),inplace=True) # 填充中位数
(4)填充众数
对每一列的缺失值,填充当列的众数。由于存在某列缺失值过多,众数为nan的情况,因此这里取的是每列删除掉nan值后的众数。
train_data.fillna(train_data.mode(),inplace=True) # 填充众数,该数据缺失太多众数出现为nan的情况
features_mode = {
}
for f in features:
print f,':', list(train_data[f].dropna().mode().values)
features_mode[f] = list(train_data[f].dropna().mode().values)[0]
train_data.fillna(features_mode,inplace=True)
(5)填充上下条的数据
对每一条数据的缺失值,填充其上下条数据的值。
train_data.fillna(method='pad', inplace=True) # 填充前一条数据的值,但是前一条也不一定有值
train_data.fillna(0, inplace=True)
train_data.fillna(method='bfill', inplace=True) # 填充后一条数据的值,但是后一条也不一定有值
train_data.fillna(0, inplace=True)
(6)填充插值得到的数据
用插值法拟合出缺失的数据,然后进行填充。
for f in features: # 插值法填充
train_data[f] = train_data[f].interpolate()
train_data.dropna(inplace=True)
(7)填充KNN数据
填充近邻的数据,先利用knn计算临近的k个数据,然后填充他们的均值。(安装fancyimpute)除了knn填充,fancyimpute还提供了其他填充方法。
from fancyimpute import KNN
train_data_x = pd.DataFrame(KNN(k=6).fit_transform(train_data_x), columns=features)
(8)填充模型预测的值
把缺失值作为新的label,建立模型得到预测值,然后进行填充。这里选择某个缺失值数量适当的特征采用随机森林RF进行拟合,其他缺失特征采用均值进行填充。
new_label = 'SNP46'
new_features = []
for f in features:
if f != new_label:
new_features.append(f)
new_train_x = train_data[train_data[new_label].isnull()==False][new_features]
new_train_x.fillna(new_train_x.mean(), inplace=True) # 其他列填充均值
new_train_y = train_data[train_data[new_label].isnull()==False][new_label]
new_predict_x = train_data[train_data[new_label].isnull()==True][new_features]
new_predict_x.fillna(new_predict_x.mean(), inplace=True) # 其他列填充均值
new_predict_y = train_data[train_data[new_label].isnull()==True][new_label]
rfr = RandomForestRegressor(random_state=666, n_estimators=10, n_jobs=-1)
rfr.fit(new_train_x, new_train_y)
new_predict_y = rfr.predict(new_predict_x)
new_predict_y = pd.DataFrame(new_predict_y, columns=[new_label], index=new_predict_x.index)
new_predict_y = pd.concat([new_predict_x, new_predict_y], axis=1)
new_train_y = pd.concat([new_train_x, new_train_y], axis=1)
new_train_data = pd.concat([new_predict_y,new_train_y])
train_data_x = new_train_data[features]
train_data_y = train_data['label']
2.2时间格式处理
(1)时间本身的特征
形式:日期、时间戳等。
方法:将时间变量作为类别变量处理。
(2)时间变量之间的组合特征
方法:根据两个或多个时间变量的含义,进行特征组合。
(3)时间序列相关特征
目的:基于历史数据预测未来信息。
方法:滞后特征、滑动窗口统计特征。
2.3类别特征处理
- 对于类别有序的特征,比如age等,是可以当成数值型变量处理。对于非类别有序变量,比如仓库编号,推荐使用one-hot,虽然one-hot会增加内存开销以及训练开销。
- 类别型变量在范围较小的时候(如[10-100]),推荐使用。
xgboost是不支持category特征的,所以在训练模型前,需要进行预处理,可以根据特征的具体形式来选择:
- 无序特征:one-hot encoding,比如城市
- 有序特征:label encoding,比如版本号
3.异常值处理
异常值也称为离群点,就是那些远离绝大多数样本点的特殊群体,通常这样的数据点在数据集中都表现出不合理的特性。如果忽视这些异常值,在某些建模场景下就会导致结论的错误(如线性回归模型、K均值聚类等),所以在数据的探索过程中,有必要识别出这些异常值并处理好它们。
当你发现异常值后,一定要先分清是什么原因导致的异常值,然后再考虑如何处理。首先,如果这一异常值并不代表一种规律性的,而是极其偶然的现象,或者说你并不想研究这种偶然的现象,这时可以将其删除。其次,如果异常值存在且代表了一种真实存在的现象,那就不能随便删除。在现有的欺诈场景中很多时候欺诈数据本身相对于正常数据勒说就是异常的,我们要把这些异常点纳入,重新拟合模型,研究其规律。能用监督的用监督模型,不能用的还可以考虑用异常检测的算法来做。
- 注意test的数据不能删。
3.1 检测异常的方法一:正态分布法
在统计学中,如果一个数据分布近似正态,那么大约 68% 的数据值会在均值的一个标准差范围内,大约 95% 会在两个标准差范围内,大约 99.7% 会在三个标准差范围内。
根据正态分布的定义可知,数据点落在偏离均值正负1倍标准差(即sigma值)内的概率为68.2%;数据点落在偏离均值正负2倍标准差内的概率为95.4%;数据点落在偏离均值正负3倍标准差内的概率为99.6%。
所以,换个角度思考上文提到的概率值,如果数据点落在偏离均值正负2倍标准差之外的概率就不足5%,它属于小概率事件,即认为这样的数据点为异常点。同理,如果数据点落在偏离均值正负3倍标准差之外的概率将会更小,可以认为这些数据点为极端异常点。为使读者直观地理解文中提到的概率值,可以查看标准正态分布的概率密度图,如下图所示:
进一步,基于上图的结论,可以按照下表中的判断条件,识别出数值型变量的异常点和极端异常点,如下表所示:
利用正态分布的知识点,结合pyplot子模块中的plot函数绘制折线图和散点图,并借助于两条水平参考线识别异常值或极端异常值。
3.2 检测异常的方法二:箱型图
箱线图技术实际上就是利用数据的分位数识别其中的异常点,该图形属于典型的统计图形,在学术界和工业界都得到广泛的应用。箱线图的形状特征如下图所示:
图中的下四分位数指的是数据的25%分位点所对应的值(Q1);中位数即为数据的50%分位点所对应的值(Q2);上四分位数则为数据的75%分位点所对应的值(Q3);上须的计算公式为Q3+1.5(Q3-Q1);下须的计算公式为Q1-1.5(Q3-Q1)。其中,Q3-Q1表示四分位差。如果采用箱线图识别异常值,其判断标准是,当变量的数据值大于箱线图的上须或者小于箱线图的下须时,就可以认为这样的数据点为异常点。
所以,基于上方的箱线图,可以定义某个数值型变量中的异常点和极端异常点,它们的判断表达式如下表所示:
- 总结一句话:四分位数会将数据分为三个点和四个区间,IQR = Q3 -Q1,下触须=Q1 − 1.5x IQR,上触须=Q3 + 1.5x IQR。
3.3异常值的处理方法
1.删除含有异常值的记录
2.将异常值视为缺失值,交给缺失值处理方法来处理
3.用平均值来修正
4.不处理
4.数据分桶
-
特征分箱的目的:
从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。 -
数据分桶的对象:
将连续变量离散化
将多状态的离散变量合并成少状态 -
分箱的原因:
数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧氏距离作为相似度函数来测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间量化即数据分桶也叫做数据分箱,然后使用量化后的结果。 -
分箱的优点:
处理缺失值:当数据源可能存在缺失值,此时可以把null单独作为一个分箱。
处理异常值:当数据中存在离群点时,可以把其通过分箱离散化处理,从而提高变量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。
业务解释性:我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但实际x与y之间经常存在着非线性关系,此时可经过WOE变换。 -
特别要注意一下分箱的基本原则:
(1)最小分箱占比不低于5%
(2)箱内不能全部是好客户
(3)连续箱单调
- 固定宽度分箱
当数值横跨多个数量级时,最好按照 10 的幂(或任何常数的幂)来进行分组:09、1099、100999、10009999,等等。固定宽度分箱非常容易计算,但如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子。
# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是loanAmnt/1000
data['loanAmnt_bin1'] = np.floor_divide(data['loanAmnt'], 1000)
## 通过对数函数映射到指数宽度分箱
data['loanAmnt_bin2'