前言:

最近在训练一个销量预测模型的时候,遇到了一个问题,不管我如何调整网络结构和超参数,查准率和召回率就是上不去。吴恩达提到过机器学习的根本其实就是数据,所以我只想回过头从数据起源这边重新审视自己的模型。顺便将自己对特征工程的所有理解系统地整理出来,给自己做个笔记,也给未来的小白做个领路。这便文章不仅有对特征工程系统的解析,还会有python代码的实例演示,欢迎点赞评论,建议收藏!

文章目录

一、特征工程是什么及特征工程的意义

特征工程是​​将原始数据转化成更好表达问题本质的特征的过程,使得将这些特征运用到预测模型中能提高对不可见数据的模型预测精度​​​;如何处理原始数据,发现对结果有明显影响作用的特征以更好地表达问题的本质就是做特征工程的目的;特征工程也是数据机器学习模型开发中较耗时、很重要的一步。重要性体现在特征选择和准备得越好,获得的结果大概率也会更好,对结果有直接的影响。耗时体现在它所涉及的流程和过程步骤较多,一份原始数据到成功的数据需要较多的处理。​​实际上,所有机器学习算法的成功取决于如何呈现数据​

二、什么是特征

一个学生具备很多属性,例如身高、眼睛大小、是否戴眼镜、体重、数学成绩、语文成绩、英语成绩、喜欢吃汉堡、喜欢打游戏、喜欢睡懒觉。如果你要预测这个学生是否是学霸,那么身高、眼睛大小、体重这些属性显然不会是要使用到的特征,因为这些属性几乎对学生是否是学霸没有影响;而数学成绩、语文成绩、英语成绩更能体现学生是否是学霸;喜欢吃汉堡、喜欢打游戏、喜欢睡懒觉更能体现学生是否是学渣;所以这些属性可以作为预测学生是否是学霸的特征。

所以​​并不是所有的属性都可以看做特征,区分它们的关键在于看这个属性对解决问题有没有影响​​。所以可以认为特征是对于建模任务有用的属性,特征可以表达更多跟问题有关的内容。

常见的表格式的数据用行来表示一个实例(例如一个人),用列来表示属性(例如身高)。

三、特征工程的总体流程

开始流程:

  1. 根据具体问题和业务要求​​采集数据​​;例如要对学生进行学霸学渣的分类,就要采集学生的各项数据、可以直接去学校的数据库里面直接取、也可以做调研,根据自己实际情况出发决定。
  2. 采集到数据之后,要对数据进行​​数据分析​​,了解数据不同属性的统计特征、例如属性的分布、最大值、最小值、标准差、方差、极值、直方图、密度分布曲线,做到对数据心中有数
  3. 了解数据的整体情况之后,要对数据进行​​数据清洗​​,去除异常值、去除空值、去除重复值等
  4. 然后对数据进行​​数据采样​​,做到样本的均衡。不同类别的数据数量不能相差几个数量级,对于不平衡的类别数据,需要在后边的算法中做处理
  5. 然后对数据进行​​缺失值处理​​。包括缺失值删除、缺失值填充等
  6. 然后对数据进行​​特征转换​​,包含连续型特征转换、离散型特征转换、时间序列处理。
  7. 上面处理完成留下来的特征都是基于我们对业务和问题的理解挑选出来的,不一定是该模型真正需要的特征,所以我们还需要对这些​​特征进行检验和选择​​,包含卡方检测、F检测、互信息,还包括嵌入法和包装法,下面章节会详细演示说明。
  8. 经过上面的流程七如果我们选择的特征还不足够满足我们的要求,我们还可以对​​特征进行构造​​,包含四则运算、特征交叉、分解类别特征、机器学习等方法
  9. 经过上面的流程七如果我们选择的特征数量远超过我们的要求,并且非常多余,我们需要对特征进行提取,包含线性降维、非线性降维、迁移学习降维等方法
  10. 到这一步就是将数据输入我们构建的网络模型进行训练了,俗称入模。
  11. 结束

流程表格:

流程

流程关键词

方法总结

1

数据采集

数据库读取、调研、问卷收集、爬虫

2

数据分析

计算属性统计特征、如最大最小值、标准差、分布直方图

3

数据清洗

去除异常值、空值、重复值

4

数据采样

数据采样、样本均衡

5

数据缺失值处理

缺失值删除、缺失值填充

6

数据特征转换(重难点)

连续型特征转换、离散型特征转换、时间序列处理

7

数据特征选择(重难点)

包含卡方检测、F检测、互信息,还包括嵌入法和包装法

