乐趣区

关于机器学习:机器学习实战-机器学习特征工程最全解读

作者:韩信子 @ShowMeAI
教程地址:http://www.showmeai.tech/tutorials/41
本文地址:http://www.showmeai.tech/article-detail/208
申明:版权所有,转载请分割平台与作者并注明出处
珍藏 ShowMeAI 查看更多精彩内容

引言

上图为大家相熟的机器学习建模流程图,ShowMeAI 在前序机器学习实战文章 Python 机器学习算法利用实际 中和大家讲到了整个建模流程十分重要的一步,是对于数据的预处理和特色工程,它很大水平决定了最初建模成果的好坏,在本篇内容汇总,咱们给大家开展对数据预处理和特色工程的实战利用细节做一个全面的解读。

特色工程

首先咱们来理解一下「特色工程」,事实上大家在 ShowMeAI 的实战系列文章 Python 机器学习综合我的项目 - 电商销量预估Python 机器学习综合我的项目 - 电商销量预估 < 进阶 > 中曾经看到了咱们做了特色工程的解决。

如果咱们对 特色工程 (feature engineering) 做一个定义,那它指的是:利用畛域常识和现有数据,发明出新的特色,用于机器学习算法;能够手动 (manual) 或主动(automated)。

  • 特色:数据中抽取进去的对后果预测有用的信息。
  • 特色工程:应用业余背景常识和技巧解决数据,使得特色能在机器学习算法上施展更好的作用的过程。

在业界有一个很风行的说法:

数据与特色工程决定了模型的下限,改良算法只不过是迫近这个下限而已。

这是因为,在数据建模上,「现实状态」和「实在场景」是有差异的,很多时候原始数据并不是规矩洁净含意明确充沛的状态:

而特色工程解决,相当于对数据做一个梳理,联合业务提取有意义的信息,以洁净参差地状态进行组织:

特色工程有着十分重要的意义:

  • 特色越好,灵活性越强。只有特色选得好,即便是个别的模型(或算法)也能取得很好的性能,好特色的灵活性在于它容许你抉择不简单的模型,同时运行速度也更快,也更容易了解和保护。
  • 特色越好,构建的模型越简略。有了好的特色,即使你的参数不是最优的,你的模型性能也能依然会体现的很好,所以你就不须要花太多的工夫去寻找最优参数,这大大的升高了模型的复杂度,使模型趋于简略。
  • 特色越好,模型的性能越杰出。显然,这一点是毫无争议的,咱们进行特色工程的最终目标就是晋升模型的性能。

本篇内容,ShowMeAI 带大家一起来零碎学习一下特色工程,包含「特色类型」「数据荡涤」「特色构建」「特色变换」「特征选择」等板块内容。

咱们这里用最简略和罕用的 Titanic 数据集 给大家解说。

Titanic 数据集是非常适合数据迷信和机器学习新手入门练习的数据集,数据集为 1912 年泰坦尼克号沉船事件中一些船员的个人信息以及存活情况。咱们能够依据数据集训练出适合的模型并预测新数据 (测试集) 中的存活情况。

Titanic 数据集能够通过 seaborn 工具库间接加载,如下代码所示:

import pandas as pd
import numpy as np
import seaborn as sns
df_titanic = sns.load_dataset('titanic')

其中数据集的数据字段形容如下图所示:

1. 特色类型

在具体演示 Titanic 的数据预处理与特色工程之前,ShowMeAI 再给大家构建一些对于数据的基础知识。

1.1 结构化 vs 非结构化数据

数据能够分为「结构化数据」和「非结构化数据」,比方在互联网畛域,大部分存储在数据库内的表格态业务数据,都是结构化数据;而文本、语音、图像视频等就属于非结构化数据。

1.2 定量 vs 定性数据

对于咱们记录到的数据,咱们通常又能够以「定量数据」和「定性数据」对齐进行辨别,其中:

  • 定量数据:指的是一些数值,用于掂量数量与大小。

    • 例如高度,长度,体积,面积,湿度,温度等测量值。
  • 定性数据:指的是一些类别,用于形容物品性质。

    • 例如纹理,滋味,气息,色彩等。

如下图是两类数据示例以及它们常见的解决分析方法的总结:

2. 数据荡涤

理论数据挖掘或者建模之前,咱们会有「数据预处理」环节,对原始态的数据进行数据荡涤等操作解决。因为事实世界中数据大体上都是不残缺、不统一的「脏数据」,无奈间接进行数据挖掘,或者开掘后果差强人意。

脏数据 产生的次要成因包含

  • 篡改数据
  • 数据不残缺
  • 数据不统一
  • 数据反复
  • 异样数据

数据荡涤过程包含数据对齐、缺失值解决、异样值解决、数据转化等数据处理办法,如下图所示:

上面咱们留神对上述提到的解决办法做一个解说。

2.1 数据对齐

采集到的原始数据,格局状态不一,咱们会对工夫、字段以及相干量纲等进行数据对齐解决,数据对齐和规整化之后的数据参差统一,更加适宜建模。如下图为一些解决示例:

(1) 工夫

  • 日期格局不统一【2022-02-20202202202022/02/2020/02/2022】。
  • 工夫戳单位不统一,有的用秒示意,有的用毫秒示意。
  • 应用有效工夫示意,工夫戳应用 0 示意,完结工夫戳应用 FFFF 示意。

(2) 字段

  • 姓名写了性别,身份证号写了手机号等。

(3) 量纲

  • 数值类型对立【如 1、2.0、3.21E3、四】。
  • 单位对立【如 180cm、1.80m】。

2.2 缺失值解决

数据缺失是实在数据中常见的问题,因为种种原因咱们采集到的数据并不一定是残缺的,咱们有一些缺失值的常见解决形式:

  • 不解决 (局部模型如XGBoost/LightGBM 等能够解决缺失值)。
  • 删除缺失数据(依照样本维度或者字段维度)。
  • 采纳均值、中位数、众数、同类均值或预估值填充。

具体的解决形式能够开展成下图:

上面回到咱们的 Titanic 数据集,咱们演示一下各种办法:

咱们先对数据集的缺失值状况做一个理解(汇总散布):

df_titanic.isnull().sum()
survived         0
pclass           0
sex              0
age            177
sibsp            0
parch            0
fare             0
embarked         2
class            0
who              0
adult_male       0
deck           688
embark_town      2
alive            0
alone            0

(1) 删除

最间接粗犷的解决是剔除缺失值,行将存在脱漏信息属性值的对象 (字段,样本 / 记录) 删除,从而失去一个齐备的信息表。优缺点如下:

  • 长处:简单易行,在对象有多个属性缺失值、被删除的含缺失值的对象与初始数据集的数据量相比十分小的状况下无效;
  • 有余:当缺失数据所占比例较大,特地当脱漏数据非随机散布时,这种办法可能导致数据产生偏离,从而引出谬误的论断。

在咱们以后 Titanic 的案例中,embark_town字段有 2 个空值,思考删除缺失解决下。

df_titanic[df_titanic["embark_town"].isnull()]
df_titanic.dropna(axis=0,how='any',subset=['embark_town'],inplace=True)

(2) 数据填充

第 2 大类是咱们能够通过一些办法去填充缺失值。比方基于统计办法、模型办法、联合业务的办法等进行填充。

① 手动填充

依据业务知识来进行人工手动填充。

② 非凡值填充

将空值作为一种非凡的属性值来解决,它不同于其余的任何属性值。如所有的空值都用 unknown 填充。个别作为长期填充或两头过程。

