数据挖掘-Task3 特征工程
特征工程目标
特征工程(Feature Engineering):将数据转换为能更好地表示潜在问题的特征,从而提高机器学习性能。
常见的特征工程包括:
- 异常处理:
- 通过箱线图(或3-Sigma)分析删除异常值;
- BOX-COX转换(处理有偏分布);
- 长尾截断;
- 特征归一化/标准化:
- 标准化(转换为标准正态分布);
- 归一化(转换到[0,1]区间);
- 数据分桶:
- 等频分桶;
- 等距分桶;
- Best-KS分桶(类似利用基尼指数进行二分类);
- 卡方分桶;
- 缺失值处理:
- 不处理(针对类似XGBoost等树模型);
- 删除(缺失数据太多);
- 插值补全,包括均值、中位数、众数、建模预测、多重插补、压缩感知补全、矩阵补全等;
- 分箱,缺失值一个箱;
- 特征构造:
- 构造统计量特征,报告计数、求和、比例、标准差等;
- 时间特征,包括相对时间和绝对时间,节假日,双休日等;
- 地理信息,包括分箱,分布编码等方法;
- 非线性变换,包括log、平方、根号等;
- 特征组合,特征交叉;
- 仁者见仁,智者见智;
- 特征筛选
- 过滤式(filter):先对数据进行特征选择,然后再训练学习器,常见的方法有Relief、方差选择法、相关系数法、卡方检验法、互信息法;
- 包裹式(wrapper):直接把最终要使用的学习器的性能作为特征子集的评价准则,常见方法有LVM(Las Vegas Wrapper);
- 嵌入式(embedding):结合过滤式和包裹式,学习器训练过程中自动进行了特征选择,常见的有lasso回归;
- 降维
- PCA、LDA、ICA;
- 特征选择也是一种降维
1 内容介绍
- 数据理解;
- 数据清洗;
- 特征构造;
- 特征选择;
- 类别不平衡。
1.1 数据理解
见上一篇,数据的探索性分析(EDA)
1.2 数据清洗
目的:提高数据质量,降低算法用错误数据建模的风险
- 特征变换:模型无法处理或不适合处理
a) 定性变量编码:Label Encoder、Onehot Encoder、Distribution Encoder;
b) 标准化和归一化:z分数标准化(标准正太分布)、min-max归一化。 - 缺失值处理:增加不确定性,可能会导致不可靠输出
a) 不处理:少量样本缺失;
b) 删除:大量样本缺失;
c) 补全:(同类)均值/中位数/众数补全、高维映射(One-hot)、模型预测、最邻近补全、矩阵补全(R-SVD)。 - 异常值处理:减少脏数据
a) 简单统计:如describe()的统计描述、散点图等;
b) 3δ法则(正态分布)、箱线图删除、截断;
c) 利用模型进行离群点检测:聚类、K近邻、One Class SVM、Lsolation Forest; - 其他:删除无效列、更改dtypes、删除列中的字符串、将时间戳从字符串转换为日期时间格式等。
1.3 特征构造
目的:增强数据表达,添加先验知识
- 统计量特征
a) 计数、求和、比例、标准差; - 时间特征
a) 绝对时间、相对时间、节假日、双休日; - 地理信息
a) 分桶 - 非线性变换
a) 取log、平方、根号; - 数据分桶
a) 等频/等距分桶、Best-KS分桶、卡方分桶; - 特征组合/特征交叉
1.4 特征选择
目的:平衡预测能力和计算复杂度,降低噪声,增强模型预测性能
- 过滤式(Filter):先用特征选择方法对初始特征进行过滤,然后再训练机器学习,特征选择过程与后续学习器无关
a) Relief、方差选择、相关系数、卡方检验、互信息法; - 包裹式(Wrapper):直接把最终将要使用的学习器的性能作为衡量特征子集的评价准则,其目的在于为给定学习器选择最有利于其性能的特征子集
a) Las Vegas Wrapper(LVM) - 嵌入式(Embedding):结合过滤式和包裹式方法,将特征选择与学习器训练过程融为一体,两者在同一优化过程中完成 ,即学习器训练过程中自动进行了特征选择
a) LR+L1或决策树
1.5 类别不平衡
目的:解决少类别信息太少,导致学习器无法学会如何判别少数类
- 扩充数据集
- 尝试其他评价指标:AUC等
- 调整θ值
- 重采样、过采样/欠采样
- 合成样本:SMOTE
- 选择其他模型:决策树等
- 加权少类别的样本错分代价
- 创新:
a) 将大类分解成多个小类
b) 将小类视为异常点,并用异常检测建模
2 代码示例
2.1 数据理解
数据的理解在上一篇文章中已经对获取的数据,进行了EDA分析,接下来是数据清洗和转换在数据挖掘中也非常重要,毕竟数据探索我们发现了问题之后,下一步就是要解决问题,通过数据清洗和转换,可以让数据变得更加整洁和干净,才能进一步帮助我们做特征工程,也有利于模型更好的完成任务。
在上一篇文章数据的探索性(EDA)分析中, 发现数据存在如下问题:
- 有缺失值,尤其是类别的那些特征(bodyType, gearbox, fullType, notRepaired)
- 类别倾斜严重的特征(seller, offtype), 考虑删除
- 类别型数据需要编码
- 数值型数据或许可以尝试归一化和标准化的操作
- 预测值需要对数转换
- power高度偏斜,这个处理异常之后对数转换试试
- 隐匿特征的相关性
- 存在高势集model, 及类别特征取值非常多, 可以考虑使用聚类的方式,然后在独热编码
============================================================== - v_13与price的相关性很差,考虑删除
- v_1与v_6相关性很强,结合它们与prince的相关性,删v_1留v_6
- v_2与v-0相关性很强,结合它们与prince的相关性,删v_2留v-0
- ============================================================
- 使用:data[‘creatDate’] - data[‘regDate’],构建汽车使用时间特征
- 使用:data[‘city’] = data[‘regionCode’].apply(lambda x : str(x)[:-3]),构建城市特征
2.1.1 加载库
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
from operator import itemgetter
%matplotlib inline
2.1.2 加载数据
path = './datalab/'
train_data = pd.read_csv(path+'used_car_train_20200313.csv',sep=' ')
test_data = pd.read_csv(path+'used_car_testA_20200313.csv',sep=' ')
print(train_data.shape)
print(test_data.shape)
2.1.3 预览数据
train_data.iloc[:,:10].head().append(train_data.iloc[:,:10].tail())
train_data.iloc[:,10:20].head().append(train_data.iloc[:,10:20].tail())
train_data.iloc[:,20:].head().append(train_data.iloc[:,20:].tail())
train_data.columns
test_data.columns
重点是在整理数据清洗和转换的技巧,首先是处理异常数据,通过箱线图捕获异常点,然后截尾处理, 然后是整理一些处理缺失的技巧, 然后是数据分桶和数据转换的一些技巧。
2.2 数据清洗
大纲:
- 异常值处理(箱线图分析删除,截尾,box-cox转换技术)
- 缺失值处理(不处理, 删除,插值补全, 分箱)
- 数据分桶(等频, 等距, Best-KS分桶,卡方分桶)
- 数据转换(归一化标准化, 对数变换,转换数据类型,编码等)
- 知识总结
方法一:
================================================================
数据清洗的时候,我这里先把数据训练集和测试集放在一块进行处理,因为我后面的操作不做删除样本的处理, 如果后面有删除样本的处理,可别这么做。 数据合并处理也是一个trick, 一般是在特征构造的时候合并起来,而我发现这个问题中,数据清洗里面训练集和测试集的操作也基本一致,所以在这里先合起来, 然后分成数值型、类别型还有时间型数据,然后分别清洗。
1、先合并、分型
"""把train_data的price先保存好"""
train_target = train_data['price']
del train_data['price']
"""数据合并"""
data = pd.concat([train_data, test_data], axis=0)
data.set_index('SaleID', inplace=True)
"""把数据分成数值型和类别型还有时间型,然后分开处理"""
"""人为设定"""
numeric_features = ['power', 'kilometer']
numeric_features.extend(['v_'+str(i) for i in range(15)])
categorical_features = ['name', 'model', 'brand', 'bodyType', 'fuelType', 'gearbox',
'notRepairedDamage','regionCode', 'seller', 'offerType']
time_features = ['regDate', 'creatDate']
num_data = data[numeric_features]
cat_data = data[categorical_features]
time_data = data[time_features]
trick1: 就是如果发现处理数据集的时候,需要训练集和测试集进行同样的处理,不放将它们合并到一块处理。
trick2: 如果发现特征字段中有数值型,类别型,时间型的数据等,也不妨试试将它们分开,因为数值型,类别型,时间型数据不管是在数据清洗还是后面的特征工程上, 都是会有不同的处理方式, 所以这里将训练集合测试集合并起来之后,根据特征类型把数据分开, 等做完特征工程之后再进行统一的整合(set_index把它们的索引弄成一样的,整合的时候就非常简单了)。
2、异常值处理
常用的异常值处理操作包括箱线图分析删除异常值, BOX-COX转换(处理有偏分布), 长尾截断的方式, 当然这些操作一般都是处理数值型的数据。
关于box-cox转换,一般是用于连续的变量不满足正态的时候,在做线性回归的过程中,一般线性模型假定Y=Xβ+ε, 其中ε满足正态分布,但是利用实际数据建立回归模型时,个别变量的系数通不过。例如往往不可观测的误差 ε 可能是和预测变量相关的,不服从正态分布,于是给线性回归的最小二乘估计系数的结果带来误差,为了使模型满足线性性、独立性、方差齐性以及正态性,需改变数据形式,故应用box-cox转换。具体详情这里不做过多介绍,当然还有很多转换非正态数据分布的方式:
箱线图分析具体实现
from scipy.stats import boxcox
boxcox_transformed_data = boxcox(original_data)
当然,也给出一个使用案例:使用scipy.stats.boxcox完成BoxCox变换
好了,BOX-COX就介绍这些吧, 因为这里处理数据先不涉及这个变换,我们回到这个比赛中来,通过这次的数据介绍一下箱线图筛选异常并进行截尾:
从上面的探索中发现,某些数值型字段有异常点,可以看一下power这个字段:
# power属性是有异常点的
num_data.boxplot(['power'])
所以,我们下面用箱线图去捕获异常,然后进行截尾, 这里不想用删除,一个原因是我已经合并了训练集和测试集,如果删除的话肯定会删除测试集的数据,这个是不行的, 另一个原因就是删除有时候会改变数据的分布等,所以这里考虑使用截尾的方式:
"""这里包装了一个异常