8

数据特征构造

四则运算、特征交叉、分解类别特征、机器学习

9

数据特征提取

包含线性降维、非线性降维、迁移学习降维

10

数据入模

生成器对象数据入模

四、特征工程流程详解和代码演示

(1)数据采集

如果你参加kaggle的比赛,那么比赛方会将所需数据提供给你;如果是公司的成熟业务,公司会有数据分析师将数据库给到你,你自己读取即可;如果是公司的创新业务,就需要你在深度理解业务的基础之上在公司能力范围内拿合适的数据,如果公司没有这些数据,你需要去网上下载、爬虫、调研、问卷收集等等手段收集数据

(2)数据分析

一般对数据先进行单变量分析,了解单个特征的数据分布情况、统计特性如平均值、最大值最小值、极值、标准差等等。然后再通过双变量分析,了解不同特征之间的关系,可以画散点图,如果图像呈现直线或者曲线,说明两个特征彼此相关。一般数据分析到这一步即可。

1、单变量分析

单变量分析是数据分析中最简单的形式,其中被分析的数据只包含一个变量。因为它是一个单一的变量,它不处理原因或关系。单变量分析的主要目的是描述数据并找出其中存在的模式。

可以将变量视为数据所属的类别,比如单变量分析中,有一个变量是“年龄”,另一个变量是“高度”等,单因素分析就不能同时观察这两个变量,也不能看它们之间的关系。

单变量数据中的发现模式有:查看平均值、模式、中位数、范围、方差、最大值、最小值、四分位数和标准偏差。此外,显示单变量数据的一些方法包括频率分布表、柱状图、直方图、频率多边形和饼状图。

a.计算统计特性

下面所说的统计特征函数主要作为Pandas的对象DataFrame或Series的方法出现。

describe 针对Series或个DataFrame列计算汇总统计

count 非na值的数量

min、max 计算最小值和最大值

idxmin、idxmax 计算能够获取到最大值和最小值得索引值

quantile 计算样本的分位数(0到1)

sum 值的总和

mean 值得平均数

median 值得算术中位数(50%分位数)

mad 根据平均值计算平均绝对离差

var 样本值的方差

std 样本值的标准差

corr 样本值的Spearman(Person)相关系数矩阵

skew 样本值得偏度(三阶矩)

kurt 样本值得峰度(四阶矩)

例如

import pandas as pd
df = pd.DataFrame([13,27,38,43,52,6],columns=['age'])
df.describe() ## 查看汇总统计

结果

6.000000
mean 29.833333
std 17.837227
min 6.000000
25% 16.500000
50% 32.500000
75% 41.750000
max 52.000000
b.绘制走势图

可以查看每一列属性值的走势情况,从中你可以知道是否有偏差值、异常值、空值等情况

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

df = pd.DataFrame([13,27,38,43,52,6],columns=['age'])
plt.plot(df.age) # 绘制走势图。根据自己需求也可以绘制散点图,散点图也比较直观
plt.show() # 显示图像
c.绘制分布直方图

可以查看数据在某个范围内的分布情况

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

df = pd.DataFrame([13,10,38,43,2, 6, 4, 70],columns=['age'])
plt.hist(df.age,bins=10,range=(0,100))
plt.show()

2、双变量分析

使用双变量分析来找出两个不同变量之间是否存在关系,在笛卡尔平面上(想想X和Y轴)将一个变量对另一个变量进行绘图,从而创建散点图(.plot),这样简单的事情有时可以让你了解数据试图告诉你的内容,如果数据似乎符合直线或曲线,那么这两个变量之间存在关系或相关性。例如,人们可能会选择热量摄入与体重的关系。

a.绘制散点图
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

df = pd.DataFrame([[13,133],
[10,105],
[38,381],
[43,435],
[2,20],
[6,62],
[4,47],
[70,650]],columns=['age','friends_number'])
plt.scatter(df.age,df.friends_number)
plt.show()

3、多变量分析

多变量分析是对三个或更多变量的分析。根据你的目标,有多种方法可以执行多变量分析,这些方法中的一些包括添加树,典型相关分析,聚类分析,对应分析/多重对应分析,因子分析,广义Procrustean分析,MANOVA,多维尺度,多元回归分析,偏最小二乘回归,主成分分析/回归/ PARAFAC和冗余分析。

(3)数据清洗

模型通常是对整体样本数据结构的一种表达方式,这种表达方式通常抓住的是整体样本一般性的性质,而那些在这些性质上表现完全与整体样本不一致的点,我们就称其为异常点

