精华内容
下载资源
问答
  • 数据准备和特征工程
    千次阅读
    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数据处理与特征工程 课程聚焦数据科学中,数据清洗与分析前的特征提取过程,解决数据科学中最重要的原始数据清洗和特征提取。 【定制课程 精准扫除学习盲点】 课程充分考虑各类实际问题...
  • 特征工程之处理时间序列数据

    千次阅读 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-09-18 21:55:09
    数据预处理、特征工程 二、特征工程 学习的b站上菜菜的课程。        特征工程将原始数据转换为更能代表预测模型的潜在问题的特征的过程,可以挑选最相关的特征,提取特征以及创造特征来...

    数据预处理、特征工程

    二、特征工程

    学习的b站上菜菜的课程。

           特征工程将原始数据转换为更能代表预测模型的潜在问题的特征的过程,可以挑选最相关的特征,提取特征以及创造特征来实现。其中创造特征又经常以降维算法来实现。

    特征工程的目的:降低计算成本,提升模型上限。
    特征工程有三种

    特征提取(feature extraction)从文字,图像,声音等其他非结构化数据中提取新信息作为特征。比如说,从淘宝宝贝名称中提取产品类别、产品颜色、是否是网红产品等等
    特征创造(feature creation)把现有特征进行组合,或互相计算,得到新的特征。比如说,有一列特征是速度,一类是距离,我们就可以通让两列相处,创造出新的特征:通过距离所花的时间
    特征选择(feature selection)在所有的特征中,选择出有意义,对模型有帮助的特征,以避免必须将所有的特征都导入模型去训练的情况。

    在sklearn中用于数据预处理和特征工程的模块
    1)preprocessing:几乎包括数据预处理的所有内容
    2)Impute:填充缺失值专用
    3)feature——selection:包含特征选择的各种方法的实践
    4)decomposition:包含降维算法

    降维本质上是从一个维度空间映射到另一个维度空间,特征的多少别没有减少,当然在映射的过程中 特征值也会相应的变化。举个例子,现在的特征是1000维,我们想要把它降到500维。降维的过程就是找个一个从1000维映射到500维的映射关系。原始数据中的1000个特征,每一个都对应着降维后的500维空间中的一个值。假设原始特征中有个特征的值是9,那么降维后对应的值可能是3。

    特征选择就是单纯地从提取到的所有特征中选择部分特征作为训练集特征,特征在选择前和选择后不改变值,但是选择后的特征维数肯定比选择前小,毕竟我们只选择了其中的一部分特征。举个例子,现在的特征是1000维,现在我们要从这1000个特征中选择500个,那个这500个特征的值就跟对应的原始特征中那500个特征值是完全一样的。对于另个500个没有被选择到的特征就直接抛弃了。假设原始特征中有个特征的值是9,那么特征选择选到这个特征后它的值还是9,并没有改变。

    数据集:

    #导入数据
    import pandas as pd
    data = pd.read_csv()
    
    data.head()
    

    在这里插入图片描述
    该数据有784维(个属性),42000条属性

    1. Filter 过滤法

           过滤方法通常用作预处理,特征选择完全独立于任何机器学习算法,它是根据各种统计检验中的分数以及相关性的各项指标来选择特征。
    过滤法的流程:

    • 全部特征----->最佳特征子集-------->算法———>模型评估

    1.1 方差过滤

    1)VarianceThreshold
           这是通过特征本身的方法来筛选特征的类,比如一个特征本身的方差很小,就表示样本在这个特征上基本没有差异,可能特征中的大多数值都一样,甚至整个特征的取值都相同,那这个特征对于样本区分没有什么作用,所以无论接下来的特征工程要做什么,都要优先消除方差为0 的特征。
           VarianceThreshold有重要参数threshold, 表示是方差的阈值,表示舍弃所有方差小于threshold的特征,不填默认为0,即删除所有的记录都相同的特征。

    form sklearn.feature_selection import VarianceThreshold
    selector = VarianceThreshold() #实例化,不填参数默认方差为0
    X_var0 = selector .fit_transform(X) #获取删除不合格特征之后的新特征矩阵
    
    X_var0.shape
    

    结果:还有4200条数据,708维数据。已经删除了方差为0的特征,明显还需要进一步的特征选择。然而,如果我们知道我们需要多少个特征,方差也可以帮助我们将特征选择一步到位。比如我们希望留一半的特征,那可以设定一个让特征总数减半的阈值,只要找到特征方差的中位数,再将这个中位数作为参数threshold的值输入就好了。

    开始干:

    import numpy as np
    
    X_fsvar = VarianceThreshold(np.median(X.var().values)).fit_transform(X)
    X_fsvar.shape
    #(结果:42000,392)
    

    当特征是二分类时,特征的取值句式伯努利随机变量,这些变量的方差可以计算为:

    • Var[X] = p(1-p)

    其中X是特征矩阵,p是二分类特征中的一类在这个特征中所占的概率。

           若特征是伯努利随机变量,假设p=0.8,即二分类特征中某种分类占到80%以上的时候删除特征。

    X_bvar = VarianceThreshold(.8 * (1 - .8)).fit_transform(X)
    X_bvar.shape
    #结果(42000,685)
    
    • 方差过滤对模型的影响

    通过上面的操作,我们会怀疑,这样做了后,对模型的效会有怎样的影响呢?
    接下来,通过KNN和随机森林来对比一下,(方差过滤前和过滤后)

    1)对于KNN来说,过滤后的效果明显,准确率稍有提升,但是平均运行时间减少了10分钟,特征选择后的算法效率上升了1/3。
    2)对于随机森林来说,随机森林的准确率低于KNN,但是运行时间却连KNN的1%都不到,只主要十几秒,其次,方差过滤后,随机森林的准确率也微弱提升,但运行时间没有什么变化,依然是11s.

    总结啦:
    1. 为什么随机森林可以这么快,又是为何说呢么方差过滤对随机森林没有很大影响?
           这是由于两种算法的原理涉及的计算量不同导致的。KNN,单颗决策树,SVM,神经网络,LR,都需要遍历特征或升维来进行计算,所以他们本身的运算量就大,消耗时间长,所以反差过滤等特征选择对它们就尤为重要。

           但是对于不需要遍历特征的算法,如随机森林,它随机选择特征进行分支,本身运算就快,因此特征选择对他们来说效果不大。无论过滤法如何降低特征的数量,随机森林也只会选取固定数量的特征来建模,而KNN就不同了,特征越少,距离计算的维度就越少,模型明显会随着特征的减少变得轻量。因此(重点来了):

    过滤法的主要对象是需要遍历特征或升维的算法们。主要目的:在维持算法表现的前提下,帮助算法们降低计算成本。**

    2. 过滤法对随机森林无效,却对数模型有效?

           从算法原理来看,传统的决策树需要遍历所有特征,计算不纯度后分支,而随机森林是随机选择特征进行计算和分支,因此随机森林的运算更快,过滤法对随机森林无用,但是对决策树有用。

           在sklearn中,决策树和随机森林都是随机选择特征进行分支的。但决策树在建模中随机抽取的特征数目远超过随机森林中每棵树随机抽取的特征数目(比如这个数据集中780维数据,随机森林每棵树只会抽取10-20个特征,而决策树可能会抽取300~400个特征)。因此,过滤法对随机森林无用,但是对决策树有用。

    对受影响的算法来说,我们可以将方差过滤的影响总结如下:

    阈值很小
    被过滤掉的特征比较少
    阈值比较大
    被过滤掉的特征有很多
    模型表现不会有太大影响可能会变的更好,代表被过滤掉的特征大部分是噪声
    也可能变得糟糕,代表被过滤特征中很多都是有特征
    运行时间可能降低模型的运行时间。基于方差很小的特征有多少,当方差很小的特征不多时,对模型没有太大影响一定能降低模型的运行时间
    算法在遍历特征时的计算越复杂,运行时间下降的越多

           在我们的对比汇总,使用的方差阈值时特征方差的中位数,因此属于阈值比较大,过滤掉的特征比较多。结果显示,无论是KNN还是随机森林,在过滤掉一半特征的之后,模型的精度都上升了,说明被过滤掉的特征在当前随机模式下大部分是噪音。因此我们可以保留这个去掉了一半特征的数据,来为以后的特征选择做准备。如果过滤之后的模型的效果反而更差了,说明,被过滤掉的特征中有很多是有效特征,那就要放弃过滤,使用其他手段进行特征选择。
    虽然随机森林算的快,但是KNN的效果比随机森林更好?可以试试调整n_estimators

    3. 选取超参数threshold?
    问题来了,我们怎么知道方差过滤掉的到底是噪声还是有效特征?过滤后的模型到底会变好还是变坏?
    答案:每个数据集不一样,只能自己去尝试!

          在现实生活中,我们只会用阈值为0或者阈值很小的方差过滤,来优先下厨一些明显用不到的特征,然后会选择更优的特征选择方法继续削减特征数量。

    1.2. 相关性过滤

          方差挑选完后,就要考虑下一个问题:相关性了,如果特征与模型无关,那只会白白浪费计算内训,还可能会给模型点来噪声。在sklearn中,有三种常用的 方法来评判与标签之间的相关性:卡方,F检验,互信息

    1.2.1 卡方过滤

          卡方过滤是专门针对离散型标签(即分类问题)的相关性过滤。卡方检验类feature_selection.chi2计算每个非负特征和便签之间的卡方统计量,并按照卡方统计量由高到低为特征排名。再结合feature_selection.SelectKBest这个可以输入“评分标准”来选出前k个分数最高的特征的类,可以借此除去最可能独立于标签,与分类目的无关的特征。
          另外,如果卡方检验检测到某个特征中所有的值都相同,会提示我们使用方差先进行方差过滤,使用threshold=中位数时完成的方差过滤的数据来做卡方检验(如果方差过滤后的模型的表现反而降低了,那就不使用方差过滤后的数据,而是原数据)

    from sklearn.ensemble import RandomForestClassifier as RFC #随机森林库
    from sklearn.mode1_selection import cross_val_score
    from sklearn.feature_selection import SelectKBest
    from sklearn.feature_selection import chi2 #卡方检验
    
    #假设在这里我一-直我需要300个特征
    x _fschi = SelectKBest(chi2, k=300).fit—_transform(X_fsvar,y) 
    #chi2卡方统计量,k=300,表示选择前300个特征。
    X_fschi.shape
    

    验证一下模型的效果如何

    cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
    

    结果:准确率93.33

    结果说明:模型的效果降低了,说明在设定k=300的时候删除了与模型相关且有效的特征,k值设置太小,需要调整k值,或者放弃相关性过滤。反之,如果模型的表现提升了则说明我们的相关性过滤是有效的,是过滤掉了模型的噪声的,这时候就可以保留相关性过滤的结果。

    1)选取超参数K
    问题:如何设置一个最佳的k值呢,在现实数据中,数据量很大,模型很复杂的时候,希望最开始就能选择一个最优的超参数,那就用:学习曲线

    学习曲线的代码:
    在这里插入图片描述
    运行结果:

    在这里插入图片描述
          通过这条曲线,可以看到,随着K值的不断增加,模型的表现不断上升,这说明,K越大越好,数据中所有的特征都是与特征相关的。但是运行这条曲线的时间同样也是非常地长,接下来我们就来介绍一种更好的选择k的方法:看p值选择k.

          卡方检验的本质是推测两组数据之间的差异,其检验的原假设是“两组数据是相互独立的"。卡方检验返回卡方值和p值两个统计量,其中卡方值很难界定有效的范围,而p值,一般使用0.01或者0.05作为显著性水平。即p值判断的边界,具体如下:

    p值<=0.05或0.01>0.05或0.01
    数据差异差异不是自然形成的这些差异是很自然的样本误差
    相关性两组数据是相关的两组数据是相关独立的
    原假设拒绝原假设,接收备择假设接受原假设

          从特征工程的角度,希望选择卡方值很大的,p值小于0.05的特征,即和标签相关联的特征,而调用SelectKBest之前,可以直接从chi2实例化后的模型中获得各特征所对应的卡方值和p值。
    在这里插入图片描述
    在这里插入图片描述
          可以观察到,所有特征的p值都是0,这说明对于digit recognizor这个数据集来说,方差验证已经把所有和标签无关的特征都剔除了,或者这个数据集本身就不含与标签无关的特征。在这种情况下,舍弃任何一个特征,都会舍弃对模型有用的信息,而使模型表现下降,因此在我们对计算速度感到满意时,我们不需要使用相关性过滤来过滤我们的数据。如果我们认为运算速度太缓慢,那我们可以酌情删除一些特征, 但前提是,我们必须牺牲模型的表现。接下来,我们试试看用其他的相关性过滤方法验证一下我们在这个数据集 上的结论。

    1.2.2 F 检验

          F检验,又称ANOVA,方差齐性检验,是用来捕捉每个特征与标签之间的线性关系的过滤方法,既可以做回归,又可以做分类,因此包含feature_selection.f_classif(F检验分类)和feature_selection.f-regression(回归)两个类,其中F检验分类用于标签是离散型变量的数据,而F检验回归用于标签是连续型变量的数据。
          和卡方检验一样,这两个类需要和类SelectKBest连用,并且我们也可以直接通过输出的统计量来判断到底需要设置一个什么样的K。需要注意的是,F检验在数据服从正态分布时效果会非常稳定,因此如果使用F检验过滤的话,需要先将数据转换成整天分布的方式。

          F检验的本质是寻找两组数据之间的线性关系,其原假设是“数据不存在显著的线性关系"。它返回F值和p值两个统计量。和卡方过滤- -样, 我们希望选取p值小于0.05或0.01的特征,这些特征与标签时显著线性相关的,而p值大于0.05或0.01的特征则被我们认为是和标签没有显著线性关系的特征,应该被删除。以F检验的分类为例,我们继续在数字数据集上来进行特征选择:

    from sklearn.feature_selection import f_classif
    
    F, pvalues_f = f_classif(X_fsvae,y)
    k = F.shape[0] - (pvalues_f > 0.05).sum() #请帮我找一下p值大于0.05的特征有多少,并加起来
    

    结果:和我们使用卡方过滤的到的结论一样,:没有任何特征的p值大于0.01,所有的特征都是和标签相关的,因此我们不需要相关性过滤。

    1. 2.3 互信息法

          互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法,和F检验相似,它既可以做回归和可以做分类,并且包含两个类feature_selection.mutual_info_classif(互信息分类)和feature_selection.mutual_infoo_regression(互信息回归)。这两个类的用法和参数都和F检验一模一样,不过互信息法比F检验更加强大,F检验只能够找出线性关系,而互信息法可以找出任意关系。

          互信息法不返回p值或者F值类似的统计量,它返回每个特征与目标之间的互信息量的估计,这个估计量在【0,1】之间取值,为0则表示两个变量独立,为1则表示两个变量完全相关,以互信息分类为例的代码如下:

    from sklearn.feature_selection import mutual_info_classif as MIC
     reuslt = MIC(X_fsvar,y)
     k = result.shape[0] - sum(result <= 0)
    

    所有特征的互信息量估计都大于0,因此所有特征都与标签相关。

          无论是F检验还是互信息法,都可以使用互信息法,都可以使用学习曲线,只是使用统计量的方法h会更加高效。当统计量判断已经没有特征可以删除的时候,无论学习曲线如何跑,删除特征都只是会降低模型的表现。

    1.3. 过滤法总结

          到这里我们学习了常用的基于过滤法的特征选择,包括方差过滤基于卡方F检验互信息的相关性过滤,讲解了各个过滤的原理和面临的问题,以及怎样调这些过滤类的超参数。通常来说,我会建议,先使用方差过滤,然后使用互信息法来捕捉相关性,不过了解各种各样的过滤方式也是必要的。所有信息被总结在下表,大家自取:

    说明超参数选择
    VarianceThreshold方差过滤,可输入方差阈值,返回方差大于阈值的新特征矩阵看具体时间究竟是含有更多噪声还是更多有效特征,一般使用0或来筛选,也可以画学习曲线或取中位数跑模型来帮助确认
    SelectKBest用来选取K和同级浪结果最佳的特征,生成付姐统计量要求的新特征矩阵看配合使用的统计量
    chi2卡方检验,专用于分类算法,捕捉相关性追求p小于显著性水平的特征
    f_classifF检验分类,只能捕捉线性相关性
    要求数据服从正态分布
    追求p小于显著性水平的特征
    f_regressionF检验回归,只能捕捉线性相关性
    要求数据服从正态分布
    追求p小于显著性水平的特征
    mutual_info_classif互信息分类,可以不合作任何相关性
    不能用于稀疏矩阵
    mutual_info_regression互信息回归,可以捕捉任何相关性
    不能用于稀疏矩阵
    追求互信息估计大于0的特征

    2. 嵌入法

          嵌入法是让算法自己决定使用哪些特征的方法,即特征选择和算法训练同时进行,在使用嵌入法时,先使用某些 机器学习算法和模型进行训练,得到各个特征的权重系数,根据权重系数从大到小选择权值系数比较大的特征,权值系数代表了特征对于模型的某种贡献或者某种重要性,比如决策树和树的继承模型中feature_inportances,可以列出各个特征对数的建立的贡献,我们就可以基于这种贡献的评估,找出对模型建立最有用的特征。相比于过滤法,嵌入法的结果会更加精确到模型的效用本身,对于提高模型效力有更好的效果。由于考虑到特征对模型的贡献,因此无关的特征(需要相关性过滤的特征)和无区分度的特征(需要方差过滤)缺乏对模型的贡献而被删除掉,可谓是过滤法的进化版。

    在这里插入图片描述
    然而嵌入法也有缺点。

          过滤法中使用的统计量可以使用统计知识来查找范围(如p值应当低于显著性水平0.05),而嵌入法中使用的权重系数没有这样的一个范围。可以说,当权重系数为0 的特征是对模型没有租用的,但当大多数特征都 对模型有贡献且贡献不一时,就很难去界定一个有效的临界值。这种情况下,模型权重系数就是我们的超参数,需要学习曲线或者根据模型本身的某些性质去判断这个超参数的最佳值究竟应该是多少。

          另外,嵌入法引入了算法来挑选特征,并且每次挑选都会使用全部特征,因此计算速度也会和应用的算法有很大关系。如果采用计算量跟大,计算缓慢的算法,嵌入法本身也会非常耗时,并且,在选择完毕后,还需要自己来评估模型。

    • feature_selection.SelectFromModel
    class sklearn.feature_selection.SelectFromModel(estimator,threshold=Node,prefit=False,norm_order=1,max_features=None)
    

          SelectFromModel是一个元变换器,可以与任何在拟合后具有coef,feature_importances_属性或参数中可选惩罚项的评估器一起使用(比如随机森林和树模型就具有属性feature_importtances,逻辑回归就带有L1和L2惩罚项,SVM也支持L2惩罚项)。

          对于有feature_importances_的模型来说,如果重要性低于提供的阈值参数,则认为这些特征不重要并被移除,feature_importances_的取值范围是【0,1】,如果设置阈值很小,比如0.001,就可以删除那些对标签预测完全没有贡献的特征,如果设置得很接近1,可能只有一两个特征能够被留下。

    使用惩罚项的嵌入法
          而对于使用惩罚项的模型来说,正则化成大项越大,特征在模型中对应的系数就越小,当正则化惩罚项大到一定程度的时候,部分特征系数就会变成0,当正则化惩罚项继续增大到一定程度时,所有的特征系数都会趋于0,但是一部分特征系数会更容易先变为0,这部分系数是可以筛选的,也就是说,选择特征系数较大的特征,另外,SVM和LR使用参数C来控制返回的特征矩阵的稀疏性,参数C越小,返回的特征越少,LASSO回归,用alpha系系数来控制返回的特征矩阵,alpha的值越大,返回的特征越少。

    嵌入法中的重要参数:

    参数说明
    estimator使用的模型评估器,只要是带feature_mportances_或者coef_属性,或带L1,L2惩罚项的模型都可以使用
    threshold特征重要性的阈值,重要性低于这个阈值的特征都将被删除
    prefit默认False,判断是否将实例化后的模型直接传递给构造函数。如果为True,则必须直接调用fit和transform,不能使用fit_transform,并且SelectFromModel不能与cross_val_score,GridSearchCV和克隆估计器的类似使用程序一起使用
    norm_orderk可输入非零整数,正无穷,负无穷,默认值为1。
    在评估器的coef_属性高于维的情况下, 用于过滤低于阈值的系数的向量的范数的阶数。
    max_features在阈值设定下,要选择的最大特征数,要禁用阈值并仅根据max_feature选择,请设置threshold = -np.inf

    首先重要考虑的是前两个参数,使用随机森林为例,则需要学习曲线来帮助我们寻找最佳特征值。

    from sklearn.feature_selection import SelectFromModel
    form sklearn.ensemble import RandomForestClassifier as RFC
    
    RFC_ = RFC(_estimators = 10,random_state = 0) #随机森林实例化
    X_embedded = SelectFormModel(RFC_,threshold=0.005).fit_transform(X,y)
    #在这里我们只想取出有限的特征,0.05这个阈值对于有780个特征的数据来说,是非常高的阈值,因为平均每个特征只能分到大约0.001的feature_importances_
    X_embedded.shape
    
    #模型的维度明显降低了,
    #同样的,也可以画学习曲线来找最佳阈值
    
    

    在这里插入图片描述

    在这里插入图片描述
          从图像上来看,随着阈值越来越高,模型的效果逐渐变差,被删除的特征越来越多,信息损失也逐渐变大。但是在0.00134之前,模型的效果都可以维持在0.93以上,因此我们可以从中挑选一个数值来验证一下模型的效果。

    #挑选的是0.00067
    X_embedded = SelectFromModel(RFC_,threshold=0.00067.fit_transform(X,y)
    X_embedded.shape
    #结果是324个特征
    cross_val_score(RFC_,X_embedded,y,cv=5).mean()
    

          可以看出,特征个数瞬间缩小到324多,这比我们在方差过滤的时候选择中位数过滤出来的结果392列要小,并且交叉验证分数0.9399高于方差过滤后的结果0.9388,这是由于嵌入法比方差过滤更具体到模型的表现的缘故,换一个算法,使用同样的阈值,效果可能就没有这么好了。和其他调参-样,我们可以在第一条学习曲线后选定一 个范围,使用细化的学习曲线来找到最佳值:

          和其他调参一样,可以在第一条学习曲线后选定一个范围,使用细化的学习曲线来找到最佳值:
    在这里插入图片描述
    在这里插入图片描述
    结果:果然0.00067并不是最高点,真正的最高单0.000564已经将模型效果提升到了94%以上。接下来,使用0.000564来跑一跑我们的SelectFromModel:
    在这里插入图片描述
    代码解释:
    第一句,实例化,RFC_是实例化好的随机森林,threshold是从图像中选出来的值,实例化后 一步到位fit_transform,形成新的特征矩阵。运行后的结果是42000,340列
    第二句话,交叉验证的结果,0.9408335…
    第二句话 重新实例化随机 森林,每个森林跑100个树,而不是10个树,剩下的都一样。结果是96.36…

          得出的特征数目依然小于方差筛选,并且模型的表现也比没有筛选之前更高,已经完全可以和计算一次半小时的KNN相匹敌(KNN的准确率是96.58%),接下来再对随机森林进行调参,准确率应该还可以再升高不少。可见,在嵌入法下,我们很容易就能够实现特征选择的目标:减少计算量,提升模型表现。因此,比起要思考很多统计量的过滤法来说,嵌入法可能是更有效的一种方法。然而,过滤法的计算远远比嵌入法要快,所以大型数据中,我们还是会优先考虑过滤法,或者下面这种结合了过滤和嵌入法的方法:包装法Wrapper

    3. 包装法 Wrapper

    包装法也是一个特征选择和算法训练同时进行的方法,与嵌入法十分相似,他是以来算法自 coef_属性或eature_importances_属性来完成特征选择。但不同的是,我们往往使用一个目标函数 帮助我么选择特征,而不是自己输入某个评估指标或统计量的阈值。包装法在初始特征集上训练评 coef_属性或通过feature_importances_属性获得每个特征的重要性。然后,从当前的一组特征中 特征。在修剪的集合上递归地重复该过程

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

    千次阅读 2020-04-11 11:37:48
      特征工程是比赛中最至关重要的的一块,特别的传统的比赛,大家的模型可能都差不多,调参带来的效果增幅是非常有限的,但特征工程的好坏往往会决定了最终的排名成绩。   特征工程的主要目的还是在于将数据...

    #数据挖掘--第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())))
    
    
    展开全文
  • 不幸的是,船上的每个人都没有足够的救生艇,导致2224名乘客船员中有1502人死亡。虽然生存中有一些运气因素,但似乎有些群体比其他群体更有可能生存下来。在这里,建立一个预测模型来回答这个问题:“什么样的人更...
  • 数据处理与特征工程

    千次阅读 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 ## 解决坐标轴刻度...
  • 要理解特征工程,首先要理解数据(Data)和特征(Feature)的概念概念 特征工程(Feature Engineering)其本质上是一项工程活动,它目的是最大限度地从原始数据中提取特征以供算法和模型使用。 特征工程数据挖掘中...
  • 特征工程——特征构造

    千次阅读 2018-05-26 17:51:47
    特征工程 = 数据准备(for 数据挖掘) 数据清洗、转换 1.1 特征工程主要内容 1.2 特征工程重要性 好数据&amp;amp;amp;amp;amp;gt;多数据&amp;amp;amp;amp;amp;gt;好算法 数据和特征决定了模型预测...
  • 特征工程

    千次阅读 2020-11-04 15:07:37
    特征工程 1 机器学习 机器学习简单来说就是选择一种学习算法,从数据中学习并建立成模型来对新的数据进行预测的计算机科学 。 机器学习是人工智能的一个分支。人工智能的研究是从以“推理”为重点—以“知识”为重点...
  • 特征是什么?...根据《福布斯》的一项调查,数据科学家把 80% 左右的时间花在数据收集、清晰以及预处理等数据准备上。 这点显示了特征工程在数据科学中的重要性。因此有必要整理一下特征工程的主要技术。
  • 机器学习之数据准备

    千次阅读 多人点赞 2018-10-03 00:07:17
    任务包括:表,记录和特征选择以及为了模型工具的数据清洗和转换。 -数据准备是费时间的 纽约时报的一篇文章报道,数据科学家在挖掘出有价值的“金块”之前要花费50%到80%的时间在很多诸如收集数据和准备不规则的...
  • 数据降维和特征选择

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

    万次阅读 多人点赞 2017-11-30 11:58:11
    机器学习里有一句名言:数据和特征决定了机器学习的上限,而模型和算法的应用只是让我们逼近这个上限。这个说法形象且深刻的提出前期数据处理和特征分析的重要性。这一点从我们往往用整个数据挖掘全流程60%以上的...
  • 本篇讲解使用自动化特征工程工具Featuretools,对数据进行自动化特征工程的方法,并借助于BigMart Sales数据集来演示自动化特征工程的相关应用。
  • [特征工程系列一] 论特征的重要性

    千次阅读 2018-02-01 18:08:20
    满打满算,还有十天左右就要过年了,...《特征工程系列二,显性特征的基本处理方法》:讲一下如何处理数据特征,以及最基本的概念《特征工程三,显性特征的衍生》:准备通过NBA球星的数据,展示下特征的衍生的一些概念
  • 本来这些储备知识,我想在后续的实际算法案例中进行解释,但是考虑到很多的小伙伴在学习的过程中都是逐步推进的,需要一定的时间去理解应用,所以前期我们需要把所有的东西都准备好,这样就可以保证后续的算法实践...
  • 特征构建是指通过研究原始数据样本,结合机器学习实战经验相关领域的专业知识,思考问题的潜在形式和数据结构,人工创造出新的特征,而这些特征对于模型训练又是有益的并且具有一定的工程意义。 特征构建的方式...
  • 特征工程】呕心之作——深度了解特征工程

    万次阅读 多人点赞 2018-11-06 10:17:05
    特征工程(Feature Engineering)特征工程是将原始数据转化成更好的表达问题本质的特征的过程,使得将这些特征运用到预测模型中能提高对不可见数据的模型预测精度。 特征工程简单讲就是发现对因变量y有明显影响...
  • 清理标准化文本的整个过程叫做文本预处理(textpreprocessing),其作用是使文本数据没有噪声并且可以分析。 主要包括三个步骤: 移除噪声词汇规范化对象标准化 下图展示了文本预处理流程的结构。 移除噪声 ...
  • FeatureTools是进行特征自动生成的框架,它可以将时间关系数据集转换为可用于机器学习的特征矩阵。 5分钟快速开始 下面是使用深度特征合成(DFS)执行自动化特征工程的示例。在本例中,我...
  • 一文读懂特征工程

    万次阅读 多人点赞 2018-07-31 20:24:01
    一文读懂特征工程   作者:July 说明:本文是七月在线机器学习第九期第五次课 特征工程的课程笔记,课程主讲老师:寒小阳 加号 张雨石 Johnson,本笔记得到寒小阳等相关老师的校对。 时间:二零一八年七月三十...
  • 使用 EDA 和特征工程的组合具有多种优势: 提高准确性 减少训练时间 减少过拟合 简化模型 特征工程技术 有多种特征工程方法可以用于机器学习的各种特定应用和数据类型。这些可以包括: 转换——缩放或编码数据...
  • 特征工程(完)

    千次阅读 2019-02-22 09:08:00
    这也是特征工程系列最后一篇文章,介绍特征提取、特征选择、特征构建三个工作,通常特征工程被认为分为这三方面的内容,只是我将前面的数据&特征预处理部分都加入到这个系列。 实际上,特征工程其实是非常需要通过...
  • 关于MLOps中的数据工程,...随着海量数据处理工具集的发展以及数据和数据格式在种类规模上的不断增长,数据工程越来越成为一门多技术综合应用的学科,用以实现最终业务目标。 数据工程是什么: 数据工程是关注数据
  • ETL工程师:从事系统编程、数据库编程设计,掌握各种常用编程语言的专业技术人员。也称为数据库工程师。 盲目地解释数据仓库的概念可能并不有趣。让我们从不同的角色开始。 老板:我是一家手机公司的老板。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 302,742
精华内容 121,096
关键字:

数据准备和特征工程