【特征工程】数据预处理

目录

2.1数据预处理

2.1.1缺失值的填充

2.1.1.1缺失值的填充

2.1.1.2单变量插补

2.1.1.3多变量插补

2.1.1.4标记缺失值

2.1.1.5使用模型填充

2.1.2文本数据编码

2.1.2.1顺序编码

2.1.2.2独热编码

扫描二维码关注公众号,回复: 11026053 查看本文章

2.1.3连续数据离散化

2.1.3.1k-bins离散化

2.1.3.2二值化

2.1.4数据无状态转换

2.1.5数据无量纲化

2.1.5.1标准化

2.1.5.2归一化

2.1.5.3无量纲化的区别和联系

2.1.6其他数据预处理

2.1.6.1Box-Cox变换

2.1.6.2特征衍生


本章节将讲述数据预处理操作,如:

  • 缺失值的填充
  • 文本数据的编码
  • 连续数据离散化
  • 数据无状态转换
  • 数据的无量纲化
  • 数据的特征衍生(是一个正好和特征选择相反的操作,有时候我们对数据进行特征衍生之后,往往可以得到更好的结果。)

操作官方文档阅读,效果更佳!

2.1数据预处理

2.1.1缺失值的填充

2.1.1.1缺失值的填充

这种方法是根据具体的情况进行的填充,我们可以填充最大值、最小值、平均值、方差、随机数等。

df.fillna(value=888)  # 填充为具体值
df.fillna(value=df.mean(axis=0))  # 填充为平均值
df.fillna(value=df.median())  # 填充为中位数
df.fillna(value=df.std())  # 填充为方差
df.fillna(method='ffill') # 填充为该数据前面的值
df.fillna(method='bfill')  # 填充为该数据后面的值

2.1.1.2单变量插补

使用sklearn中提取的类进行插补:

from sklearn.impute import SimpleImputer
# 创建插补的类
spi=SimpleImputer(
    missing_values=np.nan,  # 指定何种占位符表示的缺失值
    strategy='constant',  # 插补策略:mean,median,most_frequent,constant
    fill_value='空值', #
    verbose=0, # 控制imputer的详细程度
    copy=True,
    add_indicator=False, # 变换将堆叠到imputer的变换的输出上。
)
# 对模型进行训练和转换,data就是我们插补之后的数据
data=spi.fit_transform(data)[:,1:]

这里需要注意的是,我们可以将fit和transfrom分开,也可以结合起来使用fit_transform(data)其实它们的功能是一样的,我们还可以通过模型.statistics来查看插补后的每一个样本的插补值.

2.1.1.3多变量插补

sklearn中提供了这么一种估计器,相对来说非常智能,它会将要估计的属性作为输出,将这些数据的其他属性作为输入,通过预测分类的方式估计出输出y,也就是我们要估计的属性,一迭代的方式对每个特征进行预测.不过该类IterativeImputer还处于测试阶段,随机都可能变更,而且需要显示导入enable_iterative_imputer模块.

from sklearn.impute import IterativeImputer
imp=IterativeImputer(
    max_iter=10,
)
data=[[1,2,3],[4,np.nan,6],[7,8,np.nan]]
data=imp.fit_transform(data)
data=np.round(data)
print(data)

当你试过之后才会知道该类的强大.下面我们对该类的参数做一个介绍:

  • estimator=None, # 分类器
  • missing_values=np.nan, # 指定要填充的缺失值
  • max_iter=10, # 最大迭代次数
  • tol=0.001, # 步长 initial_strategy='mean', # 填充的方法
  • min_value=None, # 填充的最小值
  • max_value=None, # 填充的最大值
  • random_state=None, # 随机填充状态
  • add_indicator=False, # 是否继续堆叠

2.1.1.4标记缺失值

略(这种方法不怎常用)

2.1.1.5使用模型填充

除上面几种方法以外,我们还可以用分类,聚类,回归的方法进行预测加填充,有些类似的方法sklearn已经实现了,只需调用即可.eg:IterativeImputer.

2.1.2文本数据编码

首先我们要明白为什么要进行编码?在分类、回归和聚类等机器学习算法当中,特征之间距离的计算非常重要,而距离的计算都是在欧式空间中进行计算的,而我们的one-hot编码可以将离散特征的值扩展到欧式空间中,并对应欧式空间中某个点,这样做确实会让特征之后距离的计算更加合理。