数据清洗主要是挑选并去除异常值,异常值的去除方法包括基于统计的异常点检测算法,例如极差、四分位数间距、均差、标准差,这种方法适合挖掘单变量的数据型数据。第二种是基于距离的异常点检测算法,例如曼哈顿距离、欧式距离和马氏距离等方法。第三种是基于密度的异常点检测算法,考察当前点周围密度,可以发现局部异常点。

1、基于统计的异常点检测算法

这里主要介绍两种常用的方法,第一种是​​3∂原则​​,这个原则有个条件:**数据需要服从正态分布。**在3∂原则下,异常值如超过3倍标准差,那么可以将其视为异常值。正负3∂的概率是99.7%,那么距离平均值3∂之外的值出现的概率为P(|x-u| > 3∂) <= 0.003,属于极个别的小概率事件。pandas实现如下:

df2 = df1.mask((df1 - df1.mean()).abs() >3* df1.std()).dropna()  # 使用3倍标准差挑选并去除异常值

第二种是​​四分位数间距​​,四分位距(IQR)就是上四分位与下四分位的差值。而我们通过IQR的1.5倍为标准,规定:超过**(上四分位+1.5倍IQR距离,或者下四分位-1.5倍IQR距离)**的点为异常值。pandas实现如下:

Q1 = df1.quantile(q=0.25)
Q3 = df1.quantile(q=0.75)
IQR = Q3 - Q1
outlier_step = 1.5*(Q3-Q1)
df2 = df1.mask((df1<Q1-outlier_step) | (df1>Q3+outlier_step) ).dropna()

2、基于距离的异常点检测算法

基于距离的异常点检测算法主要通过距离方法来检测异常点,若​​某个样本点与大多数点之间的距离大于某个阈值​​,则视为异常点。主要使用的距离度量方法有绝对距离、欧式距离和马氏距离等方法。各类距离公式的python实现看这篇博客:https://cloud.tencent.com/developer/article/1406436。PS:实现代码都在里面

3、基于密度的异常点检测算法

基于密度的异常值检测方法的原理认为正常样本点所处的类簇密度要高于异常点样本所处的类簇密度。为解决实际异常值检测情况
中出现的问题,有一种基于局部异常因子 LOF 方法,这个方法我没用过,也还没弄懂,所以在这里就暂时不解释了。

(4)数据采样

假设我们想要预测一个学生是否是学霸,然后采集到的数据有1万个学渣学生,和只有1000个学霸学生。很明显这种数据的类别样本量是不均衡的。实际情况中,绝大多数情况都存在这种类别样本不均衡的现象。所以需要我们手动进行样本的采样。参考机器学习采样方法:https://zhuanlan.zhihu.com/p/76024846

对数据进行采样处理,主要还是分两种方式:

过采样(over-sampling):从占比较少的那一类样本中重复随机抽样,使得最终样本的目标类别不太失衡;

欠采样(under-sampling):从占比较多的那一类样本中随机抽取部分样本,使得最终样本的目标类别不太失衡;

1、过采样的方法

  • 随机过采样,它从样本少的类别中随机抽样,再将抽样得来的样本添加到数据集中,从而达到类别平衡的目的,这样子做很多时候会出现过拟合情况。
  • SMOTE,全称是Synthetic Minority Oversampling Technique,其思想就是在少数类的样本之间,进行插值操作来产生额外的样本。对于一个少数类样本,使用K-Mean法(K值需要人工确定)求出距离
  • Border-Line SMOTE,这个算法一开始会先将少数类样本分成3类,分别DANGER、SAFE、NOISE,而Border-line SMOTE算法只会在“DANGER”状态的少数类样本中去随机选择,然后利用SMOTE算法产生新样本。
  • ADASYN,全称为自适应合成抽样,其最大的特点是采用某种机制自动决定每个少数类样本需要产生多少合成样本,而不是像SMOTE那样对每个少数类样本合成同数量的样本。ADASYN的缺点是易受离群点的影响,如果一个少数类样本的K近邻都是多数类样本,则其权重会变得相当大,进而会在其周围生成较多的样本。

