数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。
常见的特征工程包括:
1.异常值处理:
- 通过箱线图(或 3-Sigma)分析删除异常值;
- BOX-COX 转换(处理有偏分布);
- 长尾截断;
2. 缺失值处理:
- 不处理(针对类似 XGBoost 等树模型);
- 删除(缺失数据太多);
- 插值补全,包括均值/中位数/众数/建模预测/多重插补/压缩感知补全/矩阵补全等;
- 分箱,缺失值一个箱;
3.特征归一化/标准化:
- 标准化(转换为标准正态分布);
- 归一化(抓换到 [0,1] 区间);
- 针对幂律分布,可以采用公式: l o g ( 1 + x 1 + m e d i a n ) log(\frac{1+x}{1+median}) log(1+median1+x)
4. 数据分桶:
- 等频分桶;
- 等距分桶;
- Best-KS 分桶(类似利用基尼指数进行二分类);
- 卡方分桶;
5. 特征构造:
- 构造统计量特征,报告计数、求和、比例、标准差等;
- 时间特征,包括相对时间和绝对时间,节假日,双休日等;
- 地理信息,包括分箱,分布编码等方法;
- 非线性变换,包括 log/ 平方/ 根号等;
- 特征组合,特征交叉;
- 其他。
6. 特征选择
完成数据预处理后,需要选择有意义的特征输入模型进行训练。通常从以下两个方面考虑来选择特征:
(1) . 特征是否发散:如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本上没有差异,分布严重失衡,那这个特征对于样本的区分没有太大作用。
(2) . 特征与目标的相关性:应当优选选择与目标相关性高的特征。
- 过滤式(filter):按照发散性或者相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,选择特征。
* Relief
* 方差选择法
* 相关系数法
* 卡方检验法
* 信息增益、互信息法 - 包裹式(wrapper):根据目标函数(通常是预测效果评分),每次选择若干特征,或者排除若干特征。
* LVM(Las Vegas Wrapper)
* 递归特征消除法 - 嵌入式(embedding):先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣。
* 基于惩罚项的特征选择法(正则化,L1:Lasso;L2:Ridge)
* 基于树模型的特征选择法(熵、信息增益)
7. 降维
- 主成分分析法(PCA)
- 线性判别分析法(LDA)
- 独立成分分析法( ICA)
特征标准化、特征选择、降维等可直接调用sklearn.preprocessing,sklearn.feature_selection和sklearn.decomposition处理。
部分参考代码
异常值处理
def outliers_proc(data, col_name, scale=3):
"""
用于清洗异常值,默认用 box_plot(scale=3)进行清洗
:param data: 接收 pandas 数据格式
:param col_name: pandas 列名
:param scale: 尺度
:return:
"""
def box_plot_outliers(data_ser, box_scale):
"""
利用箱线图去除异常值
:param data_ser: 接收 pandas.Series 数据格式
:param box_scale: 箱线图尺度,
:return:
"""
iqr = box_scale * (data_ser.quantile(0.75) - data_ser.quantile(0.25))
val_low = data_ser.quantile(0.25) - iqr
val_up = data_ser.quantile(0.75) + iqr
rule_low = (data_ser < val_low)
rule_up = (data_ser > val_up)
return (rule_low, rule_up), (val_low, val_up)
data_n = data.copy()
data_series = data_n[col_name]
rule, value = box_plot_outliers(data_series, box_scale=scale)
index = np.arange(data_series.shape[0])[rule[0] | rule[1]]
print("Delete number is: {}".format(len(index)))
data_n = data_n.drop(index)
data_n.reset_index(drop=True, inplace=True)
print("Now column number is: {}".format(data_n.shape[0]))
index_low = np.arange(data_series.shape[0])[rule[0]]
outliers = data_series.iloc[index_low]
print("Description of data less than the lower bound is:")
print(pd.Series(outliers).describe())
index_up = np.arange(data_series.shape[0])[rule[1]]
outliers = data_series.iloc[index_up]
print("Description of data larger than the upper bound is:")
print(pd.Series(outliers).describe())
fig, ax = plt.subplots(1, 2, figsize=(10, 7))
sns.boxplot(y=data[col_name], data=data, palette="Set1", ax=ax[0])
sns.boxplot(y=data_n[col_name], data=data_n, palette="Set1", ax=ax[1])
return data_n
高度共线性变量处理
#以下代码
#通过删除所比较的两个特征之一,根据我们为相关系数选择的阈值来移除共线特征
#它还打印其删除的相关性,以便我们看到调整阈值的效果。
#此处将阈值设置为0.9,如果特征之间的相关系数超过该值,则删除一对特征中的一个。
def remove_collinear_features(x, threshold):
'''
Objective:
删除数据帧中相关系数大于阈值的共线特征。 删除共线特征可以帮助模型泛化并提高模型的可解释性。
Inputs:
阈值:删除任何相关性大于此值的特征
Output:
仅包含非高共线特征的数据帧
'''
# 在数据副本上进行操作
x = x.copy()
# 仅对v_系列特征进行处理
x_numeric = x[['v_0', 'v_1', 'v_2', 'v_3', 'v_4', 'v_5', 'v_6', 'v_7',
'v_8', 'v_9', 'v_10', 'v_11', 'v_12', 'v_13', 'v_14']]
# 计算相关性矩阵
corr_matrix = x_numeric.corr()
iters = range(len(corr_matrix.columns) - 1)
drop_cols = []
# 迭代相关性矩阵并比较相关性
for i in iters:
for j in range(i):
item = corr_matrix.iloc[j:(j+1), (i+1):(i+2)]
col = item.columns
row = item.index
val = abs(item.values)
# 如果相关性超过阈值
if val >= threshold:
# 打印有相关性的特征和相关值
print(col.values[0], "|", row.values[0], "|", round(val[0][0], 3))
drop_cols.append(col.values[0])
# 删除每对相关列中的一个
drops = set(drop_cols)
x = x.drop(columns = drops)
# 将得分添加回数据
return x