但是如果特征是离散的,如果不用one-hot编码也可以表示特征之间的距离,那么我们就没必要进行one-hot编码了。

2.1.2.1顺序编码

按照0,1,2的顺序进行编码

from sklearn.preprocessing import OrdinalEncoder
data=[['male', 'from US', 'uses Safari'], 
      ['female', 'from Europe', 'uses Firefox'],
      ['female', 'from', 'uses Firefox']]
OrdinalEncoder().fit_transform(data)

结果展示:

array([[1., 2., 1.],
       [0., 1., 0.],
       [0., 0., 0.]])

2.1.2.2独热编码

举个例子:性别有两种,进行独热编码之后就是01,10;国家有三个进行独热编码之后就是001,010,100

from sklearn.preprocessing import OneHotEncoder
data=[['male', 'from US', 'uses Safari'], 
      ['female', 'from Europe', 'uses Firefox'],
      ['female', 'from', 'uses Firefox']]
OneHotEncoder().fit_transform(data).toarray()

结果展示

array([[0., 1., 0., 0., 1., 0., 1.],
       [1., 0., 0., 1., 0., 1., 0.],
       [1., 0., 1., 0., 0., 1., 0.]])

注意某些分类器有助于避免共线性问题,因为如果属性共线性的话,就会导致协方差矩阵不可逆,这样很容易.因此我们可以指定独热编码的drop='first'来解决该问题.

2.1.3连续数据离散化

2.1.3.1k-bins离散化

from sklearn.preprocessing import KBinsDiscretizer
data=np.array([
    [ -3., 5, 15,15],
    [  0, 6, 14,16 ],
    [  0, 3, 11,12 ]
])
kbd=KBinsDiscretizer(
    n_bins=[3,2,2,2], 
    encode='ordinal', # 默认onehot,我们可以使用ordinal进行编码
    strategy='quantile'  # uniform使用固定宽度的bins;
                         # quantile就是每个特征上使用指定的分位数[3,2,2]
                         # kmeans通过聚类的方式填充    
)
kbd.fit_transform(data)

结果展示

array([[0., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 0., 0., 0.]])

2.1.3.2二值化

通过参数threshold指定阈值,大于阈值结果为1,小于阈值结果为0。

from sklearn.preprocessing import Binarizer
data=np.array(
    [[ 1., -1.,  2.],
    [ 2.,  0.,  0.],
    [ 0.,  1., -1.]]
)
b=Binarizer(
    threshold=0.0,
    copy=True
)
b.fit_transform(data)

结果展示

array([[1., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.]])

2.1.4数据无状态转换

通过func参数指定我们对数据的转换规则,该转换是一种无状态转换,这种方式的转换可以忽略数据的大小。

from sklearn.preprocessing import FunctionTransformer
data=np.array([
    [1,2],
    [3,4]
])
ftf=FunctionTransformer(
    func=np.log1p,  # 该参数用来指定无状态转换的规则,使用自然对数非常有用
    validate=True  # 输入之前是否验证
)
ftf.fit_transform(data)

结果展示

array([[0.69314718, 1.09861229],
       [1.38629436, 1.60943791]])

2.1.5数据无量纲化

2.1.5.1标准化

经过处理后的数据符合标准正态分布,均值0,标准差1

首先我们使用sklearn实现标准化:

import numpy as np
from sklearn.preprocessing import StandardScaler
data=np.array([
    [1,2,3],
    [4,5,6],
    [7,8,9]
])
ss=StandardScaler()
ss.fit_transform(data)

标准化的基本公式是

每一个数减去该列的均值然后再除以每一列的方差,axis=0来指定列坐标轴,下面我们就通过公式自己手动实现一个试试:

def StandarScaler(data):
    s=data.std(axis=0)
    X=data.mean(axis=0)
    return (data-X)/s

2.1.5.2归一化

由于原始数据值的范围差异很大,这可能导致具有相同权重的要素,因为数值范围的不同而造成权重的不一致,为此将特征范围归一化,可使得,每个特征对最终距离的贡献大致成比例。归一化的优点:

  • 加快了梯度下降求最优解的速度;
  • 还有可能提高精度

缺点:

  • 如果max和min不稳定,很容易使得归一化结果不稳定,使得后续的使用效果也不稳定,实际使用可以使用经验常量值替代max和min

首先我们使用sklearn中提供的特征区间缩放的类实现一下,哦,对了声明一点就是,归一化是特殊的区间缩放方法,其实就是指定范围的区间缩放。

