精华内容
下载资源
问答
  • 数据准备与特征工程
    千次阅读
    2019-02-10 23:25:34

    机器学习入门系列(2)–如何构建一个完整的机器学习项目,第三篇!

    该系列的前两篇文章:

    分别介绍了确定项目终极目标、选择损失函数、获取数据以及构建测试集,接下来在进入选择算法和训练模型之前,一个很重要的步骤就是特征工程,它包括了对数据的预处理、特征提取、特征分析以及特征构建等几个步骤,可以说能否训练一个好的模型,除了选择合适的算法,准备好数据也是非常关键的!

    由于篇幅问题,所以这篇文章先介绍如何处理缺失值和图片数据扩充的问题,下一篇文章会介绍处理异常值和类别不平衡的问题。


    3 特征工程

    何为特征工程呢?顾名思义,就是对原始数据进行一系列工程处理,将其提炼为特征,作为输入供算法和模型使用。

    本质上讲,特征工程是一个表示和展现数据的过程;实际工作中,特征工程的目的是去除原始数据中的杂质和冗余,设计更高效的特征以刻画求解的问题与预测模型之间的关系。

    特征工程的重要性有以下几点:

    1. 特征越好,灵活性越强。好的特征的灵活性在于它允许你选择不复杂的模型,同时运行速度也更快,也更容易和维护。
    2. 特征越好,构建的模型越简单。好的特征可以在参数不是最优的情况,依然得到很好的性能,减少调参的工作量和时间,也就可以大大降低模型复杂度。
    3. 特征越好,模型的性能越出色。特征工程的目的本来就是为了提升模型的性能。

    3.1 数据预处理

    首先需要对数据进行预处理,一般常用的两种数据类型:

    1. 结构化数据。结构化数据可以看作是关系型数据库的一张表,每列都有清晰的定义,包含了数值型和类别型两种基本类型;每一行数据表示一个样本的信息。
    2. 非结构化数据。主要是文本、图像、音频和视频数据,其包含的信息无法用一个简单的数值表示,也没有清晰的类别定义,并且每个数据的大小互不相同。

    这里主要介绍结构化数据和图像数据两种数据的数据预处理方法。

    3.1.1 处理缺失值

    数据的缺失主要包括记录的缺失和记录中某个字段信息的缺失,两者都会造成分析结果的不准确。

    缺失值产生的原因
    • 信息暂时无法获取,或者获取信息的代价太大。
    • 信息被遗漏,人为的输入遗漏或者数据采集设备的遗漏。
    • 属性不存在,在某些情况下,缺失值并不意味着数据有错误,对一些对象来说某些属性值是不存在的,如未婚者的配偶姓名、儿童的固定收入等。
    缺失值的影响
    • 数据挖掘建模将丢失大量的有用信息。
    • 数据挖掘模型所表现出的不确定性更加显著,模型中蕴含的规律更难把握。
    • 包含空值的数据会使建模过程陷入混乱,导致不可靠的输出。
    缺失值的处理方法
    1. 直接使用含有缺失值的特征:当仅有少量样本缺失该特征的时候可以尝试使用;
    2. 删除含有缺失值的特征:这个方法一般适用于大多数样本都缺少该特征,且仅包含少量有效值是有效的;
    3. 插值补全缺失值

    最常使用的还是第三种插值补全缺失值的做法,这种做法又可以有多种补全方法。

    1. 均值/中位数/众数补全

    如果样本属性的距离是可度量的,则使用该属性有效值的平均值来补全;

    如果样本属性的距离不可度量,则可以采用众数或者中位数来补全。

    1. 同类均值/中位数/众数补全

    对样本进行分类后,根据同类其他样本该属性的均值补全缺失值,当然同第一种方法类似,如果均值不可行,可以尝试众数或者中位数等统计数据来补全。

    1. 固定值补全

    利用固定的数值补全缺失的属性值。

    1. 建模预测

    利用机器学习方法,将缺失属性作为预测目标进行预测,具体为将样本根据是否缺少该属性分为训练集和测试集,然后采用如回归、决策树等机器学习算法训练模型,再利用训练得到的模型预测测试集中样本的该属性的数值。

    这个方法根本的缺陷是如果其他属性和缺失属性无关,则预测的结果毫无意义;但是若预测结果相当准确,则说明这个缺失属性是没必要纳入数据集中的;一般的情况是介于两者之间。

    1. 高维映射

    将属性映射到高维空间,采用独热码编码(one-hot)技术。将包含 K 个离散取值范围的属性值扩展为 K+1 个属性值,若该属性值缺失,则扩展后的第 K+1 个属性值置为 1。

    这种做法是最精确的做法,保留了所有的信息,也未添加任何额外信息,若预处理时把所有的变量都这样处理,会大大增加数据的维度。这样做的好处是完整保留了原始数据的全部信息、不用考虑缺失值;缺点是计算量大大提升,且只有在样本量非常大的时候效果才好

    1. 多重插补

    多重插补认为待插补的值是随机的,实践上通常是估计出待插补的值,再加上不同的噪声,形成多组可选插补值,根据某种选择依据,选取最合适的插补值。

    1. 压缩感知和矩阵补全

    压缩感知通过利用信号本身所具有的稀疏性,从部分观测样本中回复原信号。压缩感知分为感知测量和重构恢复两个阶段。

    • 感知测量:此阶段对原始信号进行处理以获得稀疏样本表示。常用的手段是傅里叶变换、小波变换、字典学习、稀疏编码等

    • 重构恢复:此阶段基于稀疏性从少量观测中恢复原信号。这是压缩感知的核心

    矩阵补全可以查看知乎上的问题–矩阵补全(matrix completion)的经典算法有哪些?目前比较流行的算法是什么?

    1. 手动补全

    除了手动补全方法,其他插值补全方法只是将未知值补以我们的主观估计值,不一定完全符合客观事实。在许多情况下,根据对所在领域的理解,手动对缺失值进行插补的效果会更好。但这种方法需要对问题领域有很高的认识和理解,要求比较高,如果缺失数据较多,会比较费时费力。

    1. 最近邻补全

    寻找与该样本最接近的样本,使用其该属性数值来补全。

    3.1.2 图片数据扩充

    对于图片数据,最常遇到的问题就是训练数据不足的问题。

    一个模型所能获取的信息一般来源于两个方面,一个是训练数据包含的信息;另一个就是模型的形成过程中(包括构造、学习、推理等),人们提供的先验信息。

    而如果训练数据不足,那么模型可以获取的信息就比较少,需要提供更多的先验信息保证模型的效果。先验信息一般作用来两个方面,一是模型,如采用特定的内在结构(比如深度学习的不同网络结构)、条件假设或添加其他约束条件(深度学习中体现在损失函数加入不同正则项);第二就是数据,即根据先验知识来调整、变换或者拓展训练数据,让其展现出更多的、更有用的信息。

    对于图像数据,如果训练数据不足,导致的后果就是模型过拟合问题,即模型在训练样本上的效果不错,但在测试集上的泛化效果很糟糕。过拟合的解决方法可以分为两类:

    1. 基于模型的方法:主要是采用降低过拟合风险的措施,如简化模型(从卷积神经网络变成逻辑回归算法)、添加约束项以缩小假设空间(如 L1、L2等正则化方法)、集成学习、Dropout方法(深度学习常用方法)等;
    2. 基于数据的方法:主要就是数据扩充(Data Augmentation),即根据一些先验知识,在保持特点信息的前提下,对原始数据进行适当变换以达到扩充数据集的效果。具体做法有多种,在保持图像类别不变的前提下,可以对每张图片做如下变换处理。
      • 一定程度内的随机旋转、平移、缩放、裁剪、填充、左右翻转等,这些变换对应着同一个目标在不同角度的观察结果;
      • 对图像中的元素添加噪声扰动,如椒盐噪声、高斯白噪声等;
      • 颜色变换。比如在图像的 RGB 颜色空间进行主成分分析,得到 3 个主成分的特征向量p1,p2,p3以及对应的特征值λ1,λ2,λ3,然后在每个像素的 RGB 值上添加增量[p1,p2,p3]*[a1λ1,a2λ2,a3λ3],其中a1,a2,a3都是均值为 0, 方差较小的高斯分布随机数;
      • 改变图像的亮度、清晰度、对比度、锐度等。

    上述数据扩充方法是在图像空间进行变换的,也可以选择先对图像进行特征提取,然后在图像的特征空间进行变换,利用一些通用的数据扩充或者上采样方法,例如 SMOTE(Synthetic Minority Over-sampling Technique)。

    此外,最近几年一直比较热门的 GAN,生成对抗网络,它的其中一个应用就是生成图片数据,也可以应用于数据扩充。

    最后,还有一种方法可以不需要扩充数据,利用迁移学习的做法,也是如今非常常用的一个方法,微调(Finetuning),即借用在大数据集(如 ImageNet)上预训练好的模型,然后在自己的小数据集上进行微调,这是一种简单的迁移学习,同时也可以快速训练一个效果不错的针对目标类别的新模型。


    小结

    数据特征缺失和图片数据的不足都是机器学习任务中非常常见的问题,因此需要好好掌握如何处理缺失值,以及扩充图片数据的方法。


    参考:


    欢迎关注我的微信公众号–机器学习与计算机视觉,或者扫描下方的二维码,大家一起交流,学习和进步!

    往期精彩推荐

    学习笔记
    数学学习笔记
    Github项目 & 资源教程推荐
    更多相关内容
  • 机器学习中的数据与特征工程.pdf
  • 数据准备特征工程的详细代码实现+注释(百度飞桨)
  • 数据准备特征工程
  • Python数据处理与特征工程

    千人学习 2020-10-11 16:58:28
    CSDN全站首发——Python数据处理与特征工程 课程聚焦数据科学中,数据清洗与分析前的特征提取过程,解决数据科学中最重要的原始数据清洗和特征提取。 【定制课程 精准扫除学习盲点】 课程充分考虑各类实际问题...
  • 数据处理与特征工程

    千次阅读 2021-08-28 15:31:58
    后面代码操作以IRIS鸢尾花数据集为例解析 import numpy as np #科学计算 import pandas as pd #数据清理 import matplotlib.pyplot as plt #可视化 plt.rcParams['axes.unicode_minus'] = False ## 解决坐标轴刻度...

    IRIS数据集介绍:https://baike.baidu.com/item/IRIS/4061453?fr=aladdin

    后面代码操作以IRIS鸢尾花数据集为例解析

    import numpy as np #科学计算
    import pandas as pd #数据清理
    import matplotlib.pyplot as plt #可视化
    plt.rcParams['axes.unicode_minus'] = False  ## 解决坐标轴刻度负号乱码
    plt.rcParams['font.sans-serif'] = ['Simhei']  # 设置中文显示,KaiTi,FangSong
    from sklearn.datasets import load_iris #鸢尾花IRIS数据集
    iris = load_iris()
    iris.data.shape
    

    在这里插入图片描述

    文章目录

    一、基本概念

    定义:
    特征工程是将原始数据转换为更好地代表预测模型的潜在问题的特征的过程,从而提高对未知数据预测的准确性

    在数据挖掘中:特征工程=数据准备

    常用的特征工程方法包括:

    • 数据预处理
    • 特征构造
    • 特征选择
    • 特征转换
    • 特征学习

    二、数据预处理

    2.1 数据清洗(使用Pandas)

    看下面这篇文章学习:
    https://bigxie.blog.csdn.net/article/details/119007040

    在这里插入图片描述注:非需求数据清洗简单来说就是把不要的字段删了
    补充:箱线图
    在这里插入图片描述异常值判断:

    • 1、3σ原理(直方图)
    • 2、四分位数(箱线图)

    2.2 特征预处理

    特征预处理常用方法:

    • 数值型特征无量纲化
    • 数值型特征分箱
    • 类别型特征编码
    • 特征组合

    2.2.1 数值型特征无量纲化

    无量纲化使不同规格的数据转换到同一规格。常见的无量纲化方法有标准化,区间缩放法,正则化

    2.2.1.1 标准化

    标准化是按照数据的列进行处理
    标准化的前提:特征服从正态分布,标准化后,其转化为标准正态分布
    在这里插入图片描述
    代码实现:

    from sklearn.preprocessing import StandardScaler #标准化包
    #标准化,返回值为标准化后的数据
    std = StandardScaler()  #创建模型
    std.fit_transform(iris.data)  #标准化
    
    #fit和transform在不同作用下有不同意义,这里是标准化时候的意义
    #标准化有以下两个动作:
    #第一个动作:fit  均值,标准差求解
    #第二个动作:transform 标准化(x-均值)/std
    

    在这里插入图片描述训练集和测试集如何处理?

    #训练集和测试集情况
    std = StandardScaler()
    std.fit_transform(训练集)
    std.transform(测试集)
    

    测试集不fit的原因:模型追求泛化能力,即对具有同一个规律的数据有很好的预测能力。同一个规律:均值和标准差一样。所以可以用训练集的fit去代替测试集的fit。毕竟训练集的数据数量更多,它的均值和标准差会更接近真实数据的均值和标准差

    绘图观察:

    fig,ax = plt.subplots(1,2,figsize=(8,4)) #1,2:一行两列;8,4:图的大小;fig:返回一个绘图对象;ax:返回一个子图
    #第一个子图,标准化前
    pd.Series(iris.data[:,0]).hist(bins=50,color='green',edgecolor='black',grid=False,ax=ax[0])#ax=ax[0]:表示画的是第一个子图;hist:柱状图;bins:条数;edgecolor:边缘颜色;grid:是否需要格子
    ax[0].set_title('标准化前',fontsize=12)
    ax[0].set_xlabel('花萼长度',fontsize=12)
    ax[0].set_ylabel('频率',fontsize=12)
    
    std = StandardScaler()  #创建模型
    std_data=std.fit_transform(iris.data)  #标准化
    
    #第二个子图,标准化后
    pd.Series(std_data[:,0]).hist(bins=50,color='green',edgecolor='black',grid=False,ax=ax[1])
    ax[1].set_title('标准化后',fontsize=12)
    ax[1].set_xlabel('花萼长度',fontsize=12)
    ax[1].set_ylabel('频率',fontsize=12)
    

    在这里插入图片描述可以观察到:标准化只是把图形移动到原点和缩放,不会改变图形形状和分布

    2.2.1.2 区间缩放法(归一化)

    区间缩放法利用了边界值信息,将特征的取值区间缩放到某个特征的范围,例如[0,1]等。常见的一种是利用两个最值进行缩放
    在这里插入图片描述

    区间缩放也是按照列进行处理

    代码实现:

    from sklearn.preprocessing import MinMaxScaler
    #区间缩放,默认值为缩放到[0, 1]区间的数据
    MinMaxScaler().fit_transform(iris.data)
    #fit和transform要看对象是什么再确定意义是什么
    #比如这里的fit:求最小值和最大值
    #transform:就是上面的公式
    

    在这里插入图片描述
    绘图观察:

    fig,ax = plt.subplots(1,2,figsize=(8,4))
    #第一个子图,归一化前
    pd.Series(iris.data[:,0]).hist(bins=50,color='green',edgecolor='black',grid=False,ax=ax[0])
    ax[0].set_title('归一化前',fontsize=12)
    ax[0].set_xlabel('花萼长度',fontsize=12)
    ax[0].set_ylabel('频率',fontsize=12)
    
    Min_data=MinMaxScaler().fit_transform(iris.data) #归一化
    
    #第二个子图,归一化后
    pd.Series(Min_data[:,0]).hist(bins=50,color='green',edgecolor='black',grid=False,ax=ax[1])
    ax[1].set_title('归一化后',fontsize=12)
    ax[1].set_xlabel('花萼长度',fontsize=12)
    ax[1].set_ylabel('频率',fontsize=12)
    

    在这里插入图片描述

    补充:也可以缩放到任意区间,公式如下
    在这里插入图片描述

    #可以缩放到其他区间  比如缩放到[1,2]这个区间
    MinMaxScaler(feature_range=(1,2)).fit_transform(iris.data)
    

    在这里插入图片描述
    注:区间缩放对异常值的存在非常敏感

    2.2.1.3 正则化

    • 正则化的过程是将每个样本缩放到单位范数(每个样本的范数为1),是依照特征矩阵的处理数据
    • 其目的在于样本向量在点乘运算或其他核函数计算两个样本之间相似性时,拥有统一的标准,也就是说都转化为“单位向量”。规则为l2的正则化公式如下:
      在这里插入图片描述
      代码实现:
    from sklearn.preprocessing import Normalizer
    Normalizer(norm='l2').fit_transform(iris.data)  #默认为l2
    #这里的fit是上面公式分母的操作
    #transform就是上面公式的操作了
    

    在这里插入图片描述

    Normalizer(norm='l1').fit_transform(iris.data)  #l1正则化
    #l1正则化就是上面的公式里面的二次变为1次。上面开了2次根号,l1就不开根号,上面是平方项,l1是绝对值一次方项
    

    在这里插入图片描述

    绘图观察:

    fig,ax = plt.subplots(1,2,figsize=(8,4))
    #第一个子图,正则化前
    pd.Series(iris.data[:,0]).hist(bins=50,color='green',edgecolor='black',grid=False,ax=ax[0])
    ax[0].set_title('正则化前',fontsize=12)
    ax[0].set_xlabel('花萼长度',fontsize=12)
    ax[0].set_ylabel('频率',fontsize=12)
    
    Normal=Normalizer(norm='l2').fit_transform(iris.data)  #默认为l2
    
    #第二个子图,正则化后
    pd.Series(Normal[:,0]).hist(bins=50,color='green',edgecolor='black',grid=False,ax=ax[1])
    ax[1].set_title('正则化后',fontsize=12)
    ax[1].set_xlabel('花萼长度',fontsize=12)
    ax[1].set_ylabel('频率',fontsize=12)
    

    在这里插入图片描述

    2.2.1.4 总结

    标准化与归一化的异同

    • 相同点:都能去掉由于量纲不同引起的误差;都是一种线性变换,都是对向量x按照比例压缩再进行平移
    • 不同点:
      • 目的不同,归一化是为了消除纲量压缩到[0,1]区间
      • 标准化只是调整特征整体的分布
      • 归一化与最大,最小值有关
      • 标准化与均值,标准差有关
      • 归一化输出在[0,1]之间,标准化无限制

    标准化与归一化如何选择

    • 如果对输出结果范围有要求,用归一化
    • 如果数据较为稳定,不存在极端的最大最小值,用归一化
    • 如果数据存在异常值和较多噪音,用标准化,可以间接通过中心化避免异常值和极端值的影响

    归一化与标准化常见应用场景

    • 在分类、聚类算法中,需要使用距离来度量相似性的时候或者使用PCA技术进行降维的时候,标准化表现更好
    • 在不涉及距离度量、协方差计算、数据不符合正态分布的时候,可以使用归一化方法:比如图像处理中,将RGB图像转换为灰度图像后将
      其值限定在[0,1]的范围
    • 基于树的方法不需要进行特征的归一化:例如随机森林,bagging与boosting等方法
    • 如果是基于参数的模型或者基于距离的模型,因为需要对参数或者距离进行计算,都需要进行归一化
    • 建议优先使用标准化,对于输出有要求时再尝试别的方法

    需要掌握
    无量纲化:标准化,归一化,正则化
    1、为什么做
    2、如何做的
    3、产生什么效果

    2.3 数值型特征分箱

    离散化(分箱)是数值型特征非常重要的一个处理,其实就是要将数值型数据转化成类别型数据。连续值的取值空间可能是无穷的,为了便于表示和在模型中处理,需要对连续值特征进行离散化处理

    2.3.1 无监督分箱法

    # 创建一组数据来实践各种分箱方法
    df = pd.DataFrame([[40,1],[13,1],[33,1],[52,0],[16,0],[42,1],[53,1],[39,1],[26,0],[66,0]],columns=['年龄','性别'])
    df
    

    在这里插入图片描述

    2.3.1.1 自定义分箱

    指根据业务经验或者常识等自行设定划分的区间,然后将原始数据归类到各个区间中

    bins=[0,20,40,60,80] #按照区间分箱
    df['自定义分箱']=pd.cut(df['年龄'],bins=bins,labels=['少年','青年','壮年','老年'])
    df
    

    在这里插入图片描述

    2.3.1.2 等距分箱

    按照相同宽度将数据分成几等份。从最小值到最大值之间,均分为 N 等份,缺点是受到异常值的影响比较

    df['等距分箱'] = pd.cut(df['年龄'],3)
    df
    

    在这里插入图片描述

    2.3.1.3 等频分箱

    将数据分成几等份,每等份数据里面的个数是一样

    df['等频分箱'] = pd.qcut(df['年龄'],3)
    df
    

    在这里插入图片描述

    df['等频分箱'].value_counts()  #统计分箱后的结果
    

    在这里插入图片描述

    2.3.1.4 聚类分箱

    基于k均值聚类的分箱,将观测值聚为k类

    在这里插入图片描述

    代码实现:

    data=df['年龄'].values
    data#观察结果是一个一维数组,但是模型里面得用二维数组,所以用data.reshape(-1,1)这行代表把一维数组转化为了二维数组
    

    在这里插入图片描述

    from sklearn.cluster import KMeans
    kmodel=KMeans(n_clusters=3)   #k为聚成几类
    kmodel.fit(data.reshape(-1,1)) #训练模型  变为二维数组后进行聚类,也就是上图红圈操作
    c=pd.DataFrame(kmodel.cluster_centers_) #求聚类中心 ;也就是上图蓝点操作
    c=c.sort_values(by=0) #排序  
    w=c.rolling(window=2).mean().iloc[1:] #用滑动窗口求均值的方法求相邻两项求中点,作为边界点;也就是求上图绿点的操作
    w=[data.min()-1] +list(w[0]) + [data.max()]#把首末边界点加上;-1是因为若是不减,那最小值取不到
    df['聚类分箱'] = pd.cut(data,w)
    df
    

    在这里插入图片描述

    2.3.1.5 二值化

    将数值型的特征进行阀值化得到布尔型数据

    代码实现:

    from sklearn.preprocessing import Binarizer
    #二值化,阈值设置为3,小于等于3为0,大于3为1
    #返回值为二值化后的数据,只有一个=3,那么所有列会对应同一个阈值
    Binarizer(threshold=3).fit_transform(iris.data) #一个阈值对所有的特征
    

    在这里插入图片描述

    #最常用
    Binarizer(threshold=[5,3,1.5,0.2]).fit_transform(iris.data)  #不同的列用不同阈值二值化
    

    在这里插入图片描述

    2.4 类别特征编码

    • 序号编码(LabelEncode):取值有逻辑关系,线性编码
    • 独热编码(哑编码):取值独立,非线性编码,特征变多,drop参数,经常和降维、特征选择连用

    2.4.1 线性编码(标签编码)

    标签编码(LabelEncode):序号编码,对不连续的数字或者文本进行编号,编码值介于0和n-1之间的标签,线性编码方式
    代码实现:

    from sklearn.preprocessing import LabelEncoder
    #dataframe进行编码
    import pandas as pd
    df=pd.DataFrame([['a','青年'],['b','中年'],['c','老年']],columns=['X','Y'])
    df
    

    在这里插入图片描述

    le = LabelEncoder()
    for col in df.columns:
        df[col]=le.fit_transform(df[col])  #每一列单独处理   Unicode编码顺序
        print(col,le.classes_)  #查看编码顺序
    df
    

    在这里插入图片描述

    2.4.2 独热编码

    哑编码,设置一个个数与类型数量相同的全0数组,每一位对应一个类型,如该位为1,该数字表示该类型,非线性编码方式

    2.4.2.1 get_dummies

    import pandas as pd
    list2=[['A', 'B', 'C'], ['B', 'C', 'A'], ['C', 'A', 'B']]
    df1=pd.DataFrame(list2,columns=['P1','P2','P3'])
    df1
    

    在这里插入图片描述

    pd.get_dummies(df1,prefix=['P1','P2','P3'])#prefix列名加一个前缀
    

    在这里插入图片描述

    #因为有一列是冗余的,所以要删掉,这里我们删掉第一列(其实删掉哪一列都行,反正要删一列,习惯性删第一列)
    pd.get_dummies(df1,prefix=['P1','P2','P3'],drop_first=True)  #丢掉第一列
    

    在这里插入图片描述

    2.4.2.2 OneHotEncoder

    代码实现:

    from sklearn.preprocessing import OneHotEncoder
    list2=[['A', 'B', 'C'], ['B', 'C', 'A'], ['C', 'A', 'B']]
    One_hot=OneHotEncoder() #创建模型
    One_hot.fit_transform(list2) #稀疏矩阵的形式存储,很多模型支持稀疏矩阵
    

    在这里插入图片描述
    在这里插入图片描述

    OneHotEncoder(drop='first').fit_transform(list2) #drop参数丢掉一列
    

    在这里插入图片描述
    在这里插入图片描述

    2.5 统计变换

    2.5.1 Log变换

    log变换通常用来创建单调的数据变换。它的主要作用在于帮助稳定方差,始终保持分布接近于正态分布并使得数据与分布的平均值无关
    前提条件:x>1
    在这里插入图片描述
    当数据倾斜分布时,Log变换是很有用的,因为Log变换倾向于拉伸那些落在较低的幅度范围内自变量值的范围,倾向于压缩或减少更高幅度范围内的自变量值的范围,从而使得倾斜分布尽可能的接近正态分布

    代码实现:

    import pandas as pd
    #数据集:freeCodeCamp 做了一个名为coder-survey的调研,以了解成年人学写代码的状况
    data = pd.read_csv('2016-new-coder-survey.csv')
    data.head()
    

    在这里插入图片描述
    绘图观察分布:

    fig, ax = plt.subplots()
    data['Income'].hist(bins=30, color='green', 
                                 edgecolor='black', grid=False)
    ax.set_title('收入柱状图', fontsize=12)
    ax.set_xlabel('收入', fontsize=12)
    ax.set_ylabel('频数', fontsize=12)
    

    在这里插入图片描述

    data['Income_log'] = np.log((1+ data['Income']))#log变换,+1是为了防止负数
    
    income_log_mean = np.mean(data['Income_log'])  #取完log后的均值
    fig, ax = plt.subplots()
    data['Income_log'].hist(bins=30, color='green', 
                                     edgecolor='black', grid=False)
    plt.axvline(income_log_mean, color='r')
    ax.set_title('收入柱状图', fontsize=12)
    ax.set_xlabel('收入', fontsize=12)
    ax.set_ylabel('频数', fontsize=12);
    

    在这里插入图片描述

    2.5.2 Box-Cox变换

    Box-Cox变换是统计建模中常用的一种数据变换,用于连续的响应变量不满足正态分布的情况。该函数有一个前提条件,即数值型值必须先变换为正数。Box-Cox变换之目标有两个,一个是变换后,可以一定程度上减小不可观测的误差和预测变量的相关性。第二个是用这个变换来使得因变量获得一些性质,比如在时间序列分析中的平稳性,或者使得因变量分布为正态分布
    在这里插入图片描述
    代码实现:

    import scipy.stats as spstats
    # 从数据分布中移除非零值
    income = np.array(data['Income'])
    income_clean = income[~np.isnan(income)]#去掉空值为null的
    # 计算最佳λ值
    l, opt_lambda = spstats.boxcox(income_clean)#l是变换后的值,去掉了空值
    print('Optimal lambda value:', opt_lambda)
    # 进行Box-Cox变换
    data['Income_boxcox_lambda_opt'] = spstats.boxcox(data['Income'],lmbda=opt_lambda)#变换后得到最佳λ值
    

    在这里插入图片描述
    绘图观察:

    income_boxcox_mean = np.mean(data['Income_boxcox_lambda_opt'])#经过boxcox变换后的均值
    fig, ax = plt.subplots()
    data['Income_boxcox_lambda_opt'].hist(bins=30,color='green', edgecolor='black', grid=False)
    plt.axvline(income_boxcox_mean, color='r')
    ax.set_title('收入柱状图', fontsize=12)
    ax.set_xlabel('收入', fontsize=12)
    ax.set_ylabel('频数', fontsize=12);
    

    在这里插入图片描述

    2.6 特征组合(特征交叉)

    • 是一种合成特征的方法,可以在多维特征数据集上,进行很好的非线性特征拟合,从而提高模型预测的效果
    • 用来解决欠拟合问题

    2.6.1 连续特征交叉

    • 可以通过多项式特征变换实现特征组合
    • 4个特征,度为2的多项式转换公式如下
      在这里插入图片描述
      代码实现:
    iris.data.shape#鸢尾花数据集为例
    

    在这里插入图片描述

    from sklearn.preprocessing import PolynomialFeatures
    PolynomialFeatures(degree=2).fit_transform(iris.data).shape
    

    在这里插入图片描述

    #表示去掉平方项,interaction_only=True:只保留交叉项
    PolynomialFeatures(degree=2,interaction_only=True).fit_transform(iris.data).shape
    

    在这里插入图片描述

    #include_bias=False:把偏置项(常数项)去掉
    PolynomialFeatures(degree=2,interaction_only=True,include_bias=False).fit_transform(iris.data).shape
    

    在这里插入图片描述

    2.6.2 离散特征交叉

    • 通过笛卡尔积实现特征交叉
    • 比如属性A(年龄)有三个特征(青年,中年,老年),属性B(性别)有两个特征(男,女),笛卡尔积后就有六个组合特征,然后用one hot 给新的特征编码

    代码实现:

    df = pd.DataFrame([['青年','男'],['中年','女'],['老年','男'],['老年','女']],columns=['年龄','性别'])
    df
    

    在这里插入图片描述

    df1=pd.DataFrame(df['年龄'].unique(),columns=['年龄'])
    df1['key']=1
    df2=pd.DataFrame(df['性别'].unique(),columns=['性别'])
    df2['key']=1
    df_12=pd.merge(df1,df2,on='key')
    df_12['年龄-性别']=df_12['年龄']+df_12['性别']
    df_12
    

    在这里插入图片描述

    from sklearn.preprocessing import OneHotEncoder
    OneHot=OneHotEncoder(drop='first')
    OneHot.fit(df_12['年龄-性别'].values.reshape(-1,1))
    OneHot.transform(df['年龄-性别'].values.reshape(-1,1)).toarray()
    

    在这里插入图片描述

    三、特征构造

    特征构造指从现有的数据中构造额外特征,这些特征通常分布在多张相关的表中。特征构造需要从数据中提取相关信息并将其存入单张表格中,然后用来训练机器学习模型

    3.1 时间特征构造

    3.1.1 连续值时间特征

    • 持续时间:单页浏览时长
    • 间隔时间:上次购买/点击离现在的时长;产品上线到现在经过的时长

    代码实现:

    #构造两个时间序列
    time_list = [['2019-01-01 01:22:26', '2019-02-02 04:34:52', '2019-03-03 06:16:40',
        '2019-04-04 08:11:38', '2019-05-05 10:52:39', '2019-06-06 12:06:25'],
        ['2019-07-07 14:05:25', '2019-08-08 16:51:33', '2019-09-09 18:28:28',
        '2019-10-10 20:55:12', '2019-11-11 22:55:12', '2019-12-12 00:55:12']]
    df = pd.DataFrame(time_list).T
    df.columns=['t1','t2']  #列名
    df
    

    在这里插入图片描述

    #类型为object类型,需要转换为时间类型
    df['t1']=pd.to_datetime(df['t1'])
    df['t2']=pd.to_datetime(df['t2'])
    
    df['持续时间']=df['t2']-df['t1'] #时间差,得到有单位的时间
    df['持续时间']
    

    在这里插入图片描述

    pd.Timedelta(‘02:05:00’).seconds/3600 #Timedelta对象有属性:weeks、days、seconds、milliseconds、microseconds和nanoseconds等

    #pd.Timedelta(x).days得到天数;pd.Timedelta(x).seconds/3600/24 得到后面时间的天数
    df['持续时间'].apply(lambda x:pd.Timedelta(x).days+pd.Timedelta(x).seconds/3600/24)
    

    在这里插入图片描述

    3.1.2 离散值时间特征

    import pandas as pd
    date_time_str_list = [
        '2019-01-01 01:22:26', '2019-02-02 04:34:52', '2019-03-03 06:16:40',
        '2019-04-04 08:11:38', '2019-05-05 10:52:39', '2019-06-06 12:06:25',
        '2019-07-07 14:05:25', '2019-08-08 16:51:33', '2019-09-09 18:28:28',
        '2019-10-10 20:55:12', '2019-11-11 22:55:12', '2019-12-12 00:55:12',
    ]
    df = pd.DataFrame({'时间': date_time_str_list})
    df['时间'] = pd.to_datetime(df['时间'])
    df
    

    在这里插入图片描述

    3.1.2.1 时间特征拆解

    • 时间特征拆解:年,月,日,时,分,秒,星期几,一年中的第几天,一年中的第几个周,一天中哪个时间段:凌晨、早晨、上午、中午、下午、傍晚、晚上、深夜,一年中的哪个季度

    代码实现:

    # 年份
    df['年']=df['时间'].dt.year
     
    # 月份
    df['月']=df['时间'].dt.month
     
    # 日 一个月的第几天
    df['日']=df['时间'].dt.day
     
    # 小时  一天的第几个小时
    df['时']=df['时间'].dt.hour
     
    # 分钟  一个小时的第几分钟
    df['分']=df['时间'].dt.minute
     
    # 秒数   一个分钟内的秒数
    df['秒']=df['时间'].dt.second
     
    # 一天中的第几分钟
    df['一天中的第几分钟']=df['时间'].apply(lambda x: x.minute + x.hour*60)
     
    # 星期几,一周的第几天
    df['星期几']=df['时间'].apply(lambda x: x.dayofweek+1)
     
    # 一年中的第几天
    df['一年中的第几天']=df['时间'].dt.dayofyear
     
    # 一年中的第几周
    df['一年中的第几周']=df['时间'].dt.week
     
    # 季度
    df['一年中的第几个季度']=df['时间'].dt.quarter  
        
        
    # 一天中哪个时间段:凌晨、早晨、上午、中午、下午、傍晚、晚上、深夜;
    period_dict ={
        23: '深夜', 0: '深夜', 1: '深夜',
        2: '凌晨', 3: '凌晨', 4: '凌晨',
        5: '早晨', 6: '早晨', 7: '早晨',
        8: '上午', 9: '上午', 10: '上午', 11: '上午',
        12: '中午', 13: '中午',
        14: '下午', 15: '下午', 16: '下午', 17: '下午',
        18: '傍晚',
        19: '晚上', 20: '晚上', 21: '晚上', 22: '晚上',
    }
    df['时间段']=df['时'].map(period_dict)
     
    # 自定义季度
    season_dict = {
        1: '春季', 2: '春季', 3: '春季',
        4: '夏季', 5: '夏季', 6: '夏季',
        7: '秋季', 8: '秋季', 9: '秋季',
        10: '冬季', 11: '冬季', 12: '冬季',
    }
    df['季节']=df['月'].map(season_dict)
    df
    

    在这里插入图片描述

    df.set_index('时间').index.year  #若时间是索引的提取方式
    

    在这里插入图片描述

    3.1.2.2 时间特征判断(得到布尔型数据)

    • 时间特征判断:是否闰年,是否月初,是否月末,是否季节初,是否季节末,是否年初,是否年尾,是否周末,是否公共假期,是否营业
      时间,两个时间间隔之间是否包含节假日/特殊日期

    代码实现:

    date_time_str_list = [
        '2010-01-01 01:22:26', '2011-02-03 04:34:52', '2012-03-05 06:16:40',
        '2013-04-07 08:11:38', '2014-05-09 10:52:39', '2015-06-11 12:06:25',
        '2016-07-13 14:05:25', '2017-08-15 16:51:33', '2018-09-17 18:28:28',
        '2019-10-07 20:55:12', '2020-11-23 22:55:12', '2021-12-25 00:55:12',
        '2022-12-27 02:55:12', '2023-12-29 03:55:12', '2024-12-31 05:55:12',
    ]
    df = pd.DataFrame({'时间': date_time_str_list})
    df['时间'] = df['时间'].apply(lambda x: pd.Timestamp(x))
    df
    

    在这里插入图片描述

    # 是否闰年
    df['是否闰年'] = df['时间'].dt.is_leap_year
    
    # 是否月初,是否是一个月的第一天
    df['是否月初'] = df['时间'].apply(lambda x: x.is_month_start)
     
    # 是否月末,是否是一个月的最后一天
    df['是否月末'] = df['时间'].apply(lambda x: x.is_month_end)
     
    # 是否季节初
    df['是否季节初'] = df['时间'].apply(lambda x: x.is_quarter_start)
     
    # 是否季节末
    df['是否季节末'] = df['时间'].apply(lambda x: x.is_quarter_end)
     
    # 是否年初  每年的第一天
    df['是否年初'] = df['时间'].apply(lambda x: x.is_year_start)
       
    # 是否年尾   每年的最后一天
    df['是否年尾'] = df['时间'].apply(lambda x: x.is_year_end)
     
    # 是否周末
    df['是否周末'] = df['时间'].apply(lambda x: True if x.dayofweek in [5, 6] else False)
     
    # 是否公共假期
    public_vacation_list = [
        '20190101', '20190102', '20190204', '20190205', '20190206',
        '20190207', '20190208', '20190209', '20190210', '20190405',
        '20190406', '20190407', '20190501', '20190502', '20190503',
        '20190504', '20190607', '20190608', '20190609', '20190913',
        '20190914', '20190915', '20191001', '20191002', '20191003',
        '20191004', '20191005', '20191006', '20191007',
    ] # 此处未罗列所有公共假期
    df['日期'] = df['时间'].apply(lambda x: x.strftime('%Y%m%d')) #strftime函数把标准时间转换为咱想要的时间格式
    df['是否公共假期'] = df['日期'].apply(lambda x: True if x in public_vacation_list else False)
     
    # 是否营业时间
    df['是否营业时间'] = False
    df['小时']=df['时间'].apply(lambda x: x.hour)
    df.loc[((df['小时'] >= 8) & (df['小时'] < 22)), '是否营业时间'] = True
     
    df.drop(['日期', '小时'], axis=1, inplace=True)
    df
    

    在这里插入图片描述

    3.2 时间序列特征构造

    按固定时间长度把时间序列划分成多个时间窗,然后构造每个时间窗的特征

    3.2.1 时间序列聚合特征

    • 聚合:把多个时间段的值做一种运算
    • 平均值:历史销售量平均值、最近N天销售量平均值
    • 最小值:历史销售量最小值、最近N天销售量最小值
    • 最大值:历史销售量最大值、最近N天销售量最大值
    • 扩散值:分布的扩散性,如标准差、平均绝对偏差或四分位差,可以反映测量的整体变化趋势
    • 离散系数值:离散系数是策略数据离散程度的相对统计量,主要用于比较不同样本数据的离散程度
    • 分布性:时间序列测量的边缘分布的高阶特效估计(如偏态系数或峰态系数),或者更进一步对命名分布进行统计测试(如标准或统一性),在某些情况下比较有预测力

    代码实现:

    # 加载洗发水销售数据集,每个月的
    df = pd.read_csv('shampoo-sales.csv')
    df 
    

    在这里插入图片描述

    # 平均值
    mean_v = df['Sales'].mean()
    print('mean: {}'.format(mean_v))
     
    # 最小值
    min_v = df['Sales'].min()
    print('min: {}'.format(min_v))
     
    # 最大值
    max_v = df['Sales'].max()
    print('max: {}'.format(max_v))
     
    # 扩散值:标准差
    std_v = df['Sales'].std()
    print('std: {}'.format(std_v))
     
    # 扩散值:平均绝对偏差
    mad_v = df['Sales'].mad()
    print('mad: {}'.format(mad_v))
     
    # 扩散值:四分位差
    q1 = df['Sales'].quantile(q=0.25)  #下四分位
    q3 = df['Sales'].quantile(q=0.75)  #上四分位
    irq = q3 - q1  #四分位距
    print('q1={}, q3={}, irq={}'.format(q1, q3, irq))
     
    # 离散系数
    variation_v = std_v/mean_v
    print('variation: {}'.format(variation_v))
     
    # 分布性:偏态系数
    skew_v = df['Sales'].skew()
    print('skew: {}'.format(skew_v))
    # 分布性:峰态系数
    kurt_v = df['Sales'].kurt()
    print('kurt: {}'.format(kurt_v))
    

    在这里插入图片描述

    3.2.2 时间序列历史特征

    • 前一(或n)个窗口的取值:昨天、前天和3天前的销售量
    • 周期性时间序列前一(或n)周期的前一(或n)个窗口的取值:写字楼楼下的快餐店的销售量一般具有周期性,周期长度为7天,7天前和14天前的销售量

    代码实现:

    # 加载洗发水销售数据集
    df = pd.read_csv('shampoo-sales.csv')
     
    df['-1day'] = df['Sales'].shift(1)#往下移动1天
    df['-2day'] = df['Sales'].shift(2)
    df['-3day'] = df['Sales'].shift(3)
     
    df['-1period'] = df['Sales'].shift(1*12)#移动1年
    df['-2period'] = df['Sales'].shift(2*12)
    df
    

    在这里插入图片描述

    3.2.3 时间序列趋势特征

    • 趋势特征 :趋势特征可以刻画时间序列的变化趋势
    • 窗口差异值特征:一个窗口到下一个窗口的差异
    • 自相关性特征:原时间序列与自身左移一个时间空格(没有重叠的部分被移除)的时间序列相关联

    3.2.3.1 趋势特征

    代码实现:

    df = pd.read_csv('shampoo-sales.csv')
    #↓求最近三天的均值
    df['last 3 day mean'] = (df['Sales'].shift(1) + df['Sales'].shift(2) + df['Sales'].shift(3))/3
    df['最近3天趋势'] = df['Sales'].shift(1)/df['last 3 day mean']
    df
    

    在这里插入图片描述
    绘图观察:

    df1=df.fillna(0) #用0填充空值
    df1
    

    在这里插入图片描述

    df1[['Sales','last 3 day mean']].plot(); #产生平滑效果
    

    在这里插入图片描述

    3.2.3.2 窗口差异值特征

    df = pd.read_csv('shampoo-sales.csv')
    df['最近两月销量差异值'] = df['Sales'].shift(1) - df['Sales'].shift(2)#上个月-下个月
    df
    

    在这里插入图片描述

    3.2.3.3 自相关特征

    print('滞后数为1的自相关系数:{}'.format(df['Sales'].autocorr(1)))#往下移动一格求它的相关系数
    print('滞后数为2的自相关系数:{}'.format(df['Sales'].autocorr(2)))
    

    在这里插入图片描述

    3.3 空间特征构造

    • 按经纬度对空间进行划分
    • 使用坐标拾取系统获取行政区域信息(类别特征)
      • 省份ID/名字
      • 城市ID/名字
      • 市辖区ID/名字
      • 街道ID/名字
    • 结合其他地址计算距离
      • 例如:计算每个地点至某商业中心的距离

    距离类型

    • 曼哈顿距离
    • 欧式距离
    • 球面距离

    3.4 用户行为特征构造

    RFM模型:根据客户活跃程度和交易金额的贡献,进行客户价值细分

    RFM模型三个指标

    • R(Recency)——最近一次消费时间。基于最近一次交易日期计算的得分,距离当前日期越近,得分越高。反映客户交易活跃度
    • F(Frequency)——消费频率。基于交易频率计算的得分,交易频率越高,得分越高。反映客户交易活跃度
    • M(Monetary)——购买金额。基于交易金额计算的得分,交易金额越高,得分越高。反映客户价值

    在这里插入图片描述

    3.5 文本特征构造

    3.5.1 文本统计特征(不常用)

    • 文本长度
    • 单词个数
    • 数字个数
    • 字母个数
    • 大小写单词个数
    • 大小写字母个数
    • 标点符号个数
    • 特殊字符个数
    • 数字占比
    • 字母占比
    • 特殊字符占比
    • 名词个数
    • 动词个数

    3.5.2 词集模型(SoW)

    词集模型( Set Of Words):单词构成的集合,集合中每个元素都只有一个,也即词集中的每个单词都只有一个。把每个词表示为一个很长的向量。这个向量的维度是词表大小,一个维度的值为1,其他元素为0,这个维度就代表了当前的词

    在这里插入图片描述

    3.5.3 词袋模型(BoW)

    词袋模型(Bag Of Words): 统计其出现的次数(频数)
    在这里插入图片描述
    代码实现:

    from sklearn.feature_extraction.text import CountVectorizer  
    
    #语料库(列表形式↓)   每一行句子为一个样本;词与词之间用空格分开
    corpus=["I come to  to  to China travel travel travel", 
        "This is a car to polupar in China",          
        "I love tea and to Apple ",   
        "The work is to write some papers in science"] 
    vectorizer=CountVectorizer()  #创建模型;向量化
    vectorizer.fit_transform(corpus)   #稀疏矩阵形式进行存储
    #这里的fit是拆分词,统计多少个单词,并且给每一个词做一个编码的操作
    #transform是对句子做转换,转换为稀疏矩阵的操作;注意:对测试集不做fit操作,只做transform操作
    

    在这里插入图片描述

    #若想看里面具体数值,用toarray()转化为数组;一般情况下不用转换
    #单个字母词去掉,停用词
    df1=pd.DataFrame(vectorizer.fit_transform(corpus).toarray(),columns=vectorizer.get_feature_names())
    df1
    

    在这里插入图片描述

    在这里插入图片描述

    3.5.4 TF-IDF模型

    • TF-IDF模型:考虑词的重要度
      TF意思是词频(Term Frequency),IDF意思是逆文本频率指数(InverseDocument Frequency)
    • 字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降
    • 一个词语在一篇文章中出现次数越多, 同时在所有文档中出现次数越少, 越能够代表该文章

    在这里插入图片描述

    #TF-IDF
    from sklearn.feature_extraction.text import TfidfVectorizer
    corpus=["I come to  to  to China travel travel travel", 
        "This is a car to polupar in China",          
        "I love tea and to Apple ",   
        "The work is to write some papers in science"] 
    tfidf = TfidfVectorizer() #创建模型
    re = tfidf.fit_transform(corpus)
    re
    

    在这里插入图片描述

    #TF-IDF模型
    pd.DataFrame(re.toarray(),columns=tfidf.get_feature_names())
    

    在这里插入图片描述

    3.5.5 中文文本处理

    首先要安装jieba库
    在[Win+R → cmd]命令提示符里面输入下面那行代码完成安装

    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn jieba
    
    import jieba  #结巴库
    jieba.lcut('我喜欢来中国旅游')#分词
    

    在这里插入图片描述

    ' '.join(jieba.lcut('我喜欢来中国旅游'))#用空格分开字符串
    

    在这里插入图片描述

    corpus=["我喜欢来中国旅游", 
        "这个汽车在中国很流行",          
        "我喜欢茶叶和苹果",   
        "这个工作是写一些科技文献"] 
    corpus_jieba=[' '.join(jieba.lcut(sen)) for sen in corpus]#用空格分词
    corpus_jieba
    

    在这里插入图片描述

    vectorizer=CountVectorizer()#建模
    pd.DataFrame(vectorizer.fit_transform(corpus_jieba).toarray(),columns=vectorizer.get_feature_names())
    

    在这里插入图片描述

    3.6 图形特征构造(图形处理领域用的多)

    • HOG:方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征

    在这里插入图片描述

    • LBP:局部二值模式(Local Binary Pattern)是一种用来描述图像局部纹理特征的算子;它具有旋转不变性和灰度不变性等显著的优点

    在这里插入图片描述

    • Haar-like:Haar特征分为三类:边缘特征、线性特征、中心特征和对角线特征,组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和减去黑色矩形像素和。Haar特征值反映了图像的灰度变化情况

    在这里插入图片描述
    代码实现:

    import matplotlib.pyplot as plt
    img1 = plt.imread("lena_black.jpg")#灰度图
    img1
    

    在这里插入图片描述

    在这里插入图片描述

    img2 = plt.imread("lena_cor.jpg")#RGB彩色图
    img2.shape
    

    在这里插入图片描述

    在这里插入图片描述

    四、特征选择

    当数据预处理完成后,我们需要选择有意义的特征输入机器学习的算法和模型进行训练

    进行特征选择的原因

    • 避免维数灾难:能剔除不相关或冗余的特征,从而达到减少特征个数,提高模型精确度,减少运行时间的目的
    • 降低学习任务的难度:选取出真正相关的特征简化模型,协助理解数
      据产生的过程

    选择特征需要考虑的点

    • 特征是否发散:如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本上没有差异,这个特征对于样本的区分并没有什么用
    • 特征与目标的相关性:与目标相关性高的特征,应当优先选择

    特征选择方法可以分为三种:过滤法、嵌入法和包装法

    4.1 过滤法

    Filter:过滤法,按照发散性或者相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,选择特征

    4.1.1 方差过滤法

    使用方差选择法时,先要计算各个特征的方差,然后根据阈值,选择方差大于阈值的特征

    代码实现:

    from sklearn.datasets import load_iris #导入鸢尾花数据集为例
    iris = load_iris()
    iris.data
    

    在这里插入图片描述

    #方差选择法,先要计算各个特征的方差,然后根据阈值,选择方差大于阈值的特征
    from sklearn.feature_selection import VarianceThreshold
    VarianceThreshold(threshold=0.5).fit_transform(iris.data)#只有方差>0.5才会被选择出来
    #这个结果可以观察到选出来的特征没有名字,若特征一多就不容易分辨哪些特征被选择出来了
    

    在这里插入图片描述
    确定选择特征的字段

    data=pd.DataFrame(iris.data,columns=iris.feature_names)
    data
    

    在这里插入图片描述

    Var=VarianceThreshold(threshold=0.5)
    Var.fit_transform(data) #特征选择
    Var.get_support() #选择了哪些特征;选择的特征会以True形式标识出来
    

    在这里插入图片描述

    data.columns[Var.get_support()]#查看选择的特征字段名
    

    在这里插入图片描述

    4.1.2 卡方过滤

    • 卡方过滤是专门针对离散型标签(即分类问题)的相关性过滤。卡方检验计算每个非负特征和标签之间的卡方统计量,并依照卡方统计量由高到低为特征排名
    • 卡方检验的本质是推测两组数据之间是否相互独立,其检验的原假设是”两组数据之间无关”。卡方检验返回卡方值和P值两个统计量,其中卡方值很难界定有效的范围,而p值,我们一般使用0.01或0.05作为显著性水平,即p值判断的边界
    P值≤0.05或0.01>0.05或0.01
    数据差异差异不是自然形成的这些差异是很自然的样本误差
    相关性两组数据是相关的两组数据是相互独立的
    原假设拒绝原假设,接收备择假设接受原假设

    代码实现:

    from sklearn.feature_selection import SelectKBest #选择K个最好的
    from sklearn.feature_selection import chi2 # 卡方检验
    SelectKBest(chi2, k=3).fit_transform(iris.data, iris.target) #选择三个特征,然后数据和标签放进去,每一列都和标签进行相关性的计算,观察独立性后进行特征选择
    

    在这里插入图片描述

    chi2val, pval = chi2(iris.data, iris.target)#算出卡方值和P值
    (pval<0.05).sum()  #根据P值确定选择的数量k
    

    在这里插入图片描述

    4.1.3 F检验法

    • F检验,又称ANOVA,方差齐性检验,是用来捕捉每个特征与标签之间的线性关系的过滤方法。它既可以做回归也可以做分类,其中F检验分类(f_classif)用于标签是离散型变量的数据,而F检验回归(f_regression)用于标签是连续型变量的数据
    • F检验在数据服从正态分布时效果会非常稳定,因此如果使用F检验过滤,我们会先将数据转换成服从正态分布的方式

    代码实现:

    #分类f_classif,回归f_regression
    from sklearn.feature_selection import f_classif,f_regression
    
    SelectKBest(f_classif, k=3).fit_transform(iris.data, iris.target)
    

    在这里插入图片描述

    F, pval_f = f_classif(iris.data, iris.target)
    pval_f #可以和上面一样选择0.05或0.1来确定k的数量
    

    在这里插入图片描述

    4.1.4 互信息法

    • 经典的互信息也是评价定性自变量对定性因变量的相关性的。互信息是已知一个变量,另外一个变量减少的信息量,在概率论和信息论中,互信息是两个随机变量的间相互依赖性的度量
    • 互信息计算公式如下:

    在这里插入图片描述

    代码实现:

    from sklearn.feature_selection import mutual_info_classif as MIC
    result = MIC(iris.data,iris.target)
    SelectKBest(MIC, k=2).fit_transform(iris.data, iris.target)
    

    在这里插入图片描述
    在这里插入图片描述

    互信息法一般用在特征x和标签y都是离散型的,求概率

    4.2 嵌入法

    Embedded:嵌入法,先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣

    4.2.1 基于树模型的嵌入法

    树模型中可以输出feature_importances_属性,可以列出各个特征对树的建立的贡献,我们就可以基于这种贡献的评估,找出对模型建立最有用的特征

    随机森林代码实现:

    from sklearn.feature_selection import SelectFromModel#从模型里面进行选择
    from sklearn.ensemble import RandomForestClassifier
    clf = RandomForestClassifier()#创建模型
    SelectFromModel(clf,threshold=0.2).fit_transform(iris.data, iris.target)#阈值=0.2,表示选择特征重要性阈值>0.2的
    

    在这里插入图片描述

    在这里插入图片描述

    4.2.2 基于惩罚项模型的嵌入法

    • 用带有L1或L2正则化的项完成特征选择。正则化就是把额外的约束或者惩罚项加到已有模型(损失函数上,以防止过拟合并提高泛化能力
    • L1正则化将系数w的l1范数作为惩罚项加到损失函数上,由于正则项非零,这就迫使那些弱的特征所对应的系数(coef)变成0。因此L1正则化往往会使学到的模型很稀疏(系数w经常为0),这个特性使得L1正则化成为一种很好的特征选择方法
    from sklearn.feature_selection import SelectFromModel
    from sklearn.linear_model import LogisticRegression 
    #带L1惩罚项的逻辑回归作为基模型的特征选择  
    #默认以10^(-5)作为阈值  也就是说求出来的系数如果小于这个阈值,那么就认为这个特征不重要
    SelectFromModel(LogisticRegression(penalty="l1", C=0.1,solver='liblinear')).fit_transform(iris.data, iris.target)
    

    在这里插入图片描述

    #惩罚项   C是正则化的系数
    LR=LogisticRegression(penalty="l1", C=10,solver='liblinear')
    LR.fit(iris.data, iris.target)
    LR.coef_ #得到系数  重要的特征的系数会被求出来
    

    在这里插入图片描述

    4.3 包装法

    Wrapper:包装法,根据目标函数(通常是预测效果评分),每次选择若干特征,或者排除若干特征

    • 包装法在初始特征集上训练评估器,并且通过coef属性或通过feature_importances属性获得每个特征的重要性。然后,从当前的一组特征中修剪最不重要的特征。在修剪的集合上递归地重复该过程,直到最终到达所需数量的要选择的特征
    • 包装法最常用的是递归特征消除(Recursive feature elimination,RFE),主要思想是反复的构建模型(如SVM或者回归模型)然后选出最好的(或者最差的)的特征,然后在剩余的特征上重复这个过程,直到所有特征都遍历了。这个过程中特征被消除的次序就是特征的排序

    递归特征消除法代码实现:

    from sklearn.feature_selection import RFE
    from sklearn.linear_model import LogisticRegression #逻辑回归模型
    Rfe=RFE(estimator=LogisticRegression(), n_features_to_select=2)#n_features_to_select表示选择2个特征
    Rfe.fit_transform(iris.data, iris.target)
    #思想是:比如我们有4个特征,然后从4个特征里面先选3个特征进行组合比较,留下最好的;再从选出的这3个特征里面选2个特征组合进行比较,留下最好的
    

    在这里插入图片描述
    在这里插入图片描述

    五、特征转换

    • 特征转换是指如何从已有的信息里面构造出新的有用的特征,这样可以用更少的特征来更好的描绘出来我们的模型,从而实现降维
    • 特征转换和特征选择不同,特征选择是从我们已有的特征里面选择出来和目标值相关的特征,而特征转换是从已有的特征里面通过变换构造出来一些新的变量
    • 常用实现方式:PCA(Principal Component Analysis),主成成分分析,其目的就是找到高维数据中的主成分,并利用 “ 主成分 ” 数据来表征原始数据,从而达到降维的目的

    5.1 PCA(主成分分析)转换

    信息的衡量

    • 降维过程中,我们会减少特征的数量同时又保留大部分有效信息
    • PCA使用的信息量衡量指标,就是样本方差,又称可解释性方差,方差越大,特征所带的信息量越多

    主成分分析法的理解

    在这里插入图片描述

    • 特征x1和x2,三个样本数据的坐标点分别为(1,1),(2,2),(3,3)
    • 目标:只用一个特征向量来描述这组数据,即将二维数据降为一维数据,并且尽可能地保留信息量,即让数据的总方差尽量和原来一样
    • 将原本的直角坐标系逆时针旋转45°,形成了新的特征向量x1和x2组成的新平面,在新的平面得到新的坐标
    • 根据信息含量的排序,取信息含量最大的一个特征

    代码实现:
    还是以鸢尾花数据集为例
    在这里插入图片描述

    #PCA降维  主成分分析
    from sklearn.decomposition import PCA
    PCA(n_components=4).fit_transform(iris.data)  #n_components表示降到4维
    #先把数据中心化弄成零均值后,再对数据X乘以一个矩阵M(进行坐标轴变换)得到新的X
    #PS:中心化只是减去了均值
    

    在这里插入图片描述

    PCA(n_components=4).fit_transform(iris.data).var(axis=0)  #查看改变后X的方差
    

    在这里插入图片描述

    PCA(n_components=2).fit_transform(iris.data)  #表示降到2维;会把方差最小的两列删掉
    

    在这里插入图片描述

    #某列或某几列方差/方差的和>0.9    若=0.9则降到一维
    PCA(n_components=0.98).fit_transform(iris.data)  #保留多少的信息量
    

    在这里插入图片描述

    from sklearn.decomposition import PCA
    Pca=PCA(n_components=4)
    Pca.fit_transform(iris.data)  #表示降到4维
    Pca.explained_variance_  #可解释方差
    

    在这里插入图片描述

    #cumsum()累积和
    Per=Pca.explained_variance_.cumsum()/Pca.explained_variance_.sum()#累积和/总和
    Per
    

    在这里插入图片描述

    plt.plot(range(1,len(Per)+1),Per); #累计信息量曲线
    #横轴是维度,纵轴是可解释方差
    #特征的数量和总的可解释方差的曲线↓  可以根据曲线决定要降到几维
    

    在这里插入图片描述
    PS:进行降维的时候解释性会变差,性能也会变差一点

    六、特征学习(用于深度学习的图像处理)

    特征学习(feature learning),又叫表示学习(representation learning)或者表征学习,一般指模型自动从数据中抽取特征或者表示的方法,是模型自动学习的过程

    展开全文
  • 特征工程之处理时间序列数据

    千次阅读 2020-09-05 09:40:25
    维基百科对于特征工程的定义是:利用相关领域知识,通过数据挖掘技术从原始数据中提取特征的过程。这些特征可以用来提高机器学习算法的性能。 不过,特征工程不一定非得很花哨。特征工程的一个简单但普遍的处理对象...

    维基百科对于特征工程的定义是:利用相关领域知识,通过数据挖掘技术从原始数据中提取特征的过程。这些特征可以用来提高机器学习算法的性能。

    不过,特征工程不一定非得很花哨。特征工程的一个简单但普遍的处理对象是时间序列数据。特征工程在这个领域的重要性是因为(原始)时间序列数据通常只包含一个表示时间属性的列,即日期时间(或时间戳)。

    对于日期时间数据,特征工程可以看作是从独立的(不同的)特征数据中提取有用的信息。例如,从“2020–07–01 10:21:05”这日期时间数据中,我们可能需要从中提取以下特征:

    1. 月份:7
    2. 本月第几日:1
    3. 周几:周三(通过2020-07-01判断得到)
    4. 时刻:10:21:05

    从日期时间数据中提取这类特征正是本文的目标。之后,我们将结合我们的工程实际中的特征数据,将其作为预测因子,并且建立一个gradient boosting 回归预测模型。具体来说,我们将预测地铁州际交通量。

    本文目录

    本文主要包含以下内容:

    详细阐述如何从时间日期数据中提取以下特征数据:

    1. 月份
    2. 时间数据处于每月第几日
    3. 周几
    4. 时间
    5. 时段分类(早上、下午等)
    6. 周末标记(如果是周末则添加标记1,否则添加标记0)

    如何将上述特种数据用于搭建Gradient Boosting 回归模型,并且实现对于地铁州际交通量的预测

    数据情况

    在本文中,我们使用地铁州际交通量数据集,它可以在UCI机器学习库中找到。
    该数据集是明尼苏达州圣保罗州明尼阿波利斯市I-94的每小时交通量,其中包括2012-2018年的天气和假日数据。这48204行数据包含以下属性:

    1. holiday:类型数据,包含美国国家法定假日、区域假日、明尼苏达州博览会等
    2. temp:数值型数据,平均温度(开尔文)
    3. rain_1h:数值型数据,每小时降雨(毫米)
    4. snow_1h:数值型数据,每小时降雪(毫米)
    5. clouds_all:数值型数据,云层情况(百分比)
    6. weather_main:类型数据,当前天气的分类描述(简要)
    7. weather_description:类型数据,当前天气的分类描述(详细)
    8. data_time:时间序列数据
    9. traffic_volume:数值型数据,每小时I-94 ATR 301记录的西行交通量(本文预测目标)

    接下来,我们首先载入数据:

    # import libraries
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    # load the data
    raw = pd.read_csv('Metro_Interstate_Traffic_Volume.csv')
    # display first five rows
    raw.head()
    # display details for each column
    raw.info()
    

    raw.head()

    raw.info()

    查看info信息,我们发现data_time这一类目是object类型,所以我们需要将其转化为datetime类型:

    # convert date_time column to datetime type
    raw.date_time = pd.to_datetime(raw.date_time)
    

    特征工程

    从上面的info方法的输出中,我们知道除了date_time列之外还有其他的分类特征。但是由于本文的主要主题是处理时间序列数据,我们将重点关注针对date_time的特性工程。

    Month

    Pandas自身有许多易于使用的方法来处理datetime类型的数据。要提取时间/日期信息,我们只需调用pd.Series.dtpd.Series.dt.month是提取month信息所需的函数。这将产生一系列int64格式的月份数字(例如1代表1月,10代表10月)。

    # extract month feature
    months = raw.date_time.dt.month
    

    Day of month

    Month类似,我们只需要调用pd.Series.dt.day函数。以2012-10-27 09:00:00为例,调用该函数提取结果为27。

    # extract day of month feature
    day_of_months = raw.date_time.dt.day
    

    Hour

    类似地,pd.Series.dt.hour将生产对应的小时信息数据(范围为0-23的整数)。

    # extract hour feature
    hours = raw.date_time.dt.hour
    

    Day name

    获取Day name的方式和上面几个数据有所不同。我们想要确定raw.date_time序列中关于星期几的信息,需要以下两个步骤。首先,通过pd.Series.dt.day_name()生成day name序列。然后,我们需要通过pd.get_dummies()进行独热编码(one-hot encode)。

    # first: extract the day name literal
    to_one_hot = raw.date_time.dt.day_name()
    # second: one hot encode to 7 columns
    days = pd.get_dummies(to_one_hot)
    #display data
    days
    

    独热编码后的Day name信息

    Daypart

    在本部分中,我们将基于Hour数据创建一个分组。我们希望有六个小组代表每一天的各个部分。它们是黎明(02.00-05.59)、上午(06.00-09.59)、中午(10.00-13.59)、下午(14.00-17.59)、晚上(18.00-21.59)和午夜(22.00-次日01.59)。

    为此,我们创建了一个标识函数,稍后将使用该函数来作为数据系列的apply方法。然后,我们对得到的dayparts执行一个热编码。

    # daypart function
    def daypart(hour):
        if hour in [2,3,4,5]:
            return "dawn"
        elif hour in [6,7,8,9]:
            return "morning"
        elif hour in [10,11,12,13]:
            return "noon"
        elif hour in [14,15,16,17]:
            return "afternoon"
        elif hour in [18,19,20,21]:
            return "evening"
        else: return "midnight"
    # utilize it along with apply method
    raw_dayparts = hours.apply(daypart)
    # one hot encoding
    dayparts = pd.get_dummies(raw_dayparts)
    # re-arrange columns for convenience
    dayparts = dayparts[['dawn','morning','noon','afternoon','evening','midnight']]
    #display data
    dayparts
    

    独热编码后的Day parts信息

    Weekend flag

    我们从date_time时间序列数据中提取的最后一个特征是is_weekend。这一特征指示给定的日期时间是否在周末(星期六或星期日)。为了实现这一目标,我们将利用pd.Series.dt.day_name()方法以及lambda函数。

    # is_weekend flag 
    day_names = raw.date_time.dt.day_name()
    is_weekend = day_names.apply(lambda x : 1 if x in ['Saturday','Sunday'] else 0)
    

    Holiday flag 以及 weather

    幸运的是,这些数据还包含公共假日信息。信息是细粒度的,因为它提到每个公共假日的名称。尽管如此,本文假设对每个假期进行编码并没有显著的好处。因此,让我们创建一个二进制特性来指示对应的日期是否是假日。

    # is_holiday flag
    is_holiday = raw.holiday.apply(lambda x : 0 if x == "None" else 1)
    

    我们需要考虑的最后一个分类特征是天气。我们只对该特征进行如下独热编码。

    # one-hot encode weather
    weathers = pd.get_dummies(raw.weather_main)
    #display data
    weathers
    

    独热编码后的Weather信息

    特征处理后的数据

    现在,我们终于有了最终的可用于训练的数据!让我们创建一个名为features的全新数据集,它包含所有的特征,包括数值型特征(我们从原始数据中按原样放置)和类型特征(我们设计的特性)。

    # features table
    #first step: include features with single column nature
    features = pd.DataFrame({
        'temp' : raw.temp,
        'rain_1h' : raw.rain_1h,
        'snow_1h' : raw.snow_1h,
        'clouds_all' : raw.clouds_all,
        'month' : months,
        'day_of_month' : day_of_months,
        'hour' : hours,
        'is_holiday' : is_holiday,
        'is_weekend' : is_weekend
    })
    #second step: concat with one-hot encode typed features
    features = pd.concat([features, days, dayparts, weathers], axis = 1)
    # target column
    target = raw.traffic_volume
    

    在我们将数据输入模型之前,我们需要分割数据(训练集和测试集)。请注意,下面我们不随机化我们的数据,这是由于我们的数据具有时间序列特征。

    #split data into training and test data
    X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.1, shuffle = False)
    

    建立回归预测模型

    现在我们准备建立我们的模型来预测地铁州际交通量。在这项工作中,我们将使用Gradient Boosting回归模型。

    该模型的理论和具体细节超出了本文的讨论范围。但是简单来说,gradient-boosting模型属于集成模型,它使用梯度下降算法来降低弱学习模型(决策树)中的预测损失。

    训练模型

    让我们在训练数据上实例化模型并训练模型!

    from sklearn import datasets, ensemble
    # define the model parameters
    params = {'n_estimators': 500,
              'max_depth': 4,
              'min_samples_split': 5,
              'learning_rate': 0.01,
              'loss': 'ls'}
    # instantiate and train the model
    gb_reg = ensemble.GradientBoostingRegressor(**params)
    gb_reg.fit(X_train, y_train)
    

    评价模型

    我们选择两个指标来评价模型:MAPE 和 R2得分。在测试集上使用训练完成的模型进行预测,然后计算这两个指标。

    # define MAPE function
    def mape(true, predicted):        
        inside_sum = np.abs(predicted - true) / true
        return round(100 * np.sum(inside_sum ) / inside_sum.size,2)
    # import r2 score
    from sklearn.metrics import r2_score
    # evaluate the metrics
    y_true = y_test
    y_pred = gb_reg.predict(X_test)
    #print(f"GB model MSE is {round(mean_squared_error(y_true, y_pred),2)}")
    print(f"GB model MAPE is {mape(y_true, y_pred)} %")
    print(f"GB model R2 is {round(r2_score(y_true, y_pred)* 100 , 2)} %")
    

    测试集上的评价指标结果

    我们可以看出我们的模型性能相当不错。我们的MAPE低于15%,而R2得分略高于95%。

    结果可视化

    为了直观理解模型性能,结果可视化很有必要。

    由于我们的测试数据(4820个数据点)的长度,我们只绘制了最后100个数据点上的实际值和模型预测值。此外,我们还包括另一个模型(在下面的绘图代码中称为gb_reg_lite),它不包含日期时间特征作为其预测因子(它只包含非日期时间列作为特征,包括tempweather等)。

    fig, ax = plt.subplots(figsize = (12,6))
    index_ordered = raw.date_time.astype('str').tolist()[-len(X_test):][-100:]
    ax.set_xlabel('Date')
    ax.set_ylabel('Traffic Volume') 
    # the actual values
    ax.plot(index_ordered, y_test[-100:].to_numpy(), color='k', ls='-', label = 'actual')
    # predictions of model with engineered features
    ax.plot(index_ordered, gb_reg.predict(X_test)[-100:], color='b', ls='--', label = 'predicted; with date-time features')
    # predictions of model without engineered features
    ax.plot(index_ordered, gb_reg_lite.predict(X_test_lite)[-100:], color='r', ls='--', label = 'predicted; w/o date-time features')
    every_nth = 5
    for n, label in enumerate(ax.xaxis.get_ticklabels()):
        if n % every_nth != 0:
            label.set_visible(False)
    ax.tick_params(axis='x', labelrotation= 90)
    plt.legend()
    plt.title('Actual vs predicted on the last 100 data points')
    plt.draw()
    

    后100个点的预测结果

    该图中蓝色虚线与黑色实线十分接近。也就是说,我们提出的gradient-boosting模型可以很好地预测地铁交通量。

    同时,我们看到不使用日期时间特征的模型在性能上出现了差异(红色虚线)。为什么会这样?只是因为我们会依赖交通工具,交通流量在周末趋于减少,但在高峰时段出现高峰。因此,如果我们不对日期时间数据进行特征工程处理,我们将错过这些重要的预测因子!

    作者:Pararawendy Indarjo

    deephub翻译组 OliverLee

    展开全文
  • #数据挖掘--第2章:特征工程

    千次阅读 2020-04-11 11:37:48
    #机器学习--第2章:特征工程一、特征工程的...  特征工程的主要目的还是在于将数据转换为能更好地表示潜在问题的特征,从而提高机器学习的性能。比如,异常值处理是为了去除噪声,填补缺失值可以加入先验知识等...

    #数据挖掘--第2章:特征工程


      序言:本系列博客面向初学者,只讲浅显易懂易操作的知识。

    一、特征工程的意义

      特征工程是比赛中最至关重要的的一块,特别的传统的比赛,大家的模型可能都差不多,调参带来的效果增幅是非常有限的,但特征工程的好坏往往会决定了最终的排名和成绩。
      特征工程的主要目的还是在于将数据转换为能更好地表示潜在问题的特征,从而提高机器学习的性能。比如,异常值处理是为了去除噪声,填补缺失值可以加入先验知识等。
      对于知道特征含义(非匿名)的特征工程,特别是在工业类型比赛中,会基于信号处理,频域提取,丰度,偏度等构建更为有实际意义的特征,这就是结合背景的特征构建,在推荐系统中也是这样的,各种类型点击率统计,各时段统计,加用户属性的统计等等,这样一种特征构建往往要深入分析背后的业务逻辑或者说物理原理,从而才能更好的找到 magic。
      当然特征工程其实是和模型结合在一起的,这就是为什么要为 LR NN 做分桶和特征归一化的原因,而对于特征的处理效果和特征重要性等往往要通过模型来验证。
      总的来说,特征工程是一个入门简单,但想精通非常难的一件事。

    二、特征工程


    载入数据

    import numpy as np
    import pandas as pd
    import seaborn as sns
    from scipy import stats
    import matplotlib.pyplot as plt
    from operator import itemgetter
    from sklearn import preprocessing
    import utils  # 这个包是自写包,将在本文末尾放出
    
    train_data = pd.read_csv("二手车交易价格预测/used_car_train_20200313.csv", ' ')
    

    转化数据类型,以节省空间

    bdtype = utils.explore_best_datatype(train_data)
    # 由上节分析可知这三个特征属于无效特征
    del train_data['SaleID'], train_data['seller'], train_data['offerType'], bdtype['SaleID'], bdtype['seller'], bdtype[
        'offerType']
    # 将数据类型转化为最合适的数据类型
    utils.cast_to_datatype(train_data, bdtype)
    

    1、异常处理

    # 1.1 通过箱线图删除异常值
    def box_plot_outliers(data, scale: float = 3):
        """
        利用箱型图处理异常值
        :param data: 待处理的数据
        :param scale: 箱线图尺度,统计 scale * (上四分位点, 下四分位点) 之外的数据
        :return: 返回需要处理的数据行索引
        """
        rule_low = data < (data.quantile(0.25) * scale)
        rule_up = data > (data.quantile(0.75) * scale)
        return np.arange(data.shape[0])[rule_low | rule_up]
    
    
    # 显示处理前的箱型图
    sns.boxplot(train_data['kilometer'])
    plt.show()
    # 将异常值用均值替代
    train_data['kilometer'][box_plot_outliers(train_data['kilometer'], scale=1)] = train_data['kilometer'].mean()
    # 显示处理后的箱型图
    sns.boxplot(train_data['kilometer'])
    plt.show()
    


    - - -

    # 1.2 BOX-COX转换
    # 因为 BOX-COX 变换要求所有数据为正,而通过以往的分析得知,v_7 里有部分数据为0,因此需要提前处理
    # 绘制变换前的频率分布图
    sns.distplot(train_data['v_7'])
    plt.show()
    # 得到所有值为 0 的索引
    index = np.arange(train_data['v_7'].shape[0])[train_data['v_7'] == 0]
    # 将 0 改为一个极小值
    train_data['v_7'][index] = 1e-5
    # 进行变换
    train_data['v_7'] = stats.boxcox(train_data['v_7'])[0]
    # 绘制变换后的频率分布图
    sns.distplot(train_data['v_7'])
    plt.show()
    del index
    



    # 1.3 长尾截断
    # 查看处理前的频率分布
    sns.distplot(train_data['power'])
    plt.show()
    # 分析可知 power 大于 500 的只有 287 个,极其稀少,可以考虑使用长尾截断
    # 得到大于 500 的索引
    index = np.arange(train_data['power'].shape[0])[train_data['power'] > 500]
    # 将大于 500 的值替换为 500
    train_data['power'][index] = 500
    sns.distplot(train_data['power'])
    plt.show()
    del index
    



    2、特征归一化/标准化

    # 2.1 标准化
    # 在不知道样本的最大最小值时,将数据按比例缩放,使之落入一个小的特定区间, 即 y=(x-μ)/σ。如下:
    x_train = np.array([[1., -1., 2.],
                        [2., 0., 0.],
                        [0., 1., -1.]])
    x_scaled = preprocessing.scale(x_train)
    print(x_scaled)
    

    [[ 0. -1.22474487 1.33630621]
    [ 1.22474487 0. -0.26726124]
    [-1.22474487 1.22474487 -1.06904497]]


    # 2.2 归一化
    # 将训练数据区间缩放到[0-1]之间,即 min-max 归一化。如下:
    x_train = np.array([[1., -1., 2.],
                        [2., 0., 0.],
                        [0., 1., -1.]])
    min_max_scaler = preprocessing.MinMaxScaler()
    x_train_minmax = min_max_scaler.fit_transform(x_train)
    print(x_train_minmax)
    

    [[ 0.5 0. 1. ]
    [ 1. 0.5 0.33333333]
    [ 0. 1. 0. ]]


    3、数据分箱

    数据分箱是指将连续特征离散化,多类特征合成少类特征
    数据分箱分为有监督和无监督两大类
    有监督是指提前人为的设置好分类条件,常见的有:卡方分箱、Best-KS分箱
    无监督则无需提前设置分类条件,常见的有:等距分箱、等频分箱、自定义分箱
    这里只介绍等距、等频、自定义分箱

    数据分箱所带来的好处:
    1.一般在建立分类模型时,需要对连续变量离散化,特征离散化后,模型会更稳定,降低了模型过拟合的风险。
    2.离散特征的增加和减少都很容易,易于模型的快速迭代
    3.稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展;
    4.离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰;
    5.逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合;
    6.离散化后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力;
    7.特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问;
    8.特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险。
    9.可以将缺失作为独立的一类带入模型。
    10.将所有变量变换到相似的尺度上。


    # 3.1 等距分箱,修改 bins 和 labels 后即为自定义分箱
    # 注册时间是从19910001至20151212,所以这里按年份进行划分
    # 设置划分区间
    bins = [int(i * 1e4) for i in range(1991, 2017)]
    # 设置每个区间的标签
    labels = [i for i in range(1991, 2016)]
    # 进行划分
    train_data['regDate'] = pd.cut(train_data['regDate'], bins=bins, right=False, labels=labels)
    train_data['regDate'] = train_data['regDate'].astype(utils.explore_best_datatype(train_data['regDate'])[0])
    del bins, labels
    

    # 3.2 等频分箱
    # 将 regDate 按照等频分箱,分为 q=10 组
    labels = [i for i in range(10)]
    train_data['regDate'] = pd.qcut(train_data['regDate'], q=10, labels=labels)
    train_data['regDate'] = train_data['regDate'].astype(utils.explore_best_datatype(train_data['regDate'])[0])
    del labels
    

    4、缺失值处理

    缺失值处理方式有:
    1.不处理
    2.删除
    3.插值补全,包括:均值、众数、中位数、建模预测、多重插补、压缩感知补全、矩阵补全等方法
    4.单独作为一类


    # 4.1 不处理
    # 在上一节分析中,得知 notRepairedDamage 特征含有 ‘-’
    print(train_data['notRepairedDamage'].value_counts())
    # 将异常值 - 替换为 nan
    train_data['notRepairedDamage'].replace('-', np.nan, inplace=True)
    

    # 4.2 删除
    # 在 0 中可以得知, model字段含有 1 个异常值,查看空的那一行
    index = train_data['model'][train_data['model'].isnull()].index
    print(index)
    # 将其删除
    train_data.drop(index=index, inplace=True)
    del index
    

    # 4.3 插值补全
    # 结合实际分析,这几个特征都不太使用插值补全,但这里为了演示,所以用使用
    # 补均值
    train_data['bodyType'].replace(np.nan, train_data['bodyType'].mean(), inplace=True)
    # 补众数
    train_data['bodyType'].replace(np.nan, train_data['bodyType'].mode(), inplace=True)
    # 补中位数
    train_data['bodyType'].replace(np.nan, train_data['bodyType'].median(), inplace=True)
    

    # 4.4 单独作为一类
    # 由上节分析可知,notRepairedDamage 的空值很多,但是又无法删除或填补困难,因此可以考虑将其作为单独一类
    # 将异常值 - 替换为 -1 , 单独作为一类
    train_data['notRepairedDamage'].replace('-', -1, inplace=True)
    

    5、特征工程
    数据决定了模型预测的上限,而算法只是在逼近这个极限而已。1-4 都属于数据清洗,降低噪声干扰,增强数据的表达,并为特征工程做准备。

    机器学习的核心就是特征工程。好的数据是从原始数据抽取出来对预测结果最有用的信息。好的特征应该做到少而精。特征工程就是指把原始数据转变为模型训练数据的过程。
    特征工程需要一定的经验。一般认为特征工程包括:特征提取、特征选择、特征构建三部分。
    特征提取主要手段是通过特征间的关系转换,如组合不同的特征得到新的特征、将某一特征经过某一函数转换成新的特征
    特征选择主要手段是从特征集合中挑选一组特征作为特征子集。
    两者都能帮助减少数据冗余和特征维度。特征提取有时能发现更有意义的特征属性,特征选择的过程经常能表示出每个特征的重要性对于模型构建的重要性。
    特征构建主要手段是通过特征分割和结合。

    结构性的表格数据,可以尝试组合二、三个不同的特征构造新的特征。如果存在时间相关属性,可以划出不同的时间窗口,得到同一特征在不同时间下的特征值。
    总之特征工程是个非常麻烦的问题,书里面也很少提到具体的方法,需要对问题有比较深入的理解,从原始数据中人工的找出一些具有物理意义的特征。
    需要花时间去观察原始数据,思考问题的潜在形式和数据结构,对数据敏感性和机器学习实战经验能帮助特征构建。

    进行特征工程时应该考虑三个问题。一,这个特征是否对目标有用?二,如果有用,这个特征重要性如何?三,这个特征的信息是否在其他特征上体现过?
    列举几个常用特征工程的特征构造方法(但是本博客只挑几个对于本问题有实际意义的方法,就不一一介绍了):
    1.构造统计量特征,报告计数、求和、比例、标准差等;
    2.构造时间特征,包括相对时间和绝对时间,节假日,双休日等;
    3.地理信息,包括分箱,分布编码等方法;
    4.非线性变换,包括 log/ 平方/ 根号等;
    5.特征组合,特征交叉;
    6.仁者见仁,智者见智。


    # 5.1 构造相关统计量
    brand = train_data.groupby("brand")
    brand_info = {}
    for brand_id, data in brand:
        info = {}
        data = data[data['price'] > 0]
        info['brand_amount'] = len(data)
        info['brand_price_max'] = data.price.max()
        info['brand_price_min'] = data.price.min()
        info['brand_price_mean'] = data.price.mean()
        brand_info[brand_id] = info
    brand_info = pd.DataFrame(brand_info).T.reset_index().rename(columns={"index": "brand"})
    train_data = train_data.merge(brand_info, how="left", on="brand")
    del brand, brand_info, brand_id, data, info
    

    # 5.2 构造相对时间特征
    # regDate 为汽车注册时间,creatDate 为汽车售卖时间,因此可以利用这两个构造出新的特征:二手车使用时间
    # usedTime = creatDate - regDate
    # errors="coerce" 表示强制进行转换,如果转换失败则置为NaT   .dt为Series的datetimelike properties的访问器
    train_data['usedTime'] = (pd.to_datetime(train_data['creatDate'], errors="coerce", format="%Y%m%d") -
                              pd.to_datetime(train_data['regDate'], errors="coerce", format="%Y%m%d")).dt.days
    # 可以看到缺失值(转换失败)有 11k 个
    print(train_data['usedTime'].isnull().sum())
    

    
    # 5.6 从邮编中提取城市信息,相当于加入了先验知识
    train_data['city'] = train_data['regionCode'].apply(lambda x: str(x)[:-3])
    train_data['city'].replace('', np.nan, inplace=True)
    

    # 5.6 对类别特征进行 one-hot 编码
    train_data = pd.get_dummies(train_data, columns=['model', 'brand', 'bodyType', 'fuelType',
                                                     'gearbox', 'notRepairedDamage', 'power_bin'])
    

    6、特征筛选(特征筛选属于特征工程中的特征选择)

    # 相关性分析
    print(train_data['power'].corr(train_data['price'], method='spearman'))
    print(train_data['kilometer'].corr(train_data['price'], method='spearman'))
    print(train_data['brand_amount'].corr(train_data['price'], method='spearman'))
    print(train_data['brand_price_average'].corr(train_data['price'], method='spearman'))
    print(train_data['brand_price_max'].corr(train_data['price'], method='spearman'))
    print(train_data['brand_price_median'].corr(train_data['price'], method='spearman'))
    # 当然也可以直接看图
    data_numeric = train_data[['power', 'kilometer', 'brand_amount', 'brand_price_average',
                               'brand_price_max', 'brand_price_median']]
    correlation = data_numeric.corr()
    f, ax = plt.subplots(figsize=(7, 7))
    plt.title('Correlation of Numeric Features with Price', y=1, size=16)
    sns.heatmap(correlation, square=True, vmax=0.8)
    del data_numeric, f, ax, correlation
    


    7、降维
    降维就是指采用某种映射方法,将原高维空间中的数据点映射到低维度的空间中。降维的本质是学习一个映射函数 f : x->y,
    其中x是原始数据点的表达,目前最多使用向量表达形式。 y是数据点映射后的低维向量表达,通常y的维度小于x的维度(当然提高维度也是可以的)。
    主要算法有:
    1.PCA:Principal Component Analysis
    2.LDA:Linear Discriminant Analysis
    3.LLE:Locally linear embedding
    4.Laplacian Eigenmaps
    这里的特征维度不算高,因此这次不进行降维处理,详细算法请参考 https://blog.csdn.net/qq_15719037/article/details/80454113


    utils 源码

    import numpy as np
    import pandas
    import re
    
    
    def isnumber(number: str) -> bool:
        """
        判断给定字符串是否为数字
        :param number: 待判断字符串
        :return: 是数字返回True,否则返回 False
        """
        not_number = ["inf", "-inf", "nan"]
        try:
            if not_number.count(number):
                return False
            float(number)
            return True
        except ValueError:
            return False
    
    
    def isint(number: str) -> bool:
        """
        判断给定的字符串是否可以在不丢失精度的前提下转化为 int
        如:
        number = 0.0 -> True
        number = 0.1 -> False
        number = 1e-1 -> False
        number = 1e1 -> True
        :param number: 待判断字符串
        :return: bool
        """
        try:
            return int(str(float(number)).split('.')[1]) is 0
        except ValueError:
            return False
    
    
    def explore_best_datatype(data: pandas.DataFrame or pandas.Series, name=None) -> dict:
        """
        探索数据的最佳 numpy 数据类型,以节省存储空间
        不考虑特征中的非数字特征值
        :param data: 待探索数据
        :param name: 指定待探索数据特征名称,仅当 data 数据类型为 pandas.Series 时生效
        :return: 返回各个特征的最佳数据类型的字典,其中 __tips 为各个特征所含非数值特征的数量
        """
    
        def _replace(_feature_dtype, _replaced_dtype):
            order = [np.object, np.int8, np.int16, np.int32, np.int64, np.float32, np.float64]
            return _replaced_dtype if order.index(_replaced_dtype) > order.index(_feature_dtype) else _feature_dtype
    
        def _explore(_data: pandas.Series, name, _tips):
            _feature_dtype = np.object
            int_dtype = [np.int8, np.int16, np.int32, np.int64]
            # 如果该特征的数据类型是 int 型
            if int_dtype.count(_data.dtype):
                m_max = _data.max()
                m_min = abs(_data.min())
                m_range = m_max if m_max > m_min else m_min
                # int16
                if m_range > 32767:
                    _feature_dtype = np.int32 if m_range < 2147483647 else np.int64
                else:
                    _feature_dtype = np.int8 if m_range < 127 else np.int16
            else:
                for row in _data:
                    d = str(row)
                    # 对于非数值没必要继续检查
                    if not isnumber(d):
                        if _tips.get(name) is None:
                            _tips.update({name: {d: 1}})
                            # print("feature type '{}' contain non-numeric feature '{}'".format(name, d))
                        elif _tips[name].get(d) is None:
                            _tips[name].update({d: 1})
                            # print("feature type '{}' contain non-numeric feature '{}'".format(name, d))
                        else:
                            _tips[name][d] += 1
                        continue
                    # 如果 d 为浮点数且小数位不全为0
                    elif not isint(d):
                        # 如果小数位大于 6 则应用 numpy.float64 存储
                        _feature_dtype = _replace(_feature_dtype,
                                                  np.float64 if len(d.split('.')[1]) > 6 else np.float32)
                    # 如果 d 为整数或小数位全为0的浮点数
                    else:
                        t = abs(int(d.split('.')[0]))
                        # int16
                        if t > 32767:
                            _feature_dtype = _replace(_feature_dtype,
                                                      np.int32 if t < 2147483647 else np.int64)
                        else:
                            _feature_dtype = _replace(_feature_dtype,
                                                      np.int8 if t < 127 else np.int16)
            return _feature_dtype
    
        feature_dtype = {}
        tips = {}
        if isinstance(data, pandas.DataFrame):
            for col in data.columns:
                feature_dtype.update({col: _explore(data[col], col, tips)})
        elif isinstance(data, pandas.Series):
            if name is None:
                name = data.name
            feature_dtype.update({name: _explore(data, 0, tips)})
        feature_dtype.update({'__tips': tips})
        return feature_dtype
    
    
    def cast_to_datatype(data: pandas.DataFrame or pandas.Series, dtype: dict):
        """
        将数据转换为指定数据类型
        :param data: 待转换数据
        :param dtype: 目标数据类型
        :return: 返回转换后的数据
        """
        tips = dtype['__tips']
        t = dtype.copy()
        del t['__tips']
        for i in t.keys():
            if tips.get(i) is None:
                data[i] = data[i].astype(t[i])
            else:
                print("'{}' has non numerical value {}, quantity is {} please handle it first.".
                      format(i, list(tips[i].keys()), list(tips[i].values())))
    
    
    展开全文
  • 要理解特征工程,首先要理解数据(Data)和特征(Feature)的概念概念 特征工程(Feature Engineering)其本质上是一项工程活动,它目的是最大限度地从原始数据中提取特征以供算法和模型使用。 特征工程数据挖掘中...
  • 2 数据预处理 2.1 无量纲化 2.1.1 标准化 2.1.2 区间缩放法 2.1.3 标准化归一化的区别 2.2 对定量特征二值化 2.3 对定性特征哑编码 2.4 缺失值计算 2.5 数据变换 2.6 回顾3 特征选择 3.1 Fi...
  • 机器学习里数据预处理及特征工程总结(超详细)

    万次阅读 多人点赞 2017-11-30 11:58:11
    机器学习里有一句名言:数据特征决定了机器学习的上限,而模型和算法的应用只是让我们逼近这个上限。这个说法形象且深刻的提出前期...主要包括三部分,一是获取数据数据抽样,二是数据探索,三是数据预处理清洗。
  • 特征工程——特征构造

    千次阅读 2018-05-26 17:51:47
    特征工程 = 数据准备(for 数据挖掘) 数据清洗、转换 1.1 特征工程主要内容 1.2 特征工程重要性 好数据&amp;amp;amp;amp;amp;gt;多数据&amp;amp;amp;amp;amp;gt;好算法 数据和特征决定了模型预测...
  • 1912年4月15日,在她的处女航中,被广泛认为“不沉”的“泰坦尼克号”在冰山相撞后沉没。不幸的是,船上的每个人都没有足够的救生艇,导致2224名乘客和船员中有1502人死亡。虽然生存中有一些运气因素,但似乎有些...
  • 本篇讲解使用自动化特征工程工具Featuretools,对数据进行自动化特征工程的方法,并借助于BigMart Sales数据集来演示自动化特征工程的相关应用。
  • 特征工程

    千次阅读 2019-10-16 22:16:20
    ​ 在商业数据的分析挖掘当中,最常用的数据是结构化数据,其呈现为二维表的结构,数据可以用装载到二维数组当中,其中的每个数可以使用行列进行索引。结构化数据中的每一行称为记录,也可称为样本或实例(视不同...
  • 机器学习之数据准备

    千次阅读 多人点赞 2018-10-03 00:07:17
    04 | 数据准备 数据准备阶段包含从原始数据到形成最终数据集(将要被喂给模型工具的数据)的所有操作。数据准备任务可能要进行多次,没有规定的固定顺序。任务包括:表,记录和特征选择以及为了模型工具的数据清洗...
  • 架构师,这3个字听起来就是大牛级别,程序员的...该项目IBM合作设计和交付,通过系统学习和掌握 Hadoop生态系统、大数据工程工具的深层知识,在数据建模、数据应用方面获得实际经验,你才能在大数据浪潮中乘势而上。
  • [特征工程系列一] 论特征的重要性

    千次阅读 2018-02-01 18:08:20
    满打满算,还有十天左右就要过年了,...《特征工程系列二,显性特征的基本处理方法》:讲一下如何处理数据特征,以及最基本的概念《特征工程三,显性特征的衍生》:准备通过NBA球星的数据,展示下特征的衍生的一些概念
  • 关于MLOps中的数据工程,你一定要知道的.......背景:数据工程是什么:MLOps中的数据工程有哪些看点?1.数据收集2.数据探索3.数据处理5.暗线 背景: 21世纪以来,以Hadoop、Spark、Hive为代表的大数据工具,和以...
  • 特征是什么?...根据《福布斯》的一项调查,数据科学家把 80% 左右的时间花在数据收集、清晰以及预处理等数据准备上。 这点显示了特征工程在数据科学中的重要性。因此有必要整理一下特征工程的主要技术。
  • 总共分为四个notebook,主要讲解机器学习的流程,有监督无监督学习,数据处理与特征工程。 1.背景 在我们学习某个行业之前,首先大概了解一下这个行业的发展,以及要研究的方向,以下是从网上找的一些有关于机器...
  • 特征工程特征工程技术方法

    万次阅读 2015-07-31 15:43:56
    引言在之前学习机器学习技术中,很少关注特征工程(Feature Engineering),...特征工程的重要意义数据特征会直接影响你使用的预测模型和实现的预测结果。准备和选择的特征越好,则实现的结果越好。 影响预测结果好坏的
  • 特征工程】呕心之作——深度了解特征工程

    万次阅读 多人点赞 2018-11-06 10:17:05
    善战者不争,善胜者不怒。君子不争,故天下无之争
  • 本来这些储备知识,我想在后续的实际算法案例中进行解释,但是考虑到很多的小伙伴在学习的过程中都是逐步推进的,需要一定的时间去理解和应用,所以前期我们需要把所有的东西都准备好,这样就可以保证后续的算法实践...
  • 为梯度提升学习选择默认的特征编码策略需要考虑的两个重要因素是训练时间和与特征表示相关的预测性能。Automunge库是处理表格数据常用的库,它可以填充空值,也可以进行分类的编码和归一化等操作,默认的境况下...
  • 数据降维和特征选择

    万次阅读 2016-11-26 11:50:37
    数据降维和特征选择 博主言:本文作为理论和概念整理,不做公式推导和详解计算过程,如需了解相关概念的计算公式和过程,在文后的参考链接中有详细的公式,计算过程和实例。
  • 一文读懂特征工程

    万次阅读 多人点赞 2018-07-31 20:24:01
    一文读懂特征工程   作者:July 说明:本文是七月在线机器学习第九期第五次课 特征工程的课程笔记,课程主讲老师:寒小阳 加号 张雨石 Johnson,本笔记得到寒小阳等相关老师的校对。 时间:二零一八年七月三十...
  • 特征工程(完)

    千次阅读 2019-02-22 09:08:00
    这也是特征工程系列最后一篇文章,介绍特征提取、特征选择、特征构建三个工作,通常特征工程被认为分为这三方面的内容,只是我将前面的数据&特征预处理部分都加入到这个系列。 实际上,特征工程其实是非常需要通过...
  • 机器学习之特征工程

    千次阅读 2018-01-02 17:09:41
    做了一段机器学的东西,一直对特征这块很模糊,有时候凭感觉在找...特征工程是使用专业背景知识和技巧处理数据,使得特征能在机器学习算法上发挥更好的作用的过程。过程包含了特征提取、特征构建、特征选择等模块...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 284,300
精华内容 113,720
热门标签
关键字:

数据准备与特征工程