代码实现

df_titanic['embark_town'].fillna('unknown', inplace=True)

③ 统计量填充

若缺失率较低,能够依据数据分布的状况进行填充。罕用填充统计量如下:

  • 中位数:对于数据存在歪斜散布的状况,采纳中位数填补缺失值。
  • 众数:离散特色可应用众数进行填充缺失值。
  • 平均值:对于数据合乎均匀分布,用该变量的均值填补缺失值。

中位数填充——fare:缺失值较多,应用中位数填充

df_titanic['fare'].fillna(df_titanic['fare'].median(), inplace=True) 

众数填充——embarked:只有两个缺失值,应用众数填充

df_titanic['embarked'].isnull().sum()
#执行后果:2
df_titanic['embarked'].fillna(df_titanic['embarked'].mode(), inplace=True)
df_titanic['embarked'].value_counts()
#执行后果:#S    64

同类均值填充

age:依据 sex、pclass 和 who 分组,如果落在雷同的组别里,就用这个组别的均值或中位数填充。

df_titanic.groupby(['sex', 'pclass', 'who'])['age'].mean()
age_group_mean = df_titanic.groupby(['sex', 'pclass', 'who'])['age'].mean().reset_index()
def select_group_age_median(row):
    condition = ((row['sex'] == age_group_mean['sex']) &
                (row['pclass'] == age_group_mean['pclass']) &
                (row['who'] == age_group_mean['who']))
    return age_group_mean[condition]['age'].values[0]

df_titanic['age'] =df_titanic.apply(lambda x: select_group_age_median(x) if np.isnan(x['age']) else x['age'],axis=1)

④ 模型预测填充

如果其余无缺失字段丰盛,咱们也能够借助于模型进行建模预测填充,将待填充字段作为 Label,没有缺失的数据作为训练数据,建设分类 / 回归模型,看待填充的缺失字段进行预测并进行填充。

最近间隔邻法(KNN)

  • 先依据欧式间隔或相干剖析来确定间隔具备缺失数据样本最近的 K 个样本,将这 K 个值加权均匀 / 投票来预计该样本的缺失数据。

回归(Regression)

  • 基于残缺的数据集,建设回归方程。对于蕴含空值的对象,将已知属性值代入方程来预计未知属性值,以此估计值来进行填充。当变量不是线性相关时会导致有偏差的预计,罕用线性回归。

咱们以 Titanic 案例中的 age 字段为例,解说一下:

  • age 缺失量较大,这里咱们用 sex、pclass、who、fare、parch、sibsp 六个特色构建随机森林模型,填充年龄缺失值。
df_titanic_age = df_titanic[['age', 'pclass', 'sex', 'who','fare', 'parch', 'sibsp']]
df_titanic_age = pd.get_dummies(df_titanic_age)
df_titanic_age.head()
# 乘客分成已知年龄和未知年龄两局部
known_age = df_titanic_age[df_titanic_age.age.notnull()]
unknown_age = df_titanic_age[df_titanic_age.age.isnull()]
# y 即指标年龄
y_for_age = known_age['age']
# X 即特色属性值
X_train_for_age = known_age.drop(['age'], axis=1)
X_test_for_age = unknown_age.drop(['age'], axis=1)
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)
rfr.fit(X_train_for_age, y_for_age)
# 用失去的模型进行未知年龄后果预测
y_pred_age = rfr.predict(X_test_for_age)
# 用失去的预测后果填补原缺失数据
df_titanic.loc[df_titanic.age.isnull(), 'age'] = y_pred_age
sns.distplot(df_titanic.age)

⑤ 插值法填充

还能够用插值法对数据填充,细分一下包含线性插值、多重插补、热平台插补、拉格朗日插值、牛顿插值等。

线性插值法

应用插值法能够计算缺失值的估计值,所谓的插值法就是通过两点 (x0, y0),(x1, y1) 预计两头点的值。假如 y =f(x)是一条直线,通过已知的两点来计算函数 f(x),而后只有晓得 x 就能求出 y,以此办法来预计缺失值。

.interpolate(method = 'linear', axis)办法将通过 linear 插值应用沿着给定 axis 的值替换 NaN 值,这个差值也就是前后或者高低的两头值

df_titanic['fare'].interpolate(method = 'linear', axis = 0)

同时,也可用行值插入

df_titanic['fare'].interpolate(method = 'linear', axis = 1)

多重插补(Multiple Imputation)

多值插补的思维来源于贝叶斯预计,认为待插补的值是随机的,它的值来自于已观测到的值。具体实际上通常是预计出待插补的值,而后再加上不同的噪声,造成多组可选插补值。依据某种抉择根据,选取最合适的插补值。

多重插补办法分为三个步骤:

  • ① 为每个空值产生一套可能的插补值,这些值反映了无响应模型的不确定性;每个值都能够被用来插补数据集中的缺失值,产生若干个残缺数据汇合;
  • ② 每个插补数据汇合都用针对残缺数据集的统计办法进行统计分析;
  • ③ 对来自各个插补数据集的后果,依据评分函数进行抉择,产生最终的插补值。

⑥ 哑变量填充

有另外一种十分有意思的填充形式,叫做「哑变量填充」,在变量为离散型,且不同值较少的状况下能够采纳,以 Titanic 数据为例:

  • 性别 SEX 变量,存在 male,fameal,NA(缺失)三个不同的值,可将该列转换成IS_SEX_MALEIS_SEX_FEMALEIS_SEX_NA
  • 若某个变量存在十几个不同的值,可依据每个值的频数,将频数较小的值归为一类other,升高维度。此做法可最大化保留变量的信息。

以下为参考代码示例:

sex_list = ['MALE', 'FEMALE', np.NaN, 'FEMALE', 'FEMALE', np.NaN, 'MALE']
df = pd.DataFrame({'SEX': sex_list})
display(df)

df.fillna('NA', inplace=True)
df = pd.get_dummies(df['SEX'],prefix='IS_SEX')
display(df)
# 原始数据
 SEX
0    MALE
1    FEMALE
2    NaN
3    FEMALE
4    FEMALE
5    NaN
6    MALE
# 填充后
 IS_SEX_FEMALE     IS_SEX_MALE    IS_SEX_NA
0    0                 1                0
1    1                 0                0
2    0                 0                1
3    1                 0                0
4    1                 0                0
5    0                 0                1
6    0                 1 

当特征值缺失超过 80% 以上,倡议删除【或退出「是」「否」标记位信息】,容易影响模型成果

df_titanic.drop(["deck"],axis=1)

2.3 异样值解决

数据品质也会很大水平影响机器学习利用成果,数据的谬误值或异样值可能会造成测量误差或异样零碎条件的后果,给模型学习带来很大的问题。理论咱们很多时候会有异样值检测与解决环节,上面给大家做一个梳理。

(1) 异样检测办法

① 基于统计分析

通常用户用某个统计散布对数据点进行建模,再以假设的模型,依据点的散布来确定是否异样。

如通过剖析统计数据的散度状况,即数据变异指标,对数据的散布状况有所理解,进而通过数据变异指标来发现数据中的异样点数据。

罕用的数据变异指标有 极差 四分位数间距 均差 标准差 变异系数 等等,如变异指标的值大示意变异大、分布广;值小示意离差小,较密集。

比方,最大最小值能够用来判断这个变量的取值是否超过了正当的范畴,如客户的年龄为 -20 岁或 200 岁,为异样值。

② 3σ 准则