from sklearn.preprocessing import MinMaxScaler
data=np.array([
    [1,2,3],
    [4,5,6],
    [7,8,9]
])
mms=MinMaxScaler(
    feature_range=(0,2)  # 指定特征的范围
)
mms.fit_transform(data)

这里我们出示一下归一化的公式(区间缩放就是再归一化的基础上做了乘法运算):

下面我们就手动实现一下归一化操作:

def MinMaxScaler(data):
    x_min=data.min(axis=0)
    x_max=data.max(axis=0)
    return (data-x_min)/(x_max-x_min)
print(MinMaxScaler(data))

除此之外,还有非线性归一化。经常用来数据分化比较大的场景,有些数值比较大,有些比较小,通过一些数学函数,将原始值进行映射。该方法包含log、指数、正切等。需要根据数据分布情况,决定非线性函数曲线,比如log(V,2)还是log(V,10)等。

2.1.5.3无量纲化的区别和联系

个人理解归一化是没有弹性的,取值范围再0-1之间,而标准化的取值范围却不确定,即标准化是有弹性的,正常情况下,标准化要比归一化的应用场景更加广泛。

  • 标准化更好的保持了样本之间的距离。当样本中有异常点时,归一化有可能将正常的样本“挤”到一起去。比如三个样本,某个特征的值为1,2,10000,假设10000这个值是异常值,用归一化的方法后,正常的1,2就会被“挤”到一起去。如果不幸的是1和2的分类标签还是相反的,那么,当我们用梯度下降来做分类模型训练时,模型会需要更长的时间收敛,因为将样本分开需要更大的努力!而标准化在这方面就做得很好,至少它不会将样本“挤到一起”。
  • 样本的分布没有发生太大变化。对一个数值特征来说,很大可能它是服从正态分布的。标准化其实是基于这个隐含假设,只不过是略施小技,将这个正态分布调整为均值为0,方差为1的标准正态分布而已。(标准化之后的样本分布基本上没有发生改变)

2.1.6其他数据预处理

2.1.6.1Box-Cox变换

有时候一个因素对我们的属性产生影响,而该属性值的大小有很大程度上影响着其他的属性,其实为其他属性造成影响的不应该算是该因子,为了消除这种不必要的影响,我们就可以采用box-cox变换的方式。这种变化有两种方式,一种是将数据映射成均匀分布;另一种是将数据映射成高斯分布。

将数据映射成均匀分布公式:

from sklearn.preprocessing import QuantileTransformer
data=np.array([
    [1,2,3],
    [4,5,6],
    [7,8,9]
])
qtf=QuantileTransformer()
qtf.fit_transform(data)

将数据映射成高斯分布:

from sklearn.preprocessing import PowerTransformer
data=np.array([
    [1,2,3],
    [4,5,6],
    [7,8,9]
])
ptf=PowerTransformer(
    method='box-cox',
    standardize=False
)
ptf.fit_transform(data)

经过变换之后,我们的数据将更加光滑.

2.1.6.2特征衍生

每个小特征产生的价值大小不同。

在实际业务中,通常我们会拥有十几个或者几十个基础属性,但是大多数是没有太大实际意义的,不适合直接建模,如:用户地址(多种属性值的分类变量)、用户日消费金额(弱数值变量)。而此类变量做一定的变换和组合之后往往会产生更强的信息价值,为我们模型的搭建起到一定的帮助作用,因为我们可以为基础的属性特征做一定的衍生工作,也就是特征的构建。

其实就是从原始数据中构建新的特征,这也是特征选择的一种手段,当我们对某个领域足够的了解之后,我们从原始数据中找到具有物理意义的特征,通过一下几种方式进行衍生:

  • 特征扩展:通过一个特征产生新的特征。发货地址
  • 合成特征:通过多个特征产生一个特征。总金额和商品销量
  • 特征交叉:两个或者多个特征
  • 特征组合

两个变量的衍生:

from sklearn.preprocessing import PolynomialFeatures
data=np.arange(6).reshape(3,2)
ploy=PolynomialFeatures(2)
display(data)
ploy.fit_transform(data)

三个变量的衍生:

某些情况下仅需要要素之间的交叉项,我们可以通过参数interaction_only=True进行设定。

发布了133 篇原创文章 · 获赞 67 · 访问量 9899

猜你喜欢

转载自blog.csdn.net/weixin_43797885/article/details/105320226