2、欠采样的方法

  • 随机欠采样,随机从多数类中删除一些样本,这样子的缺失也是很明显,那就是造成部分信息丢失,整体模型分类效果不理想
  • EasyEnsemble ,将多数类样本随机划分成n份,每份的数据等于少数类样本的数量,然后对这n份数据分别训练模型,最后集成模型结果
  • BalanceCascade,这类算法采用了有监督结合boosting的方式,在每一轮中,也是从多数类中抽取子集与少数类结合起来训练模型,然后下一轮中丢弃此轮被正确分类的样本,使得后续的基学习器能够更加关注那些被分类错误的样本
  • NearMiss,从多数类样本中选取最具代表性的样本用于训练,主要是为了缓解随机欠采样中的信息丢失问题。NearMiss采用一些启发式的规则来选择样本,根据规则的不同可分为3类:
  • **NearMiss-1:**选择到最近的K个少数类样本平均距离最近的多数类样本
  • **NearMiss-2:**选择到最远的K个少数类样本平均距离最近的多数类样本
  • **NearMiss-3:**对于每个少数类样本选择K个最近的多数类样本,目的是保证每个少数类样本都被多数类样本包围

(5)数据缺失值处理

数据缺失值处理主要分为两种,一种是缺失值的删除,一种是缺失值的填充,大部分情况下,如果数据充裕,直接删除不会产生什么影响,但是一般而言,我们的数据都是不够的,因为机器学习没有谁会觉得自己数据太多,所以对数据的缺失值进行填充是绝大多数人的选择

1、缺失值的删除

如果该实例的属性缺失值很多,建议直接删除实例,pandas代码如下

df.dropna(inplace=True)

如果数据集中的某个特征的值缺失很多,也可以将这个特征直接删除,pandas代码如下

df.drop(axis=1, columns='columns_name', inplace=True)

2、缺失值填充

简单填充可以使用均值、中位数、固定值、众数、前一个数、后一个数、插值法进行填充。也可以使用KNN或者是随机森林进行填充

df['rate'] = df['rate'].fillna('0.1')  # 用固定值‘0.1’进行填充
df['rate'] = df['rate'].fillna(df['rate'].mean()) # 用均值进行填充
df['rate'] = df['rate'].fillna(df['rate'].mode()) # 用众数进行填充
df['rate'] = df['rate'].fillna(df['rate'].median()) # 用中位数进行填充
df['rate'] = df['rate'].fillna(method='pad') # 用前一个数进行填充
df['rate'] = df['rate'].fillna(method='bfill') # 用后一个数进行填充
df['rate'] = df['rate'].interpolate() # 用插值进行填充

KNN或者是随机森林进行填充我不太清楚实现流程,这里就不解释了,有兴趣的小伙伴去谷歌搜吧。

(6)数据特征转换

1、连续型特征处理

很多时候我们拿到的属性数据的量纲彼此不同,例如一个人的收入和体重,就需要用无量纲化的方法使不同规格的数据转换到同一规格。无量纲化的方法主要分为两类。一类是标准化、一类是归一化,归一化和标准化都是对数据做变换的方式,将原始的一列数据转换到某个范围

a.标准化

标准化:将数据变换为均值为0,标准差为1的分布,公式如下
一文入门特征工程【建议收藏】_缺失值
其中xi是原始数据,其它两个分别是原始数据的均值和标准差,python代码实现:

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import preprocessing

def plot(data, title):
sns.set_style('dark')
f, ax = plt.subplots()
ax.set(ylabel='frequency')
ax.set(xlabel='height(blue) / weight(green)')
ax.set(title=title)
sns.distplot(data[:, 0:1], color='blue')
sns.distplot(data[:, 1:2], color='green')
plt.savefig(title + '.png')
plt.show()

np.random.seed(42)
height = np.random.normal(loc=168, scale=5, size=1000).reshape(-1, 1)
weight = np.random.normal(loc=70, scale=10, size=1000).reshape(-1, 1)

original_data = np.concatenate((height, weight), axis=1)
plot(original_data, 'Original')

standard_scaler_data = preprocessing.StandardScaler().fit_transform(original_data)
plot(standard_scaler_data, 'StandardScaler')
b.归一化

归一化:将一列数据变化到某个固定区间(范围)中,通常,这个区间是[0, 1],广义的讲,可以是各种区间,比如映射到[0,1]一样可以继续映射到其他范围,图像中可能会映射到[0,255],其他情况可能映射到[-1,1]

  • 最大最小值归一化:将数据缩放到0和1之间,公式
    一文入门特征工程【建议收藏】_机器学习_02
    python代码实现
min_max_scaler_data = preprocessing.MinMaxScaler().fit_transform(original_data)
plot(min_max_scaler_data, 'MinMaxScaler')