如果数据近似正态分布,在 3σ 准则下,异样值为一组测定值中与平均值的偏差超过 3 倍标准差的值。

  • 如果数据遵从正态分布,间隔平均值 3σ 之外的值呈现的概率为 P(|x-μ|>3σ)<=0.003,属于极个别的小概率事件。
  • 如果数据不遵从正态分布,也能够用远离平均值的多少倍标准差来形容。

③ 箱线图剖析

大家还记得在数据分析局部有一个很无效的工具叫做 箱线图,箱线图提供了辨认异样值的一个规范:如果一个值小于 Q1-1.5IQR 或大于 Q3+1.5IQR 的值,则被称为异样值。

  • Q1 为下四分位数,示意全副察看值中有四分之一的数据取值比它小;
  • Q4 为上四分位数,示意全副察看值中有四分之一的数据取值比它大;
  • IQR 为四分位数间距,是上四分位数 Q1 与下四分位数 Q3 的差值,蕴含了全副察看值的一半。

箱型图判断异样值的办法以四分位数和四分位距为根底,四分位数具备鲁棒性:25% 的数据能够变得任意远并且不会烦扰四分位数,所以异样值不能对这个规范施加影响。因而箱型图辨认异样值比拟主观,在辨认异样值时有肯定的优越性。

sns.catplot(y="fare",x="survived", kind="box", data=df_titanic,palette="Set2")

④ 基于模型检测

咱们也能够基于模型对异样值检测,基本思路是先建设一个数据模型,那些同模型不能完满拟合的对象就视作异样。

  • 如果模型是簇的汇合,则异样是不显著属于任何簇的对象。
  • 在应用回归模型时,异样是绝对远离预测值的对象。

长处:有松软的统计学实践根底,当存在充沛的数据和所用的测验类型的常识时,这些测验可能十分无效。

毛病:对于多元数据,可用的抉择少一些,并且对于高维数据,这些检测可能性很差。

⑤ 基于间隔

咱们还有基于间隔的办法能够用于异样检测。这类办法基于上面这个假如:如果一个数据对象和大多数点间隔都很远,那这个对象就是异样。通过定义对象之间的邻近性度量,依据间隔判断异样对象是否远离其余对象,次要应用的间隔度量办法有相对间隔(曼哈顿间隔)、欧氏间隔和马氏间隔等办法。

  • 长处

    • 基于间隔的办法比基于统计类办法要简略得多;因为为一个数据汇合定义一个间隔的度量要比确定数据汇合的散布容易的多。
  • 毛病

    • 基于邻近度的办法须要 O(m2)工夫,大数据集不实用;
    • 该办法对参数的抉择也是敏感的;
    • 不能解决具备不同密度区域的数据集,因为它应用全局阈值,不能思考这种密度的变动。

⑥ 基于密度

一个很间接的异样检测思路是基于散布密度来做,具体为:考查以后点四周密度,部分异样点 / 离群点的部分密度显著低于大部分近邻点。这类办法实用于非平均的数据集。

  • 长处

    • 给出了对象是离群点的定量度量,并且即便数据具备不同的区域也可能很好的解决。
  • 毛病

    • 与基于间隔的办法一样,这些办法必然具备 O(m2)的工夫复杂度。
    • 对于低维数据应用特定的数据结构能够达到 O(mlogm);
    • 参数抉择艰难。
    • 尽管算法通过观察不同的 k 值,获得最大离群点得分来解决该问题,然而,依然须要抉择这些值的上下界。

⑦ 基于聚类

咱们能够基于聚类的办法进行异样检测,远离 cluster 的样本更可能是异样值。

不过该办法会受到聚类 cluster 个数 k 的影响,一种策略是对于不同的簇个数反复该剖析;另一种办法是找出大量小簇,其想法是:

  • 较小的簇偏向于更加凝聚;
  • 如果存在大量小簇时一个对象是异样点,则它多半是一个真正的异样点。
  • 不利的一面是一组异样点可能造成小簇而回避检测。

  • 长处

    • 基于线性和靠近线性复杂度 (k 均值) 的聚类技术来发现离群点可能是高度无效的;
    • 簇的定义通常是离群点的补,因而可能同时发现簇和离群点。
  • 毛病

    • 产生的离群点集和它们的得分可能十分依赖所用的簇的个数和数据中离群点的存在性;
    • 聚类算法产生的簇的品质对该算法产生的离群点的品质影响十分大。

⑧ 基于邻近度的异样点检测

同样的,咱们也有基于近邻度的思路来做异样检测,咱们认为异样点远离大部分的点。这种办法比统计学办法更个别、更容易应用,因为确定数据集的有意义的邻近性度量比确定它的统计散布更容易。一个对象的异样点得分由到它的 K - 最近邻的间隔给定,所以异样点得分对 K 的取值高度敏感:

  • 如果 K 太小(例如 1),则大量的邻近异样异样点可能导致较异样低的异样点得分。
  • 如果 K 太大,则点数少于 K 的簇中所有的对象可能都成了异样异样点。

为了使该计划对于 K 的选取更具备鲁棒性,能够应用 K 个最近邻的均匀间隔。

  • 长处

    • 简略
  • 毛病

    • 基于邻近度的办法须要 O(m2)工夫,大数据集不实用;
    • 该办法对参数的抉择也是敏感的;
    • 不能解决具备不同密度区域的数据集,因为它应用全局阈值,不能思考这种密度的变动。

在数据处理阶段将离群点作为影响数据品质的异样点思考,而不是作为通常所说的异样检测指标点,个别采纳较为简单直观的办法,联合箱线图和 MAD 的统计办法判断变量的离群点。如下为绘制散点图依据散布直接判断。

sns.scatterplot(x="fare", y="age", hue="survived",data=df_titanic,palette="Set1")

(2) 异样解决办法

对异样值解决,须要具体情况具体分析,异样值解决办法罕用的有以下几种:

  • 删除含有异样值的记录;

    • 某些筛选进去的异样样本是否真的是不须要的异样特色样本,最好联合业务再确认一编,避免失常样本被过滤。
  • 将异样值视为缺失值,交给缺失值解决办法来解决;
  • 应用均值 / 中位数 / 众数来修改;
  • 不解决。

3. 特色构建

前序的数据预处理过程能保障咱们拿到洁净参差精确的数据,但这些数据未必对于建模是最无效的,下一步咱们通常会进行特色构建,联合业务场景产生衍生变量来晋升数据表达能力和模型建模成果。

3.1 统计特色构建

统计特色是一类十分无效的特色,尤其在时序问题场景中,以下为统计特色构建的一些思考维度和办法:

  • ① 基于业务规定、先验常识等构建新特色。
  • ② 四分位数、中位数、平均值、标准差、偏差、偏度、偏锋、离散系统。
  • ③ 结构长、短期统计量(如周、月)。
  • ④ 工夫衰减(越凑近观测权重值高)。

回到 Titanic 数据集,咱们来看看联合业务了解,咱们能够做哪些新特色:

年龄解决

咱们对年龄 age 字段进行进一步解决,思考到不同的年龄段对应的人群可能获救概率不同,咱们依据年龄值分成不同区间段,对应到 child、young、midlife、old 等

def age_bin(x):
 if x <= 18:
 return 'child'
 elif x <= 30:
 return 'young'
 elif x <= 55:
 return 'midlife'
 else:
 return 'old'
df_titanic['age_bin'] = df_titanic['age'].map(age_bin)
df_titanic['age_bin'].unique()
执行后果:array(['young', 'midlife', 'child', 'old'], dtype=object)

抽取「称说」特色