最大最小值归一化的数据转化只跟数据的极大值、极小值有关,跟其它数据无关,所以如果数据存在异常的极大极小值,这种方式就不适用。不同于标准化,标准化计算需要用到原始数据的均值和标准差,所以跟除了极大极小值之外的其它数据有关,所以标准化适合绝大多数场景。

  • 均值归一化(也称为中心化),将数据缩放到-1和1之间,公式
    一文入门特征工程【建议收藏】_数据_03
    python代码实现
max_abs_scaler_data = preprocessing.MaxAbsScaler().fit_transform(original_data)
plot(max_abs_scaler_data, 'MaxAbsScaler')
c.函数转换

log变换,公式
一文入门特征工程【建议收藏】_缺失值_04
sigmoid变换(sigmoid函数),公式
一文入门特征工程【建议收藏】_数据挖掘_05
softmax变换(softmax函数),公式
一文入门特征工程【建议收藏】_机器学习_06

d.二值化

特征的二值化处理是将数值型数据输出为布尔类型。其核心在于设定一个阈值,当样本数据大于该阈值时,输出为1,小于等于该阈值时输出为0。公式
$$
X_{new} = \begin{cases} 1,x>threshold \ 0, x<=threshold\end{cases}

$$
python代码实现

from sklearn import preprocessing 
#二值化,阈值设置为3,返回值为二值化后的数据
erzhihua_data = preprocessing.Binarizer(threshold=3).fit_transform(iris.data)
e.分箱处理

分箱处理就是将连续的数值型数据转为离散的类别数据,离散化方法的关键是怎么确定分段中的离散点,确定离散点的方法有两种。

一种是等距离离散法,就是离散点选取等距点。例如人的年龄是1-100岁,可使用等距离离散法分为1-10为童年,11-20为少年,以此类推~

另外一种是等样本点离散法,选取的离散点保证落在每段里的样本点数量大致相同。

分箱处理的好处就是我们可以提前知道这个特征的分类,可以更好地提取特征的有效信息,降低干扰。例如2岁的孩子和4岁的孩子都喜欢玩具,那么2岁和四岁对于模型来说就是没有区别的,可以归为一类来看。例如每天步数少于1000的人,容易肥胖,步数在1000到2000之间为正常,步数在2000以上为健身达人,我们就可以通过步数来区分三者的区别了。

from sklearn import preprocessing
X = np.array([[ -3., 5., 15 ],
[ 0., 6., 14 ],
[ 6., 3., 11 ]])
est = preprocessing.KBinsDiscretizer(n_bins=[3, 2, 2], encode='ordinal').fit(X)
ans = est.transform(X)
# ans的值如下:
array([[0., 1., 1.],
[1., 1., 1.],
[2., 0., 0.]])

函数解析:

sklearn.preprocessing.KBinsDiscretizer(n_bins=5, encode=’onehot’, strategy=’quantile’)
  • n_bins:分箱的数量,默认值是5,也可以是列表,指定各个特征的分箱数量,例如,[feature1_bins,feature2_bins,…]
  • encode:编码方式,{‘onehot’, ‘onehot-dense’, ‘ordinal’}, (default=’onehot’)
  • onehot:以onehot方式编码,返回稀疏矩阵
  • onehot-dense:以onehot方式编码,返回密集矩阵
  • ordinal:以ordinal方式编码,返回分箱的序号
  • strategy:定义分箱宽度的策略,{‘uniform’, ‘quantile’, ‘kmeans’}, (default=’quantile’)
  • uniform:每个分箱等宽
  • quantile:每个分箱中拥有相同数量的数据点
  • kmeans:每个箱中的值具有与1D k均值簇最近的中心

2、离散型特征处理

a.数值化处理

将一个属性的k种值,按序转换成k个数字(1,2,3…k)。最常用是的是一个属性只有两种值,则将这两种值赋值为0和1。例如一个人的身高有矮、正常、高,则可以将这三种值赋值为1和2和3,但是对于神经网络而已,相同权重跟这些值计算得到的结果不一样,所以一般来说,直接数值化处理的结果存在的缺点太大,用于二分类赋值还行,多于二分类的赋值,效果不好,所以也就衍生出下面的几种方法。

b.亚编码之独热编码