咱们在 name 字段里,能够看到各种不同的称说,如「Mr」「Master」「Dr」等,这些称说体现了乘客的身份等信息,咱们能够对其做抽取构建新的特色。

# 提取称说
df_titanic['title'] = df_titanic['name'].map(lambda x: x.split(',')[1].split('.')[0].strip())

df_titanic['title'].value_counts()

执行后果如下:

Mr              757
Miss            260
Mrs             197
Master           61
Rev               8
Dr                8
Col               4
Ms                2
Major             2
Mlle              2
Dona              1
Sir               1
Capt              1
Don               1
Lady              1
Mme               1
the Countess      1
Jonkheer          1

咱们做一个简略的「称说」统计

# 对称说细分,是官员,还是皇室,还是女士、学生、小姐
df_titanic['title'].unique()

执行后果:

array(['Mr', 'Mrs', 'Miss', 'Master', 'Don', 'Rev', 'Dr', 'Mme', 'Ms',
 'Major', 'Lady', 'Sir', 'Mlle', 'Col', 'Capt', 'the Countess',
 'Jonkheer', 'Dona'], dtype=object)

上面咱们对这些「称说」「称呼」做一个规范化对立。

title_dictionary = {
 "Mr": "Mr",
 "Mrs": "Mrs",
 "Miss": "Miss",
 "Master": "Master",
 "Don": "Royalty",
 "Rev": "Officer",
 "Dr": "Officer",
 "Mme": "Mrs",
 "Ms": "Mrs",
 "Major": "Officer",
 "Lady": "Royalty",
 "Sir": "Royalty",
 "Mlle": "Miss",
 "Col": "Officer",
 "Capt": "Officer",
 "the Countess": "Royalty",
 "Jonkheer": "Royalty",
 "Dona": 'Mrs'
}
df_titanic['title'] = df_titanic['title'].map(title_dictionary)
df_titanic['title'].value_counts()

执行后果如下:

Mr         757
Miss       262
Mrs        201
Master      61
Officer     23
Royalty      5

抽取家庭规模

在 Titanic 上,有的成员之间有亲属关系,思考到家族大小对于最终是否获救也有影响,咱们能够构建一个 family_size 的特色,用于表征家庭规模。

df_titanic['family_size'] = df_titanic['sibsp'] + df_titanic['parch'] + 1
df_titanic['family_size'].head()

执行后果如下:

0    2
1    2
2    1
3    2
4    1

3.2 周期值

在电商等场景下,数据有肯定的周期法则,咱们能够提取一些周期值作为无效信息。

时序周期的一些思考维度如下:

  • ①前 n 个周期 / 天 / 月 / 年的周期值,如过来 5 天性位数、平均值等
  • ②同比 / 环比

3.3 数据分桶

数据分桶,是对间断值属性解决的一种罕用办法,它指的是咱们把间断数值切段,并把间断值归属到对应的段中。数据分桶也叫做数据分箱或离散化。

(1) 等频、等距分桶

(a) 自定义分箱

指依据业务教训或者常识等自行设定划分的区间,而后将原始数据归类到各个区间中。

(b) 等距分箱

依照雷同宽度将数据分成几等份。

从最小值到最大值之间,均分为 N 等份。如果 A、B 为最小最大值,则每个区间的长度为 W =(B−A)/N,区间边界值为 A +W、A+2W、…、A+(N−1)W。

等距分箱只思考边界,每个等份外面的实例数量可能不等。等距分桶的毛病是受到异样值的影响比拟大。

(c) 等频分箱

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

在等频分箱中,区间的边界值要通过计算取得,最终每个区间蕴含大抵相等的实例数量。比如说 N =5,每个区间应该蕴含大概 20% 的实例。

  • 数值变量分箱

咱们先对船票价格做一个等频切分(大家如果对船票价格进行散布绘图,会发现是很长尾的散布,并不适宜等距切分),看看离开的区间段。

# qcut 等频率分箱
df_titanic['fare_bin'], bins = pd.qcut(df_titanic['fare'], 5, retbins=True)
df_titanic['fare_bin'].value_counts()

后果如下:

(7.854, 10.5]        184
(21.679, 39.688]     180
(-0.001, 7.854]      179
(39.688, 512.329]    176
(10.5, 21.679]       172
bins #array([0.    ,   7.8542,  10.5   ,  21.6792,  39.6875, 512.3292])

上面依据区间段对其进行等频切分

# 对船票 fare 进行分段分桶
def fare_cut(fare):
    if fare <=  7.8958:
        return 0
    if fare <= 10.5:
        return 1
    if fare <= 21.6792:
        return 2
    if fare <=  39.6875:
        return 3
    return 4

df_titanic['fare_bin'] = df_titanic['fare'].map(fare_cut)

相比船票价格,年龄 age 字段的散布更加集中,且区间大小比拟明确,咱们采纳等距切分,代码如下:

# cut 等间隔分箱
bins = [0, 12, 18, 65, 100]
pd.cut(df_titanic['age'], bins).value_counts

(2) Best-KS 分桶

  • 1. 将特征值值进行从小到大的排序。
  • 2. 计算出 KS 最大的那个值,即为切点,记为 D。而后把数据切分成两局部。
  • 3. 反复步骤 2,进行递归,D 左右的数据进一步切割。直到 KS 的箱体数达到咱们的预设阈值即可。
  • 4. 连续型变量:分箱后的 KS 值 <= 分箱前的 KS 值
  • 5. 分箱过程中,决定分箱后的 KS 值是某一个切点,而不是多个切点的独特作用。这个切点的地位是原始 KS 值最大的地位。

(3) 卡方分桶

自底向上的 (即基于合并的) 数据离散化办法,依赖于卡方测验:具备最小卡方值的相邻区间合并在一起,直到满足确定的进行准则。

根本思维

如果两个相邻的区间具备十分相似的类散布,则这两个区间能够合并;否则,它们该当放弃离开。而低卡方值表明它们具备类似的类散布。

实现步骤

  • ①事后定义一个卡方的阈值
  • ②初始化;依据要离散的属性对实例进行排序,每个实例属于一个区间
  • ③合并区间

    • 计算每一对相邻区间的卡方值
    • 将卡方值最小的一对区间合并

代码实现:https://github.com/Lantianzz/Scorecard-Bundle

(4) 最小熵法分箱

还有最小熵分箱法,须要使总熵值达到最小,也就是使分箱可能最大限度地区分因变量的各类别。

熵是信息论中数据无序水平的度量规范,提出信息熵的根本目标是找出某种符号零碎的信息量和冗余度之间的关系,以便能用最小的老本和耗费来实现最高效率的数据存储、治理和传递。

数据集的熵越低,阐明数据之间的差别越小,最小熵划分就是为了使每箱中的数据具备最好的相似性。给定箱的个数,如果思考所有可能的分箱状况,最小熵办法失去的箱应该是具备最小熵的分箱。

3.4 特色组合

咱们在有些场景下会思考特色组合构建强特色,如下为罕用的特色组合构建形式:

1) 离散 + 离散 :构建笛卡尔积(即两两组合「且」关系)。
2) 离散 + 间断 :间断特色分桶后进行笛卡尔积或基于类别特色 group by 构建统计特色。
3) 间断 + 间断:加减乘除,多项式特色,二阶差分等。

  • 多项式特色

针对间断值特色,咱们对几个特色构建多项式特色,以达到特色组合与高阶加强的作用。

在 Titanic 的例子中,如下为数值型特色:

df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size']]
df_titanic_numerical.head()