例: 再次举一个简单的例子,由{红,绿、蓝}组成的颜色属性,最常用的方式是把每个类别属性转换成二元属性,即从{0,1}取一个值。因此基本上增加的属性等于相应数目的类别。对于数据集中的每个实例,只有一个是1(其他的为0),这也就是独热(one-hot)编码方式(类似于转换成哑变量)。 如果你不了解这个编码的话,你可能会觉得分解会增加没必要的麻烦(因为编码大量的增加了数据集的维度)。相反,你可能会尝试将类别属性转换成一个标量值,例如颜色属性可能会用{1,2,3}表示{红,绿,蓝}。这里存在两个问题,首先,对于一个数学模型,这意味着某种意义上红色和绿色比和蓝色更“相似”(因为|1-3| > |1-2|)。除非你的类别拥有排序的属性(比如铁路线上的站),这样可能会误导你的模型。然后,可能会导致统计指标(比如均值)无意义,更糟糕的情况是,会误导你的模型。还是颜色的例子,假如你的数据集包含相同数量的红色和蓝色的实例,但是没有绿色的,那么颜色的均值可能还是得到2,也就是绿色的意思。

哑编码主要是采用N位状态寄存器来对N个状态进行编码,一个变量N个值就转换成N个虚拟变量,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。 使用one-hot编码,将离散特征的取值扩展到了欧式空间,离散特征的某个取值就对 应欧式空间的某个点。 在回归,分类,聚类等机器等学习算法中,特征之间距离的计算或相似度的计算是 非常重要的,而我们常用的距离或相似度的计算都是在欧式空间的相似度计算,计算 余弦相似性,基于的就是欧式空间。

from sklearn import preprocessing
enc = preprocessing.OneHotEncoder() # 建立对象
enc.fit([['zero', 'zero', 'three'],
['one', 'one', 'zero'],
['one', 'two', 'two'],
['zero', 'zero', 'two']]) # 对象编码
ans = enc.transform([['zero', 'two', 'three']]).toarray() # 将离散型数据转换成one-hot编码

inverse_ans = enc.inverse_transform(ans) # 将one-hot编码转换成离散型数据

使用OneHot时要避免高基类别的特征以及基于决策树的模型。例如如果有个特征的值有一万种,那么one-hot编码之后的维度就变成了一万维,变成很大的稀疏举证,不利模型的训练。one-hot也不利于决策树的学习,也不适合用于决策树。

b.亚编码之顺序性哑变量编码

与one-hot编码一样,都是将一个变量的k个值生成k个哑变量,但同时保护了特征的顺序关系。例如下面例子

特征取值

向量表示

bad

(1,0,0)

normal

(1,1,0)

good

(1,1,1)

python代码实现

from sklearn import preprocessing 
enc = preprocessing.OrdinalEncoder()
X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
enc.fit(X) # 该方法接受输入和标签,计算出数据变换的方式
ans = enc.transform([['female', 'from US', 'uses Safari']]) # 根据已经计算出的变换方式,返回对输入数据x变换后的结果
inverse_ans = enc.inverse_transform(ans) # # 根据已经计算出的变换方式,返回对输入数据x变换前的结果

3、时间序列处理

a.时间戳处理

时间戳属性通常需要分离成多个维度比如年、月、日、小时、分钟、秒钟。python代码实现

import pandas as pd
import numpy as np
import datetime
from dateutil.parser import parse
# parse根据字符串解析成datetime,字符串可以很随意,可用时间日期的英文单词,可用横线,逗号,空格等做分隔符
import pytz # 时区

time_stamps = ['2015-03-08 10:30:00.360000+00:00', '2017-07-13 15:45:05.755000-07:00',
'2012-01-20 22:30:00.254000+05:30', '2016-12-25 00:30:00.000000+10:00']
df = pd.DataFrame(time_stamps, columns = ['Time'])
ts_objs = np.array([pd.Timestamp(item) for item in np.array(df.Time)])
df['Ts_obj'] = ts_objs
df['Year'] = df['Ts_obj'].apply(lambda d: d.year)
df['Month'] = df['Ts_obj'].apply(lambda d: d.month)
df['Day'] = df['Ts_obj'].apply(lambda d: d.day)
df['DayOfWeek'] = df['Ts_obj'].apply(lambda d: d.dayofweek)
df['DayName'] = df['Ts_obj'].apply(lambda d: d.day_name())
df['DayOfYear'] = df['Ts_obj'].apply(lambda d: d.dayofyear)
df['WeekOfYear'] = df['Ts_obj'].apply(lambda d: d.weekofyear)
df['Quarter'] = df['Ts_obj'].apply(lambda d: d.quarter)

df结果如下

0    2015-03-08 10:30:00.360000+00:00    2015-03-08 10:30:00.360000+00:00    2015    3    8    6    67    10    1    Sunday
1 2017-07-13 15:45:05.755000-07:00 2017-07-13 15:45:05.755000-07:00 2017 7 13 3 194 28 3 Thursday
2 2012-01-20 22:30:00.254000+05:30 2012-01-20 22:30:00.254000+05:30 2012 1 20 4 20 3 1 Friday
3 2016-12-25 00:30:00.000000+10:00 2016-12-25 00:30:00+10:00 2016 12 25 6 360 51 4

(7)数据特征选择

特征选择顾名思义,就是从大量的特征中选择少量的有用特征。不是所有的特征都是平等的。那些与问题不相关的属性需要被删除;还有一些特征比其他特征重要,也有一些特征是冗余的。特征选择就是自动地选择对于问题中重要的特征的一个子集。

特征选择的方法主要分为三大类:过滤式方法 (Filter Methods),包裹式方法 (Wrapper Methods) 和嵌入式方法 (Embedded Methods)。

  • 过滤式方法运用统计指标来为每个特征打分并筛选特征,其聚焦于数据本身的特点。其优点是计算快,不依赖于具体的模型,缺点是选择的统计指标不是为特定模型定制的,因而最后的准确率可能不高。而且因为进行的是单变量统计检验,没有考虑特征间的相互关系。
  • 包装式方法使用模型来筛选特征,通过不断地增加或删除特征,在验证集上测试模型准确率,寻找最优的特征子集。包裹式方法因为有模型的直接参与,因而通常准确性较高,但是因为每变动一个特征都要重新训练模型,因而计算开销大,其另一个缺点是容易过拟合。
  • 嵌入式方法利用了模型本身的特性,将特征选择嵌入到模型的构建过程中。典型的如 Lasso 和树模型等。准确率较高,计算复杂度介于过滤式和包裹式方法之间,但缺点是只有部分模型有这个功能。

1、过滤式方法进行特征选择

a.卡方检验

既然特征选择的目的是去除无关特征,那么什么是无关特征? 对于分类问题,在过滤式方法中一般假设与标签独立的特征为无关特征,而卡方检验恰好可以进行独立性检验,所以其适用于特征选择。如果检验结果是某个特征与标签独立,则可以去除该特征。python实现如下

导入工具包和加载测试数据

from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
from sklearn.datasets import load_iris

#导入IRIS数据集
iris = load_iris()

iris.data如下