咱们能够参考下述代码构建多项式特色

# 扩大数值特色
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, include_bias=False, interaction_only=False)
df_titanic_numerical_poly = poly.fit_transform(df_titanic_numerical)
pd.DataFrame(df_titanic_numerical_poly, columns=poly.get_feature_names()).head()

在构建实现特色后,咱们查看下衍生新特色变量的相关性状况,上面的热力求 heatmap 里色彩越深相关性越大:

sns.heatmap(pd.DataFrame(df_titanic_numerical_poly, columns=poly.get_feature_names()).corr())

4. 特色变换

咱们对于构建完的特色,会做一些「特色变换」的操作,以适应不同的模型,更好地实现建模。

4.1 标准化(Standardization)

标准化操作也称作 Z -score 变换,它使数值特色列的算数均匀为 0,方差 (以及标准差) 为 1,如下图所示。

留神:如果数值特色列中存在数值极大或极小的 outlier(通过 EDA 发现),应该应用更持重 (robust) 的统计数据:用中位数而不是算术平均数,用分位数 (quantile) 而不是方差。这种标准化办法有一个重要的参数:(分位数上限,分位数下限),最好通过 EDA 的数据可视化确定。免疫 outlier。

标准化操作的参考代码如下:

from sklearn.preprocessing import StandardScale
#标准化模型训练
Stan_scaler = StandardScaler()
Stan_scaler.fit(x)
x_zscore = Stan_scaler.transform(x)
x_test_zscore = Stan_scaler.transform(x_test)
joblib.dump(Stan_scaler,'zscore.m')  #写入文件

4.2 归一化(Normalization)

归一化操作会基于向量模长调整数据幅度大小,但并不会扭转原始数据的程序。如下图所示:

4.3 幅度缩放(scaling)

幅度缩放是为了让不同特色的取值在大体一致的数量级和数据区间内,比拟罕用的办法是最大最小值缩放,如下图所示:

上面为幅度缩放操作的参考代码:

from sklearn import preprocessing
min_max_scaler = preprocessing.MinMaxScaler()
min_max_scaler.fit_transform(x)
x_minmax = min_max_scaler.transform(x)
x_test_minmax = min_max_scaler.transform(x_test)
joblib.dump(min_max_scaler,'min_max_scaler.m')  #写入文件

4.4 归一化 VS 标准化

归一化和标准化是两个十分常见的特色变换操作,上面咱们来比照一下标准化和归一化:

  • 目标不同,归一化是为了打消纲量压缩到 [0,1] 区间;标准化只是调整特色整体的散布。
  • 归一化与最大,最小值无关;标准化与均值,标准差无关。
  • 归一化输入在 [0,1] 之间;标准化无限度。

它们别离的实用场景能够演绎总结如下:

  • 在分类、聚类算法中 (参考 ShowMeAI 教程 图解机器学习算法:从入门到精通系列教程 ),须要应用间隔来度量相似性的时候(如 SVM、KNN) 或者应用 PCA 技术进行降维的时候,标准化 (Z-score standardization) 体现更好。
  • 在不波及间隔度量、协方差计算、数据不合乎正太散布的时候,能够应用第一种办法或其余归一化办法。例如图像处理时,将 RGB 图像转换为灰度图像后将其值限定在 [0,255] 的范畴。
  • 基于树的模型 (如随机森林、GBDT、XGBoost、LightGBM 等,具体模型参考 ShowMeAI 教程 图解机器学习算法:从入门到精通系列教程 ) 不须要进行特色的归一化。如果是基于参数的模型或者基于间隔的模型(逻辑回归、K-Means 聚类、神经网络等),因为须要对参数或者间隔进行计算,都须要进行归一化。

4.5 非线性变换

咱们在有些场景下,还会对数值字段进行散布调整或者校对,利用统计或数学变换来加重数据分布歪斜的影响。使本来密集的区间的值尽可能的扩散,本来扩散的区间的值尽量的聚合。

大部分变换函数都属于幂变换函数簇,次要作用是稳固方差,放弃散布靠近于正态分布并使得数据与散布的平均值无关。

咱们来看看一些典型的非线性统计变换。

(1) log 变换

log 变换通常用来创立枯燥的数据变换。次要作用为稳固方差,始终保持散布靠近于正态分布并使得数据与散布的平均值无关。

  • log 变换偏向于拉伸那些落在较低的幅度范畴内自变量值的范畴,偏向于压缩或缩小更高幅度范畴内的自变量值的范畴,从而使得歪斜散布尽可能的靠近正态分布。
  • 针对一些数值间断特色的方差不稳固,特征值重尾散布咱们须要采纳 log 化来调整整个数据分布的方差,属于方差稳定型数据转换。

log 变换属于幂变换函数簇,数学表达式为

$$
y=log_{b}(x)
$$

上面咱们对 Titanic 数据集中的船票价格字段进行 log1p 变换,示例代码如下:

sns.distplot(df_titanic.fare,kde=False)

df_titanic['fare_log'] = np.log((1+df_titanic['fare']))
sns.distplot(df_titanic.fare_log,kde=False)

(2) box-cox 变换

box-cox 变换是 box 和 cox 在 1964 年提出的一种狭义幂变换办法,是统计建模中罕用的一种数据变换,用于间断的响应变量不满足正态分布的状况。box-cox 变换之后,能够肯定水平上减小不可观测的误差和预测变量的相关性。

box-cox 变换的次要特点是引入一个参数,通过数据自身预计该参数进而确定应采取的数据变换模式,box-cox 变换能够显著地改善数据的正态性、对称性和方差相等性,对许多理论数据都是卓有成效的。

box-cox 变换函数数学表达式如下:

$$
y(\lambda)=\left\{\begin{array}{ll}
\frac{y^{\lambda}-1}{\lambda}, & \lambda \neq 0 \\
\ln y, & \lambda=0
\end{array}\right.
$$

生成的变换后的输入 y,是输出 x 和变换参数的函数;当 λ = 0 时,该变换就是自然对数 log 变换,后面咱们曾经提到过了。λ 的最佳取值通常由最大似然或最大对数似然确定。

上面咱们对 Titanic 数据集中的船票价格字段进行 box-cox 变换,示例代码如下:

# 从数据分布中移除非零值
fare_positive_value = df_titanic[(~df_titanic['fare'].isnull()) & (df_titanic['fare']>0)]['fare']
import scipy.stats as spstats
# 计算最佳 λ 值
l, opt_lambda = spstats.boxcox(fare_positive_value)
print('Optimal lambda value:', opt_lambda) # -0.5239075895755266
# 进行 Box-Cox 变换
fare_boxcox_lambda_opt = spstats.boxcox(df_titanic[df_titanic['fare']>0]['fare'],lmbda=opt_lambda)
sns.distplot(fare_boxcox_lambda_opt,kde=Fal

4.6 离散变量解决

对于类别型的字段特色(比方色彩、类型、好坏水平),有很多模型并不能间接解决,咱们对其进行编码后能更好地出现信息和撑持模型学习。有以下常见的类别型变量编码方式:

(1) 标签编码(label encoding)

标签编码 (label encoding) 是最常见的类别型数据编码形式之一,编码值介于 0 和 n_classes-1 之间的标签。

例如:比方有[dog,cat,dog,mouse,rabbit],咱们把其转换为[0,1,0,2,3]。

  • 长处:绝对于 OneHot 编码,LabelEncoder 编码占用内存空间小,并且反对文本特色编码。
  • 毛病 :它的编码方式给不同类别带来了额定的大小程序关系,在有些计算型模型(比方逻辑回归) 里有影响,它能够应用在树模型中。

标签编码的参考代码如下:

from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit(["超一线", "一线", "二线", "三线"])
print('特色:{}'.format(list(le.classes_)))
# 输入 特色:['一线', '三线', '二线', '超一线']
print('转换标签值:{}'.format(le.transform(["超一线", "一线", "二线"])))
# 输入 转换标签值:array([3 0 2]...)
print('特色标签值反转:{}'.format(list(le.inverse_transform([2, 2, 1]))))
# 输入 特色标签值反转:['二线', '二线', ' 三线

(2) 独热向量编码(one hot encoding)

独热编码通常用于解决类别间不具备大小关系的特色。

例如:特色:血型,一共有四种类别(A,B,AB,O),采纳独热编码后,会把血型变成有一个 4 维的稠密向量

  • A 示意为[1,0,0,0]
  • B 示意为[0,1,0,0]
  • AB 示意为[0,0,1,0]
  • O 示意为[0,0,0,1]

最终生成的稠密向量的维度,和类别数雷同。

  • 长处:独热编码解决了分类器不好解决属性数据的问题,在肯定水平上也起到了裁减特色的作用。它的值只有 0 和 1,不同的类型存储在垂直的空间。
  • 毛病:只能对数值型变量二值化,无奈间接对字符串型的类别变量编码。当类别的数量很多时,特色空间会变得十分大。在这种状况下,个别能够用 PCA 来缩小维度。而且 one hot encoding+PCA 这种组合在理论中也十分有用。

如果借助于 pandas 工具库 (查看 ShowMeAI 的 数据分析系列教程 数据迷信工具速查 | Pandas 使用指南 进行具体理解),独热向量编码的 Python 代码参考示例如下:

sex_list = ['MALE', 'FEMALE', np.NaN, 'FEMALE', 'FEMALE', np.NaN, 'MALE']
df = pd.DataFrame({'SEX': sex_list})
display(df)
df.fillna('NA', inplace=True)
df = pd.get_dummies(df['SEX'],prefix='IS_SEX')
display(df)

最终变换前后的后果如下:

# 原始数据
 SEX
0   MALE
1   FEMALE
2   NaN
3   FEMALE
4   FEMALE
5   NaN
6   MALE

# 独热向量编码后
 IS_SEX_FEMALE     IS_SEX_MALE    IS_SEX_NA
0    0                 1                0
1    1                 0                0
2    0                 0                1
3    1                 0                0
4    1                 0                0
5    0                 0                1 

上面咱们对 ’sex’, ‘class’, ‘pclass’, ’embarked’, ‘who’, ‘family_size’, ‘age_bin’ 这些字段都进行独热向量编码。

pd.get_dummies(df_titanic, columns=['sex', 'class', 'pclass', 'embarked', 'who', 'family_size', 'age_bin'],drop_first=True)

当然,咱们也能够借助 SKLearn(查看 ShowMeAI 教程 SKLearn 最全利用指南AI 建模工具速查 | Scikit-learn 使用指南 具体学习),进行独热向量编码实现:

import numpy as np
from sklearn.preprocessing import OneHotEncoder
# 非负整数示意的标签列表
labels = [0,1,0,2]
# 行向量转列向量
labels = np.array(labels).reshape(len(labels), -1)
# 独热向量编码
enc = OneHotEncoder()
enc.fit(labels)
targets = enc.transform(labels).toarray()
# 如果不加 toarray() 的话,输入的是稠密的存储格局,即索引加值的模式,也能够通过参数指定 sparse = False 来达到同样的成果

输入后果如下:

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

(3) 标签二值化(LabelBinarizer)

性能与 OneHotEncoder 一样,然而 OneHotEncoder 只能对数值型变量二值化,无奈间接对字符串型的类别变量编码,而 LabelBinarizer 能够间接对字符型变量二值化。

示例代码如下:

from sklearn.preprocessing import LabelBinarizer
lb=LabelBinarizer()
labelList=['yes', 'no', 'no', 'yes','no2']
# 将标签矩阵二值化
dummY=lb.fit_transform(labelList)
print("dummY:",dummY)
# 逆过程
yesORno=lb.inverse_transform(dummY)
print("yesOrno:",yesORno)

输入如下:

dummY: [[0 0 1]
 [1 0 0]
 [1 0 0]
 [0 0 1]
 [0 1 0]]
yesOrno: ['yes' 'no' 'no' 'yes' 'no2']

4.7 降维

在理论的机器学习我的项目中,咱们可能还会做 降维 解决,次要因为数据存在以下几个问题:

  • 数据的多重共线性:特色属性之间存在着互相关联关系。多重共线性会导致解的空间不稳固,从而导致模型的泛化能力弱。
  • 高纬空间样本具备稠密性,导致模型比拟难找到数据特色。
  • 过多的变量会障碍模型查找法则。
  • 仅仅思考单个变量对于指标属性的影响可能疏忽变量之间的潜在关系。

通过特色降维心愿达到的目标:

  • 缩小特色属性的个数
  • 确保特色属性之间是互相独立的

罕用的降维办法有:

  • PCA
  • SVD
  • LDA
  • T-sne 等非线性降维

这里降维的解说,咱们给大家基于 iris 数据集解说:

from sklearn import datasets

iris_data = datasets.load_iris()

X = iris_data.data
y = iris_data.target

def draw_result(X, y):
    plt.figure()
    # 提取 Iris-setosa
    setosa = X[y == 0]
    # 绘制点:参数 1 x 向量,y 向量
    plt.scatter(setosa[:, 0], setosa[:, 1], color="red", label="Iris-setosa")

    versicolor = X[y == 1]
    plt.scatter(versicolor[:, 0], versicolor[:, 1], color="orange", label="Iris-versicolor")

    virginica = X[y == 2]
    plt.scatter(virginica[:, 0], virginica[:, 1], color="blue", label="Iris-virginica")

    plt.legend()
    plt.show()

draw_result(X, y)

(1) PCA(Principal Component Analysis)

对于 PCA 主成分剖析降维算法,大家能够查阅 ShowMeAI 文章 图解机器学习 | 降维算法详解 进行具体学习。

PCA 降维的参考代码实现如下:

import numpy as np
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
newX = pca.fit_transform(X)
draw_result(newX, y)

(2) SVD(Singular Value Decomposition)

SVD 办法的次要步骤如下:

$$
A^{T} A=\left(U \Sigma V^{T}\right)^{T} U \Sigma V^{T}=V \Sigma^{T} U^{T} U \Sigma V^{T}=V \Sigma^{T} \Sigma V^{T}=V \Sigma^{2} V^{T}
$$

所以 \(V \) 是 \(A^{T} A \) 特征值合成的特征向量按列组成的正交矩阵,\(\Sigma^{2} \)是 \(A^{T} A \) 特征值组成的对角矩阵,也能够看出 \(A_{m \times n} \) 的奇怪值 \(\sigma_{i} \) 是 \(A^{T} A \) 特征值 \(\lambda_{i} \)的平方根。

$$
\sigma_{i}=\sqrt{\lambda_{i}}
$$

如果 \(A^{T} A \) 的特征向量为 \(v_{i} \),\(U \) 中对应的 \(u_{i} \) 则能够由下式求出:

$$
u_{i}=\frac{A v_{i}}{\sigma_{i}}
$$

也即奇怪值合成的关键在于对 \(A^{T} A \) 进行特征值合成。

对应的代码参考实现如下:

from sklearn.decomposition import TruncatedSVD
iris_2d = TruncatedSVD(2).fit_transform(X)
draw_result(iris_2d, y)

PCA vs SVD

PCA 求解关键在于求解协方差矩阵 \(C=\frac{1}{m} X X^{T} \) 的特征值合成。

SVD 关键在于 \(A^{T} A \) 的特征值合成。

很显著二者所解决的问题十分类似,都是对一个实对称矩阵进行特征值合成,如果取:

$$
A=\frac{X^{T}}{\sqrt{m}}
$$

则有:

$$
A^{T} A=\left(\frac{X^{T}}{\sqrt{m}}\right)^{T} \frac{X^{T}}{\sqrt{m}}=\frac{1}{m} X X^{T}
$$

此时 SVD 与 PCA 等价,所以 PCA 问题能够转化为 SVD 问题求解。

(3) LDA(Linear Discriminant Analysis)

是有监督的降维,通过最小化类内离散度与最大化类间离散度来取得最优特色子集。

上图解读:LD1 通过线性断定,能够很好的将呈正态分布的两个类离开。LD2 的线性断定放弃了数据集的较大方差,但 LD2 无奈提供对于类别的信息,因而 LD2 不是一个好的线性断定。

对应的降维参考实现代码如下:

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
lda = LDA(n_components=2)
iris_2d = lda.fit_transform(X, y)
draw_result(iris_2d, y)

LDA vs PCA

PCA 试图寻找到方差最大的正交的主成分重量轴 LDA 发现能够最优化分类的特色子空间 LDA 和 PCA 都是可用于升高数据集维度的线性转换技巧 PCA 是无监督算法 LDA 是监督算法 LDA 是一种更优越的用于分类的特征提取技术

(4) T-SNE

T-SNE(t-distributed stochastic neighbor embedding)是一种非线性降维办法,参考的代码实现如下:

from sklearn.manifold import TSNE
tsne = TSNE(n_components=2)
iris_2d = tsne.fit_transform(X)
draw_result(iris_2d, y)

5. 特征选择

特征选择是在建模过程中常常会用到的一个解决,也有重要意义:

  • 特色冗余,局部特色相关度太高,耗费计算资源
  • 存在噪声,对模型后果有负面影响
  • 局部特色容易引起过拟合

总体来说,进行特征选择有 2 个次要思考方向:

  • 特色发散水平:如果一个特色不发散,例如方差靠近于 0,也就是说样本在这个特色上基本上没有差别,这个特色对于样本的辨别并没有什么用。
  • 特色与指标的相关性:特色与指标相关性高,越该当被保留,这点大家也比拟容易了解。

对特征选择的办法进行归类,又大体能够演绎为下述 3 种:

  • Filter:过滤法,依照发散性或者相关性对各个特色进行评分,设定阈值或者待抉择阈值的个数来抉择特色。
  • Wrapper:包装法,依据指标函数(通常是预测成果评分),每次抉择若干特色或者排除若干特色。
  • Embedded:嵌入法,先应用某些机器学习的算法和模型进行训练,失去各个特色的权值系数,依据系数从大到小抉择特色。相似于 Filter 办法,然而是通过训练来确定特色的优劣。咱们应用 SKLearn 中的 feature_selection 库来进行特征选择。

5.1 过滤式 Filter

(1) 方差过滤

这是通过特色自身的方差来筛选特色的类。

比方一个特色自身的方差很小,就示意样本在这个特色上根本没有差别,可能特色中的大多数值都一样,甚至整个特色的取值都雷同,那这个特色对于样本辨别没有什么作用。

咱们会剔除掉方差十分小的字段特色,参考代码实现如下:

from sklearn.feature_selection import VarianceThreshold
variancethreshold = VarianceThreshold() #实例化,默认方差为 0. 方差 <=0 的过滤掉
df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size']]
X_var = variancethreshold.fit_transform(df_titanic_numerical)    #获取删除不合格特色后的新特色矩阵
del_list = df_titanic_numerical.columns[variancethreshold.get_support()==0].to_list()  #取得删除

(2) 卡方过滤

卡方测验,专用于分类算法,捕获相关性,谋求 p 小于显著性程度的特色。卡方过滤是专门针对离散型标签 (即分类问题) 的相关性过滤。

p 值和取到这一个统计量的概率取值其实是正相干的:p 值越大,取到这个统计量的概率就越大,即越正当;p 值越小,取到这个统计量的概率就越小,即越不合理,此时应该回绝原假如,接管备择假如。

如下为卡方过滤的参考代码示例:

df_titanic_categorical = df_titanic[['sex', 'class', 'embarked', 'who',  'age_bin','adult_male','alone','fare_bin']]
df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size','pclass']]
df_titanic_categorical_one_hot = pd.get_dummies(df_titanic_categorical, columns=['sex', 'class', 'embarked', 'who',  'age_bin','adult_male','alone','fare_bin'], drop_first=True)
df_titanic_combined = pd.concat([df_titanic_numerical,df_titanic_categorical_one_hot],axis=1)

y = df_titanic['survived']
X = df_titanic_combined.iloc[:,1:]

from sklearn.feature_selection import chi2
from sklearn.feature_selection import SelectKBest
chi_value, p_value = chi2(X,y)
#依据 p 值,得出 k 值
k = chi_value.shape[0] - (p_value > 0.05).sum()  #要保留的特色的数量 14
#依据卡方值,抉择前几特色, 筛选后特色
X_chi = SelectKBest(chi2, k=14).fit_transform(X, y)

(3) F 测验

F 测验捕获线性相关性,要求数据遵从正态分布,谋求 P 值小于显著性程度特色。

其特征选择的参考代码如下:

from sklearn.feature_selection import f_classif
f_value, p_value = f_classif(X,y)
#依据 p 值,得出 k 值
k = f_value.shape[0] - (p_value > 0.05).sum()
#筛选后特色
X_classif = SelectKBest(f_classif, k=14).fit_transform(X, y)

(4) 互信息法

互信息法是用来捕获每个特色与标签之间的任意关系 (包含线性和非线性关系) 的过滤办法。

其特征选择的参考代码如下:

from sklearn.feature_selection import mutual_info_classif as MIC
#互信息法
mic_result = MIC(X,y)   #互信息量预计
k = mic_result.shape[0] - sum(mic_result <= 0)    #16
X_mic = SelectKBest(MIC, k=16).fit_transform(X, y)

5.2 包裹式 Wrapper

(1) 递归特色删除法

递归打消删除法应用一个基模型来进行多轮训练,每轮训练后,打消若干权值系数的特色,再基于新的特色集进行下一轮训练。应用 feature_selection 库的 RFE 类来抉择特色的代码如下:

from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特色打消法,返回特征选择后的数据
#参数 estimator 为基模型
#参数 n_features_to_select 为抉择的特色个数
X_ref = RFE(estimator=LogisticRegression(), n_features_to_select=10).fit_transform(X, y)

(2) 特色重要性评估

咱们基于一些模型 (如各类树模型) 能够失去特色重要度,进而进行筛选

from sklearn.ensemble import ExtraTreesClassifier
# 建模与获取特色重要度
model = ExtraTreesClassifier()
model.fit(X, y)
print(model.feature_importances_)

# 特色重要度排序
feature=list(zip(X.columns,model.feature_importances_))
feature=pd.DataFrame(feature,columns=['feature','importances'])
feature.sort_values(by='importances',ascending=False).head(20)

(3) 排列重要性评估

咱们还有一类办法能够评估特色重要度,进而进行筛选,叫作排列重要度。

原理:在训练机器学习模型之后计算置换重要性。这种办法在向模型提出假如,如果在保留指标和所有其余列的同时随机打乱一列验证集特色数据,对预测机器学习模型的准确性的影响水平。对于一个具备高度重要性的特色,random-reshuffle 会对机器学习模型预测的准确性造成更大的侵害。

长处:疾速计算;易于应用和了解;特色重要性度量的属性;谋求特色稳定性。

参考代码实现如下:

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import eli5
from eli5.sklearn import PermutationImportance
my_model = RandomForestClassifier(random_state=0).fit(train_X, train_y)
perm = PermutationImportance(my_model, random_state=1).fit(val_X, val_y)
eli5.show_weights(perm, feature_names = val_X.columns.tolist())

5.3 嵌入式 Embedded

(1) 基于惩办项的特征选择法

应用带惩办项的基模型,除了筛选出特色外,同时也进行了降维。

应用 feature_selection 库的 SelectFromModel 类联合带 L1 惩办项的逻辑回归模型,来抉择特色的代码如下:

from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
#带 L1 和 L2 惩办项的逻辑回归作为基模型的特征选择, 这个设置带 L1 惩办项的逻辑回归作为基模型的特征选择
lr = LogisticRegression(solver='liblinear',penalty="l1", C=0.1)
X_sfm = SelectFromModel(lr).fit_transform(X, y)
X_sfm.shape
(891, 7

应用 feature_selection 库的 SelectFromModel 类联合 SVM 模型,来抉择特色的代码如下:

from sklearn.feature_selection import SelectFromModel
from sklearn.svm import LinearSVC
lsvc = LinearSVC(C=0.01,penalty='l1',dual=False).fit(X, y)
model = SelectFromModel(lsvc,prefit=True)
X_sfm_svm = model.transform(X)
X_sfm_svm.shape
(891, 7

(2) 基于树模型

树模型中 GBDT 也可用来作为基模型进行特征选择,应用 feature_selection 库的 SelectFromModel 类联合 GBDT 模型,来抉择特色的代码如下:

from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
#GBDT 作为基模型的特征选择
gbdt = GradientBoostingClassifier()
X_sfm_gbdt = SelectFromModel(gbdt).fit_transform(X, y)

5.4 特征选择总结

对于特征选择,做一个经验总结,如下:

  • ① 类别型特色变量,那么能够从 SelectKBest 开始,用卡方或者基于树的选择器来抉择变量;
  • ② 定量特色变量,能够间接用线性模型和基于相关性的选择器来抉择变量;
  • ③ 二分类问题,能够思考应用 SelectFromModel 和 SVC;
  • ④ 特征选择前,要充沛理解数据,个别须要做探索性数据分析 EDA。

6. 特色工程实战倡议

最初,ShowMeAI 结合实际工业利用教训,总结一些特色工程要点,如下:

6.1 数据了解

构建特色的有效性,和业务及数据分布强相干,因而倡议在此步骤之前做 EDA 探索性数据分析来充沛了解数据(能够参考 ShowMeAI 文章 Python 机器学习综合我的项目 - 电商销量预估Python 机器学习综合我的项目 - 电商销量预估 < 进阶 > 理解 EDA 的根本过程和办法)。

6.2 数据预处理

咱们可能会做的一些数据预处理与特色解决如下:

  • 间断特色离散化

    • 实质是限度浮点数特色的精度,异样数据有很强的鲁棒性,模型也会更稳固。
    • 树模型不须要做
  • 数值截断

    • 把特征值的取值限度在肯定范畴内(对异样剔除有帮忙)
    • 能够用 pandas dataframe 的.clip(low,upper)办法

6.3 数据荡涤

联合业务场景和数据分布,进行正当的缺失值、异样值解决。

6.4 特色构建与变换

倡议不要上来就做 PCA 或 LDA 降维,最好先构建特色并对特色做筛选。

  • 线性组合(linear combination)

    • 实用于决策树以及基于决策树的 ensemble(如 gradient boosting,random forest),因为常见的 axis-aligned split function 不善于捕捉不同特色之间的相关性;
    • 不适用于 SVM、线性回归、神经网络等。
  • 类别特色与数值特色的组合

    • 用 N1 和 N2 示意数值特色,用 C1 和 C2 示意类别特色,利用 pandas 的 groupby 操作,能够发明出以下几种有意义的新特色:(其中,C2 还能够是离散化了的 N1)
median(N1)_by(C1)   中位数
mean(N1)_by(C1)   算术平均数
mode(N1)_by(C1)   众数
min(N1)_by(C1)   最小值
max(N1)_by(C1)   最大值
std(N1)_by(C1)   标准差
var(N1)_by(C1)   方差
freq(C2)_by(C1)   频数
  • 统计特色 + 线性组合

    • 统计特色能够和线性组合等根底特色工程办法联合(仅用于决策树),能够失去更多有意义的特色,如:
N1 - median(N1)_by(C1)
N1 - mean(N1)_by(C1)
  • 基于树模型发明新特色

    • 在决策树系列算法中 (例决策树、gbdt、随机森林,具体能够查看 ShowMeAI 教程 图解机器学习算法:从入门到精通系列教程 具体学习了解),每一个样本都会被映射到决策树的叶子上。
    • 咱们能够把样本通过每一棵决策树映射后的 index(自然数)或 one-hot-encoding-vector(哑编码失去的稠密矢量)作为一项新的特色,退出到模型中。

在 Scikit-Learn 和 XGBoost 里,能够基于 apply()以及 decision_path()等办法实现。

6.5 模型

咱们在不同类型的模型里,也会思考不同的特色工程办法

  • 树模型

    • 对特色数值幅度不敏感,能够不进行无穷纲化和统计变换处理;
    • 数模型特色依赖于样本间隔来进行学习,能够不进行类别特色编码(但字符型特色不能间接作为输出,所以须要至多要进行标签编码)。
    • LightGBM 和 XGBoost 都能将缺失值作为数据的一部分进行学习,所以不须要解决缺失值。其余状况须要填充缺失。
  • 依赖样本间隔的模型

    • 如线性回归、SVM、深度学习等属于这一类。
    • 对于数值型特色须要进行无穷纲化解决。
    • 对于一些长尾散布的数据特色,能够做统计变换,使得模型能更好优化。
    • 对于线性模型,特色分箱能够晋升模型表达能力。

参考资料

  • 图解机器学习算法 | 从入门到精通系列
  • 数据分析系列教程
  • 数据迷信工具速查 | Pandas 使用指南

ShowMeAI 系列教程举荐

  • 图解 Python 编程:从入门到精通系列教程
  • 图解数据分析:从入门到精通系列教程
  • 图解 AI 数学根底:从入门到精通系列教程
  • 图解大数据技术:从入门到精通系列教程
  • 图解机器学习算法:从入门到精通系列教程
  • 机器学习实战:手把手教你玩转机器学习系列

相干文章举荐

  • Python 机器学习算法利用实际
  • SKLearn 入门与简略利用案例
  • SKLearn 最全利用指南
  • XGBoost 建模利用详解
  • LightGBM 建模利用详解
  • Python 机器学习综合我的项目 - 电商销量预估
  • Python 机器学习综合我的项目 - 电商销量预估 < 进阶计划 >
  • 机器学习特色工程最全解读
  • 自动化特色工程工具 Featuretools 利用
  • AutoML 自动化机器学习建模
退出移动版