array([[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
[4.7, 3.2, 1.3, 0.2],
[4.6, 3.1, 1.5, 0.2],
[5. , 3.6, 1.4, 0.2],
...

进行卡方检验

kafanger = SelectKBest(chi2, k=2) #选择k个最佳特征
kafang_irisdata = kafanger.fit_transform(iris.data, iris.target) #iris.data是特征数据,iris.target是标签数据,该函数可以选择出k个特征

kafang_irisdata如下

array([[1.4, 0.2],
[1.4, 0.2],
[1.3, 0.2],
[1.5, 0.2],
[1.4, 0.2],
...

可以看出,后面两个特征正是卡方检验选择的特征。不信的话我们可以计算卡方检验的得分和P值来验证。得分越高,特征越好;P值越低,特征越好。

计算得分

model.scores_

结果

array([ 10.81782088,   3.7107283 , 116.31261309,  67.0483602 ])

计算P值

model.pvalues_

结果

array([4.47651499e-03, 1.56395980e-01, 5.53397228e-26, 2.75824965e-15])
b.F 检验

F检验,又称ANOVA,方差齐性检验,是用来捕捉每个特征与标签之间的线性关系的过滤方法.它既可以做回归又可以做分类。F检验之前需要先将数据转换成服从正态分布的形式,python代码如下,注意看代码注释,F检验分别有两种方法,分别是单因素方差分析​​f_classif​​用于分类,和线性相关分析f_regression用于回归

from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2,f_classif,f_regression
from sklearn.datasets import load_iris

#导入IRIS数据集
iris = load_iris()
F_classier = SelectKBest(f_classif, k=2) #单因素方差分析用于分类,使用选择k个最佳特征
kafang_irisdata = F_classier.fit_transform(iris.data, iris.target) #iris.data是特征数据,iris.target是标签数据,该函数可以选择出k个特征
# 查看得分和P值
print(F_classier.scores_) # 查看得分
print(F_classier.pvalues_) # 查看P值

相应的,线性相关分析的python代码实现为,

sklearn.feature_selection.SelectKBest(f_regression, k: 选择的特征数)
c.互信息

互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性关系和非线性关系)的过滤方法,可以做回归也可以做分类。

其用法与F检验和卡方检验相同,需要搭配​​SelectKBest​​使用

sklearn.feature_selection.SelectKBest(mutual_info_calssif, k: 选择的特征数)
sklearn.feature_selection.SelectKBest(mutual_indo_regression, k: 选择的特征数)

具体python代码使用例子为

from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2,f_classif,f_regression,mutual_info_classif,mutual_info_regression
from sklearn.datasets import load_iris

#导入IRIS数据集
iris = load_iris()
hu_classier = SelectKBest(mutual_info_classif, k=2) #单因素方差分析用于分类,使用选择k个最佳特征
kafang_irisdata = hu_classier.fit_transform(iris.data, iris.target) #iris.data是特征数据,iris.target是标签数据,该函数可以选择出k个特征
# 查看得分和P值
print(hu_classier.scores_) # 查看得分
print(hu_classier.pvalues_) # 查看P值

2、包装式方法进行特征选择(待更新)

3、嵌入式方法进行特征选择(待更新)

(8)数据特征构造

1、四则运算

比如原来的特征是x1和x2,那么x1+x2就是一个新的特征,或者当x1大于某个数c的时候,就产生一个新的变量x3,并且x3=1,当x1小于c的时候,x3=0,所以这样看来呢,可以按照这种方法构造出很多特征,这个就是构造。

2、特征交叉组合分类特征

交叉特征算是特征工程中非常重要的方法之一了,它是将两个或更多的类别属性组合成一个。当组合的特征要比单个特征更好时,这是一项非常有用的技术。数学上来说,是对类别特征的所有可能值进行交叉相乘。假如拥有一个特征A,A有两个可能值{A1,A2}。拥有一个特征B,存在{B1,B2}等可能值。然后,A&B之间的交叉特征如下:{(A1,B1),(A1,B2),(A2,B1),(A2,B2)},并且你可以给这些组合特征取任何名字,但是需要明白每个组合特征其实代表着A和B各自信息协同作用。一个更好地诠释好的交叉特征的实例是类似于(经度,纬度)。一个相同的经度对应了地图上很多的地方,纬度也是一样。但是一旦你将经度和纬度组合到一起,它们就代表了地理上特定的一块区域,区域中每一部分是拥有着类似的特性。

3、分解分类特征

对于一个特征item_color有‘red’、‘green’、‘unknown’三个取值,那么可以创造一些新的特征例如: - 二值特征has_color: 1知道具体颜色,0表示不知道。这个可以替换item_color特征用到更简单的线性模型中。 - 三个二值特征is_red、is_green和is_unknown。这个可以添加到原有特征中用到决策树模型中。

4、重构数值量

依据实际业务和数据、构造范围二值特征、构造阶段性的统计特征等。例如闯红灯、不管提前几秒都归于闯红灯,可以把提前几秒闯红灯的时间转换成是否闯红灯。第二,假设现在你有一个店铺从2017年1月1日到2020年12月31日的每天的总销售额数据,你要通过这些数据预测该店铺在2021年每天的销售情况。那么我们可以通过每天的销售数据以每周或者每月为阶段,构建每周和每个月的销售总和数据、构建平均每周和每个月的销售平均数据

5、分解Datatime

将一个完整的时间特征分解成年、月、周、日、时、分、秒等时间单位,以此构建新特征,详细说明可以参考上面‘’(6)数据特征转换 - 3、时间序列处理 - a.时间戳处理“

6、窗口变量统计

举个例子,假设现在你有一个店铺从2017年1月1日到2020年12月31日的每天的总销售额数据,你要通过这些数据预测该店铺在2021年每天的销售情况。那么我们可以通过每天的销售数据用7和30为窗口,构建每周和每个月的销售总和数据,构建平均每周和每个月的销售平均数据,通过这些统计变量的方式来构建新的特征。

(9)数据特征提取

可能由于特征矩阵过大,一些样本如果直接使用预测模型算法可能在原始数据中有太多的列被建模,导致计算量大,训练时间长的问题,因此降低特征矩阵维度也是必不可少的。特征提取是一个自动化的降维过程。使得特征太多的样本被建模的维数降低。数据降维有以下几个好处:

  • 避免维度灾难,导致算法失效,降低时间复杂度高
  • 避免高维数据中引入的噪声,防止过拟合
  • 压缩存储,可视化分析

特征提取的方法可以分为两类,一类是线性方法,另一类是非线性方法。

线性方法包括PCA主成分分析、LDA线性判别分析

非线性方法包括保留局部性质的局部线性嵌入、拉普拉斯特征映射;保留全局性质的随机邻域嵌入、t-分布邻域嵌入

1、PCA主成分分析(待更新)
2、LDA线性判别分析(待更新)