TowardsDataScience 博客中文翻译 2019(一百九十三)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

面向机器学习的特征工程基本技术

原文:https://towardsdatascience.com/feature-engineering-for-machine-learning-3a5e293a5114?source=collection_archive---------0-----------------------

用熊猫例子进行综合数据预处理所需的所有方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

介绍

什么是特性,为什么我们需要它的工程化?基本上,所有的机器学习算法都使用一些输入数据来创建输出。该输入数据包括特征,这些特征通常是结构化列的形式。算法需要具有某些特定特征的功能才能正常工作。这里,出现了对特征工程的需求。我认为特性工程的努力主要有两个目标:

  • 准备适当的输入数据集,与机器学习算法要求兼容。
  • 提高机器学习模型的性能。

你使用的特性比其他任何东西都更能影响结果。据我所知,没有任何算法可以单独补充正确的特征工程所提供的信息增益。

—卢卡·马萨隆

根据《福布斯》的一项调查,数据科学家花费 80% 的时间在数据准备上:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source: https://www.forbes.com/sites/gilpress/2016/03/23/data-preparation-most-time-consuming-least-enjoyable-data-science-task-survey-says/

这一指标显示了特征工程在数据科学中的重要性,令人印象深刻。因此,我决定写这篇文章,用简短的描述总结特征工程的主要技术。我还为每种技术添加了一些基本的 python 脚本。你需要导入 PandasNumpy 库来运行它们。

import pandas as pd
import numpy as np

上面的一些技术可能更适合一些算法或数据集,而其中一些可能在所有情况下都是有益的。本文不打算在这方面深入探讨。尽管如此,还是有可能为上面的每一种方法写一篇文章,我试着保持解释的简短和翔实。我认为获得特征工程专业知识的最佳方式是在各种数据集上实践不同的技术,并观察它们对模型性能的影响。

技术列表

1.归罪

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当您尝试为机器学习准备数据时,丢失值是您可能会遇到的最常见问题之一。丢失值的原因可能是人为错误、数据流中断、隐私问题等等。无论是什么原因,丢失的值都会影响机器学习模型的性能。

一些机器学习平台在模型训练阶段自动丢弃包括缺失值的行,并且由于训练规模的减小而降低了模型性能。另一方面,大多数算法不接受具有缺失值的数据集并给出错误。

对于缺失值,最简单的解决方案是删除行或整列。没有最佳的删除阈值,但是您可以使用 70% 作为示例值,并尝试删除丢失值高于此阈值的行和列。

threshold = 0.7**#Dropping columns with missing value rate higher than threshold**
data = data[data.columns[data.isnull().mean() < threshold]]

**#Dropping rows with missing value rate higher than threshold**
data = data.loc[data.isnull().mean(axis=1) < threshold]

数值插补

插补是比删除更可取的选择,因为它保留了数据的大小。然而,有一个重要的选择,你归咎于丢失的价值。我建议从考虑列中缺失值的可能默认值开始。例如,如果您有一个只有 1NA 的列,那么很可能 NA 行对应于 0 。再举一个例子,如果您有一个显示**“上个月的客户访问计数”**的列,那么只要您认为这是一个合理的解决方案,缺失的值可能会被替换为 0

缺少值的另一个原因是连接不同大小的表,在这种情况下,输入 0 可能也是合理的。

除了缺失值有默认值的情况,我认为最好的插补方式是使用列的中位数。因为列的平均值对异常值敏感,而中间值在这方面更可靠。

**#Filling all missing values with 0**
data = data.fillna(0)**#Filling missing values with medians of the columns**
data = data.fillna(data.median())

分类插补

用列中的最大出现值替换缺失值是处理分类列的一个好选择。但是,如果你认为该列中的值是均匀分布的,并且没有一个主导值,那么把一个像“其他”这样的类别归入可能更合理,因为在这种情况下,你的插补很可能收敛于一个随机选择。

**#Max fill function for categorical columns**
data['column_name'].fillna(data['column_name'].value_counts()
.idxmax(), inplace=True)

2.处理异常值

在提到如何处理异常值之前,我想说明检测异常值的最好方法是直观地展示数据。所有其他的统计方法都有可能出错,而可视化异常值提供了一个高精度决策的机会。无论如何,我计划在另一篇文章中深入讨论可视化,让我们继续讨论统计方法。

统计方法不如我提到的精确,但另一方面,他们有优势,他们很快。这里我将列出两种处理异常值的不同方法。这些将使用标准差百分位数来检测它们。

基于标准差的异常值检测

如果一个值与平均值的距离高于 *x 标准差,则可以将其视为异常值。那什么 x 应该是?

对于 x 没有简单的解决方案,但是通常,介于 2 和 4 之间的值似乎是可行的。

**#Dropping the outlier rows with standard deviation**
factor = 3
upper_lim = data['column'].mean () + data['column'].std () * factor
lower_lim = data['column'].mean () - data['column'].std () * factor

data = data[(data['column'] < upper_lim) & (data['column'] > lower_lim)]

另外,可以用 z-score 代替上面的公式。 Z 分数(或标准分数)使用标准差标准化数值和平均值之间的距离。

基于百分位数的异常值检测

另一种检测异常值的数学方法是使用百分位数。您可以将顶部或底部值的某个百分比假定为异常值。这里的关键点是再次设置百分比值,这取决于前面提到的数据分布。

此外,一个常见的错误是根据数据范围使用百分位数。换句话说,如果你的数据范围是从 0100 ,那么你的 top 5% 就不是在 96100 之间的值。Top 5% 在这里是指超出第 95 个数据百分点的数值。

**#Dropping the outlier rows with Percentiles**
upper_lim = data['column'].quantile(.95)
lower_lim = data['column'].quantile(.05)

data = data[(data['column'] < upper_lim) & (data['column'] > lower_lim)]

离群困境:下降还是上升

处理异常值的另一个选择是限制它们,而不是丢弃它们。因此,您可以保持您的数据大小,并且在一天结束时,它可能对最终的模型性能更好。

另一方面,封顶会影响数据的分布,因此最好不要夸大它。

**#Capping the outlier rows with Percentiles**
upper_lim = data['column'].quantile(.95)
lower_lim = data['column'].quantile(.05)data.loc[(df[column] > upper_lim),column] = upper_lim
data.loc[(df[column] < lower_lim),column] = lower_lim

3.扔掉

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Binning illustration of numerical data

宁滨可应用于分类数据和数值数据:

**#Numerical Binning Example****Value      Bin**       
0-30   ->  Low       
31-70  ->  Mid       
71-100 ->  High**#Categorical Binning Example****Value      Bin**       
Spain  ->  Europe      
Italy  ->  Europe       
Chile  ->  South America
Brazil ->  South America

宁滨的主要动机是使模型更加健壮并防止过度拟合,然而,这是以性能为代价的。每次你绑定一些东西,你就牺牲了一些信息,使你的数据更有规律。(请参见机器学习中的正则化)

性能过度拟合之间的权衡是宁滨过程的关键点。在我看来,对于数值列,除了一些明显的过度拟合情况,宁滨对于某种算法来说可能是多余的,因为它会影响模型性能。

然而,对于分类列,低频标签可能会对统计模型的稳健性产生负面影响。因此,为这些不太频繁的值分配一个通用类别有助于保持模型的稳健性。例如,如果您的数据大小是 100,000 行,那么将计数小于 100 的标签合并到一个新的类别中,比如**“其他”**,这可能是一个不错的选择。

**#Numerical Binning Example**data['bin'] = pd.cut(data['value'], bins=[0,30,70,100], labels=["Low", "Mid", "High"]) **value   bin**
0      2   Low
1     45   Mid
2      7   Low
3     85  High
4     28   Low**#Categorical Binning Example** **Country**
0      Spain
1      Chile
2  Australia
3      Italy
4     Brazilconditions = [
    data['Country'].str.contains('Spain'),
    data['Country'].str.contains('Italy'),
    data['Country'].str.contains('Chile'),
    data['Country'].str.contains('Brazil')]

choices = ['Europe', 'Europe', 'South America', 'South America']

data['Continent'] = np.select(conditions, choices, default='Other') **Country      Continent**
0      Spain         Europe
1      Chile  South America
2  Australia          Other
3      Italy         Europe
4     Brazil  South America

4.对数变换

对数变换是特征工程中最常用的数学变换之一。日志转换的好处是什么:

  • 它有助于处理倾斜的数据,在转换后,分布变得更接近正常。
  • 在大多数情况下,数据的数量级在数据范围内变化。例如:年龄 1520 的差别不等于年龄 6570 。从年龄上来说,是的,他们是相同的,但是对于其他所有方面来说, 5 年轻年龄的差异意味着更高的量级差异。这种类型的数据来自乘法过程,对数变换会像这样对幅度差异进行归一化。
  • 由于幅度差的归一化,它还降低了异常值的影响,并且模型变得更加稳健。

**重要提示:**您应用对数变换的数据必须只有正值,否则您会收到一个错误。此外,您可以在转换数据之前将 1 添加到数据中。因此,您可以确保转换的输出为正。

Log(x+1)

**#Log Transform Example**
data = pd.DataFrame({'value':[2,45, -23, 85, 28, 2, 35, -12]})data['log+1'] = (data['value']+1).transform(np.log)**#Negative Values Handling
#Note that the values are different** data['log'] = (data['value']-data['value'].min()+1) .transform(np.log) **value  log(x+1)  log(x-min(x)+1)**
0      2   1.09861          3.25810
1     45   3.82864          4.23411
2    -23       nan          0.00000
3     85   4.45435          4.69135
4     28   3.36730          3.95124
5      2   1.09861          3.25810
6     35   3.58352          4.07754
7    -12       nan          2.48491

5.一键编码

一键编码是机器学习中最常见的编码方式之一。该方法将一列中的值扩展到多个标志列,并将 01 分配给它们。这些二进制值表示分组列和编码列之间的关系。

该方法将算法难以理解的分类数据更改为数字格式,并使您能够在不丢失任何信息的情况下对分类数据进行分组。(详情请参见分类列分组的最后一部分)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

One hot encoding example on City column

为什么一热?:如果你的列中有 N 个不同的值,把它们映射到 N-1 个二进制列就足够了,因为缺失的值可以从其他列中扣除。如果我们手里的所有列都等于 0 ,那么缺失值一定等于 1 。这就是它被称为一键编码的原因。不过,我会用熊猫的 get_dummies 函数来举例说明。该函数将一列中的所有值映射到多列。

encoded_columns = pd.get_dummies(data['column'])
data = data.join(encoded_columns).drop('column', axis=1)

6.分组操作

在大多数机器学习算法中,每个实例都由训练数据集中的一行来表示,其中每一列都显示该实例的不同特征。这种数据称为**【整齐】**。

整齐的数据集易于操作、建模和可视化,并且具有特定的结构:每个变量是一列,每个观察值是一行,每种类型的观察单元是一个表。

—哈德利·韦翰

像事务这样的数据集很少符合上面整洁数据的定义,因为一个实例有多行。在这种情况下,我们按实例对数据进行分组,然后每个实例仅由一行表示。

分组操作的关键是确定特征的聚合函数。对于数字特征,平均值和总和函数通常是方便的选项,而对于分类特征,这就更复杂了。

分类列分组

我建议用三种不同的方法来聚合分类列:

  • 第一个选项是选择最高频率的标签。换句话说,这是对分类列的 max 操作,但是普通的 max 函数一般不会返回这个值,为此需要使用 lambda 函数。
data.groupby('id').agg(lambda x: x.value_counts().index[0])
  • 第二个选择是制作一个数据透视表。这种方法类似于上一步中的编码方法,但有一点不同。代替二进制表示法,它可以被定义为分组列和编码列之间的值的聚合函数。如果您的目标不仅仅是二进制标志列,而是将多个要素合并成信息更丰富的聚合要素,那么这将是一个不错的选择。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Pivot table example: Sum of Visit Days grouped by Users

**#Pivot table Pandas Example**data.pivot_table(index='column_to_group', columns='column_to_encode', values='aggregation_column', aggfunc=np.sum, fill_value = 0)
  • 最后一个分类分组选项是在应用一键编码后,通过函数应用分组。这个方法保留了所有的数据——在第一个选项中,您会丢失一些——此外,您同时将编码列从分类转换为数字。关于数值列分组的解释,可以查看下一节。

数字列分组

在大多数情况下,使用 summean 函数对数字列进行分组。根据特征的含义,两者都是优选的。例如,如果你想获得列,你可以使用二元列的平均值。在同一个示例中,sum 函数也可用于获得总计数。

#sum_cols: List of columns to sum
#mean_cols: List of columns to averagegrouped = data.groupby('column_to_group')

sums = grouped[sum_cols].sum().add_suffix('_sum')
avgs = grouped[mean_cols].mean().add_suffix('_avg')

new_df = pd.concat([sums, avgs], axis=1)

7.特征分割

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Jaxon Lott on Unsplash

分割特征是使它们在机器学习方面有用的好方法。大多数情况下,数据集包含的字符串列违反了整齐数据原则。通过将列的可利用部分提取到新特征中:

  • 我们让机器学习算法能够理解它们。
  • 使得可以对它们进行绑定和分组。
  • 通过发现潜在信息来提高模型性能。

分割功能是一个很好的选择,但是,没有一种分割功能的方式。这要看栏目的特点,怎么拆分。下面用两个例子来介绍一下。首先,一个普通名称列的简单拆分函数:

**data.name**
0  Luther N. Gonzalez
1    Charles M. Young
2        Terry Lawson
3       Kristen White
4      Thomas Logsdon#Extracting first names **data.name.str.split(" ").map(lambda x: x[0])** 0     Luther
1    Charles
2      Terry
3    Kristen
4     Thomas#Extracting last names **data.name.str.split(" ").map(lambda x: x[-1])** 0    Gonzalez
1       Young
2      Lawson
3       White
4     Logsdon

上面的例子通过只取第一个和最后一个元素来处理长度超过两个单词的名称,这使得该函数对于极端情况是健壮的,这在处理这样的字符串时应该被考虑。

split 函数的另一种情况是提取两个字符之间的字符串部分。以下示例显示了通过在一行中使用两个 split 函数来实现这种情况。

#String extraction example **data.title.head()**
0                      Toy Story (1995)
1                        Jumanji (1995)
2               Grumpier Old Men (1995)
3              Waiting to Exhale (1995)
4    Father of the Bride Part II (1995)**data.title.str.split("(", n=1, expand=True)[1].str.split(")", n=1, expand=True)[0]** 0    1995
1    1995
2    1995
3    1995
4    1995

8.缩放比例

在大多数情况下,数据集的数字特征不具有特定的范围,并且它们彼此不同。在现实生活中,期望年龄收入栏有相同的范围是无稽之谈。但是从机器学习的角度来看,这两列怎么比较呢?

缩放解决了这个问题。在缩放过程之后,连续特征在范围方面变得相同。对于许多算法来说,这个过程不是强制性的,但是应用起来可能还是不错的。然而,基于距离计算的算法,如 k-NNk-Means 需要将缩放的连续特征作为模型输入。

基本上,有两种常见的缩放方式:

正常化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

归一化(或最小-最大归一化)在 01 之间的固定范围内缩放所有值。这种变换不会改变特征的分布,由于标准偏差减小,异常值的影响会增加。因此,在标准化之前,建议处理异常值。

data = pd.DataFrame({'value':[2,45, -23, 85, 28, 2, 35, -12]})

data['normalized'] = (data['value'] - data['value'].min()) / (data['value'].max() - data['value'].min()) **value  normalized**
0      2        0.23
1     45        0.63
2    -23        0.00
3     85        1.00
4     28        0.47
5      2        0.23
6     35        0.54
7    -12        0.10

标准化

标准化(或 z 分数标准化)在考虑标准偏差的同时缩放数值。如果特征的标准偏差不同,它们的范围也会彼此不同。这降低了特征中异常值的影响。

在下面的标准化公式中,均值表示为标准 偏差表示为 σ

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*data = pd.DataFrame({'value':[2,45, -23, 85, 28, 2, 35, -12]})

data['standardized'] = (data['value'] - data['value'].mean()) / data['value'].std() **value  standardized**
0      2         -0.52
1     45          0.70
2    -23         -1.23
3     85          1.84
4     28          0.22
5      2         -0.52
6     35          0.42
7    -12         -0.92*

9.提取日期

虽然日期列通常提供关于模型目标的有价值的信息,但是它们作为输入被忽略或者被无意义地用于机器学习算法。可能是这个原因,日期可以以多种格式呈现,这使得算法很难理解,即使它们被简化为类似*" 01–01–2017 "*的格式。

如果不处理日期列,在值之间建立顺序关系对于机器学习算法来说是非常具有挑战性的。这里,我建议对日期进行三种类型的预处理:

  • 将日期的各个部分提取到不同的列中:年、月、日等。
  • 根据年、月、日等提取当前日期和列之间的时间段。
  • 从日期中提取一些特定的特征:工作日的名称、是否是周末、是否是假日等。

如果您将日期列转换为如上所述的提取列,它们的信息就会公开,机器学习算法可以轻松理解它们。

*from datetime import date

data = pd.DataFrame({'date':
['01-01-2017',
'04-12-2008',
'23-06-1988',
'25-08-1999',
'20-02-1993',
]})

**#Transform string to date**
data['date'] = pd.to_datetime(data.date, format="%d-%m-%Y")

**#Extracting Year**
data['year'] = data['date'].dt.year

**#Extracting Month**
data['month'] = data['date'].dt.month

**#Extracting passed years since the date**
data['passed_years'] = date.today().year - data['date'].dt.year

**#Extracting passed months since the date**
data['passed_months'] = (date.today().year - data['date'].dt.year) * 12 + date.today().month - data['date'].dt.month

**#Extracting the weekday name of the date**
data['day_name'] = data['date'].dt.day_name() **date  year  month  passed_years  passed_months   day_name**
0 2017-01-01  2017      1             2             26     Sunday
1 2008-12-04  2008     12            11            123   Thursday
2 1988-06-23  1988      6            31            369   Thursday
3 1999-08-25  1999      8            20            235  Wednesday
4 1993-02-20  1993      2            26            313   Saturday*

结论

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

https://xkcd.com/1838/

我试图解释在特征工程过程中有益的基本方法。在这篇文章之后,继续进行数据准备的其他主题,例如特征选择、训练/测试分割、采样可能是一个不错的选择。

你可以查看我的另一篇关于过采样的文章

最后,我想以一个提醒来结束这篇文章。这些技术不是神奇的工具。如果你的数据微小、肮脏且无用,特征工程可能仍然无能为力。别忘了***垃圾进来,垃圾出去!”*****

参考

Python 中的要素工程

原文:https://towardsdatascience.com/feature-engineering-in-python-part-i-the-most-powerful-way-of-dealing-with-data-8e2447e7c69e?source=collection_archive---------3-----------------------

"我们的目标是将数据转化为信息,并将信息转化为洞察力。——卡莉·菲奥莉娜

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by h heyerlein on Unsplash

我们生活在一个数据驱动的经济中。在这个世界里,拥有大量数据、理解这些数据并知道如何处理数据就是力量。理解你的数据并不是数据科学中最困难的事情之一,但是很费时间。当我们知道数据的来源时,数据的解释是有效的。

数据

我们相信上帝。所有其他人必须携带数据。— W .戴明
首先,我们需要了解我们的数据。有 3 种数据:

  1. 数值 这代表某种定量测量。例如:身高的人,股票价格,页面加载时间等。它可以进一步分解为两部分:
    离散数据: 这是基于整数的,通常是一些事件的计数。例如:“一个用户喜欢多少首歌?”或者“一枚硬币掷到“头”上多少次?”
    连续数据: 它有无限多种可能的值。比如:“一个用户购买用了多长时间?”
  2. 分类的 这代表没有明显内在数学意义的定性数据。例子:是或否,性别,种族,婚姻状况等。这些可以被赋予像是(0)和否(1)这样的数字,但是数字没有数学意义。
  3. 序数
    它兼具数值型和分类型数据的特征。但是分类中的数字有数学意义。例如:电影评分为 1-5 等。这些值具有数学意义。因此,我们往往会忽略这一步,直接跳入水中。

特征工程

机器学习将数学符号与数据进行拟合,以获得一些见解。模型将特征作为输入。特征通常是现实世界现象或数据的一个方面的数字表示。就像迷宫中有死胡同一样,数据路径充满了噪音和缺失的部分。作为一名数据科学家,我们的工作是找到一条通往洞察最终目标的清晰道路。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Victor Garcia on Unsplash

数学公式对数字量起作用,原始数据并不完全是数字。特征工程是从数据中提取特征并将其转换为适合机器学习算法的格式的方法。

它分为三大类

特征选择:所有特征不相等。这就是从大量的特性中选择一小部分特性。我们选择那些最能解释自变量与目标变量之间关系的属性。对于模型的准确性来说,某些特征比其他特征更重要。它与降维不同,因为降维方法是通过组合现有属性来实现的,而特征选择方法是包含或排除那些特征。
特征选择的方法有卡方检验、相关系数评分、LASSO、岭回归等。

**特性转换:**就是把我们原来的特性转换成原来特性的功能。缩放、离散化、宁滨和填充缺失数据值是最常见的数据转换形式。为了 减少 数据的右偏,我们使用 log。

**特征提取:**当一个算法要处理的数据太大时,一般认为是冗余的。使用大量变量进行分析需要大量的计算能力和内存,因此我们应该减少这些类型变量的维数。这是一个构建变量组合的术语。对于表格数据,我们使用主成分分析来减少特征。对于图像,我们可以使用直线或边缘检测。

我们从 MachineHack 的这个 数据集开始。
我们将首先导入特征工程所需的所有包。

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

我们将使用 pandas 加载数据,并将显示设置为最大,以便显示所有包含详细信息的列:

pd.set_option('display.max_columns', None)data_train = pd.read_excel('Data_Train.xlsx')data_test = pd.read_excel('Data_Test.xlsx')

在我们开始预处理数据之前,我们希望单独存储目标变量或标签。在从训练数据集中移除标签之后,我们将组合我们的训练和测试数据集。我们合并训练和测试数据集的原因是,机器学习模型不擅长**推断,**即 ML 模型不擅长从现有信息中推断出尚未明确陈述的东西。因此,如果测试集中的数据没有得到很好的表示,比如在训练集中,预测就不可靠。

price_train = data_train.Price # Concatenate training and test sets data = pd.concat([data_train.drop(['Price'], axis=1), data_test])

这是我们在data.columns之后得到的输出

Index(['Airline', 'Date_of_Journey', 'Source', 'Destination', 'Route','Dep_Time', 'Arrival_Time', 'Duration', 'Total_Stops',
'Additional_Info', 'Price'], dtype='object')

要检查前五行数据,请键入data.head()

为了更全面地了解情况,我们使用了data.info()方法。

<class 'pandas.core.frame.DataFrame'>
Int64Index: 13354 entries, 0 to 2670
Data columns (total 10 columns):
Airline            13354 non-null object
Date_of_Journey    13354 non-null object
Source             13354 non-null object
Destination        13354 non-null object
Route              13353 non-null object
Dep_Time           13354 non-null object
Arrival_Time       13354 non-null object
Duration           13354 non-null object
Total_Stops        13353 non-null object
Additional_Info    13354 non-null object
dtypes: int64(1), object(10)
memory usage: 918.1+ KB

要了解我们数据的分布,使用data.describe(include=all)

我们希望分析数据并删除所有重复的值。

data = data.drop_duplicates()

因此,我们希望检查数据中的任何空值。

Airline            0
Date_of_Journey    0
Source             0
Destination        0
Route              1
Dep_Time           0
Arrival_Time       0
Duration           0
Total_Stops        1
Additional_Info    0
Price              0
dtype: int64

因此,我们使用下面的代码来删除空值。

data = data.drop(data.loc[data['Route'].isnull()].index)

航空公司

让我们查一下航空公司一栏。我们注意到它包含了分类值。在使用了data['Airline'].unique()之后,我们注意到航空公司的价值观在某种程度上是重复的。

我们首先想要可视化该列:

sns.countplot(x='Airline', data=data)plt.xticks(rotation=90)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这种形象化帮助我们理解某些航空公司被分成了两个部分。例如,Jet Airways 有另一个部分叫做 Jet Airways Business。我们想把这两类结合起来。

data['Airline'] = np.where(data['Airline']=='Vistara Premium economy', 'Vistara', data['Airline'])data['Airline'] = np.where(data['Airline']=='Jet Airways Business', 'Jet Airways', data['Airline'])data['Airline'] = np.where(data['Airline']=='Multiple carriers Premium economy', 'Multiple carriers', data['Airline'])

这就是我们的列中的值现在的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

航班的目的地

目的地也是如此。我们发现德里和新德里被分成了两个不同的类别。因此,我们将它们合二为一。

data['Destination'].unique()data['Destination'] = np.where(data['Destination']=='Delhi','New Delhi', data['Destination'])

旅行日期

我们检查这个列,data['Date_of_Journey'],发现这个列的格式是:-

24/03/2019
1/05/2019

这只是原始数据。我们的模型不能理解它,因为它不能给出数值。为了从这个列中提取有用的特性,我们想把它转换成工作日和月份。

data['Date_of_Journey'] = pd.to_datetime(data['Date_of_Journey'])**OUTPUT** 2019-03-24
2019-01-05

然后从中获取工作日

data['day_of_week'] = data['Date_of_Journey'].dt.day_name()**OUTPUT** Sunday
Saturday

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从旅行日期开始,我们还会得到月份。

data['Journey_Month'] = pd.to_datetime(data.Date_of_Journey, format='%d/%m/%Y').dt.month_name()**OUTPUT** March
January

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

航空公司的出发时间

出发时间采用 24 小时制(22:20),我们希望将其绑定以获得更多信息。

我们将创建固定宽度的箱,每个箱包含一个特定的数字范围。通常,这些范围是手动设置的,具有固定的大小。在这里,我决定将时间分成 4 个部分。[0–5]、[6–11]、[12–17]和[18–23]是 4 个容器。我们不能在计数中有大的间隙,因为这可能会产生没有数据的空箱。这个问题通过基于数据的分布定位箱来解决。

data['Departure_t'] = pd.to_datetime(data.Dep_Time, format='%H:%M')a = data.assign(dept_session=pd.cut(data.Departure_t.dt.hour,[0,6,12,18,24],labels=['Night','Morning','Afternoon','Evening']))data['Departure_S'] = a['dept_session']

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们在“出发 _S”列中用“夜间”填充空值。

data['Departure_S'].fillna("Night", inplace = True)

持续时间

我们的持续时间栏中的时间是这样写的2h 50m。为了帮助机器学习算法获得有用的见解,我们将把这些文本转换成数字。

duration = list(data['Duration'])for i in range(len(duration)) :
    if len(duration[i].split()) != 2:
        if 'h' in duration[i] :
            duration[i] = duration[i].strip() + ' 0m'
        elif 'm' in duration[i] :
            duration[i] = '0h {}'.format(duration[i].strip())dur_hours = []
dur_minutes = []  

for i in range(len(duration)) :
    dur_hours.append(int(duration[i].split()[0][:-1]))
    dur_minutes.append(int(duration[i].split()[1][:-1]))

data['Duration_hours'] = dur_hours
data['Duration_minutes'] =dur_minutesdata.loc[:,'Duration_hours'] *= 60data['Duration_Total_mins']= data['Duration_hours']+data['Duration_minutes']

我们现在得到的结果本质上是连续的。

可视化数据后,删除持续时间少于 60 分钟的行是有意义的。

# Get names of indexes for which column Age has value 30indexNames = data[data.Duration_Total_mins < 60].index# Delete these row indexes from dataFramedata.drop(indexNames , inplace=True)

我们将删除有噪音或文本的列,这对我们的模型没有帮助。为了获得更好的见解,我们将这些内容转化为更新的专栏。

data.drop(labels = ['Arrival_Time','Dep_Time','Date_of_Journey','Duration','Departure_t','Duration_hours','Duration_minutes'], axis=1, inplace = True)

虚拟变量

我们设计了几乎所有的功能。我们已经处理了缺失值、装箱的数字数据,现在是时候将所有变量转换为数字变量了。我们将使用get_dummies()来进行转换。

cat_vars = ['Airline', 'Source', 'Destination', 'Route', 'Total_Stops',
       'Additional_Info', 'day_of_week', 'Journey_Month', 'Departure_S' ]
for var in cat_vars:
    catList = 'var'+'_'+var
    catList = pd.get_dummies(data[var], prefix=var)
    data1 = data.join(catList)
    data = data1

data_vars = data.columns.values.tolist()
to_keep = [i for i in data_vars if i not in cat_vars]data_final=data[to_keep]

下一步

我们将不得不重新划分数据集。特征工程有助于从原始数据中提取信息,即它已经创建了许多特征。这意味着我们需要找到整个批次的主要特征。这也被称为维数灾难。我们用特征选择来对抗特征生成。

如果你想了解 峰度 ,点击这里。

如果你想了解数据科学 背后的 统计学,点击这里。

如果你想获得一个 机器学习入门 ,点击这里。

感谢阅读!

***这里也可以给支持:https😕/*buymeacoffee.com/divadugar

雪花中的特征工程

原文:https://towardsdatascience.com/feature-engineering-in-snowflake-1730a1b84e5b?source=collection_archive---------12-----------------------

在之前的故事中,我展示了一个真正的云数据仓库能够处理结构化数据的常见机器学习任务,如训练基于树的模型。

我的论点的要点是,如果架构是正确的,用例是常见的,那么您不应该需要将数据从数据库转移到通用计算集群中。作为雪花用户,您的分析工作负载可以利用其微分区来删除大量处理,并且预热后的每秒计费计算集群已准备好参与非常短暂但繁重的数字处理任务。

然而,人们常说,数据科学家 80%的时间都花在特性工程上,这几乎总是在你可以训练模型之前的一个要求。这部分也可以在数据库中完成吗?

什么是特征工程?

特征工程是关于转换输入数据,以便它可以被机器学习算法使用。数据背后有许多上下文含义,人类使用我们丰富的经验和能力来概括我们的学习,从而直观地解释这些含义。但是,尽管有一些营销材料,机器还不能自己做到这一点。

一些 ML 算法要求它们的输入是“工程化的”,这有几个原因,但用一个例子来说明:假设你的两个输入变量是工资和年龄,你的目标变量是购买产品的可能性。在你的数据集中,工资可能在 30,000 到 200,000 美元之间,年龄可能在 10 到 90 岁之间。读者们可能会理解,20 岁的年龄差距比 20 美元的工资差距要重要得多,但对于算法来说,它们只是需要拟合曲线的数字。因此,为了做好工作,计算机需要你考虑到这一点,在这种情况下,你可以通过缩放来做到这一点(例如,使工资和年龄都在 0.0 和 1.0 之间)。您还可以使用像宁滨(又名分桶)这样的技术将值放入固定数量的值范围之一(例如,工资范围 0 到 6)。

实现数据库内

当你看到像 scikit-learn 的预处理库这样的东西时,从头开始实现它们以在数据库中运行的前景可能会令人望而生畏,想象出处理低级数字操作的数千行代码的图像。

我不觉得它令人生畏的原因是,它的大部分实际上不是从零开始的。从概念上讲,它更像是实现一个金字塔的顶端部分,那里已经有了许多基础的构建块。例如,在我的决策树算法中,我使用标准偏差减少来计算树分裂。但是我不需要从头开始实现标准差。这是一个内置的雪花函数,我只需要把它插在正确的地方。有用的不仅仅是聚合函数;窗口函数有助于分解数据集,表函数非常适合切片和切块。

同样的原则应该适用于特征工程。如果你真的找到了一个缺失的构建模块,或者只是想把事情收拾得干净整洁,你总是可以成为一个优秀的工程师,像雪花函数一样孤立地解决它,然后根据需要调用它。继续读下去,你会发现我这样做了好几次。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这个故事中,我将研究 scikit-learn 预处理函数的完整列表,并展示一个等效的雪花解决方案,使用所有雪花帐户可用的样本数据。

除非另有说明,否则数据库为“雪花 _ 样本 _ 数据”,模式为“TPCDS_SF10TCL”。

如果您像我一样在 Tableau Desktop 中运行这些查询,请确保在最后添加一个示例(500 行),因为其中一些表有数十亿行,真的会挤满一个图表。

preprocessing.Binarizer

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

根据阈值将数据二值化(将特征值设置为 0 或 1)

作为热身练习,让我们将 customer _ demographic dependencies 计数二进制化。假设它只与你是否有家属相关,我们不在乎有多少。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Before

select iff(CD_DEP_COUNT>0, true, false ) as CD_DEPENDANTS
from CUSTOMER_DEMOGRAPHICS

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After

preprocessing.FunctionTransformer

从任意可调用函数构造一个转换器。

这个只是让你 BYO 变换函数,所以不是真正的算法。我们知道我们可以对存储过程或函数做同样的事情。

preprocessing.KBinsDiscretizer

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将连续数据分入区间。

在 scikit-learn 函数中有几个选择。编码器参数可以是“onehot”、“onehot-dense”或“ordinal”。我将使用 ordinal 这个简单的方法,但是不要担心,我们稍后必须实现一个热编码作为它自己的函数。

同样,为了简单起见,我将采取“统一”的策略,但我计划在以后的帖子中处理 k-means。

这是商店销售的 SS_LIST_PRICE:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Before

放入 20 个等宽的箱子中:

with aggregates as (
  select min(SS_LIST_PRICE) as min_price,
  max(SS_LIST_PRICE) as max_price
  from STORE_SALES)
select width_bucket("SS_LIST_PRICE",min_price,max_price,20)
from aggregates,STORE_SALES
limit 100

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After

这是垃圾箱的样子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

preprocessing.KernelCenterer

居中一个核矩阵

我不知道您怎么想,但是我在关系数据库的上下文中并不经常遇到内核矩阵,所以很难想象这是如何应用的。如果你认为我在这里太容易摆脱困境,请发表评论。

preprocessing.LabelBinarizer

以一对一的方式二进制化标签

这个和 OneHotEncoder 太像了,这里就不赘述了。

preprocessing.LabelEncoder

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用 0 和 n_classes-1 之间的值对标签进行编码。

这里我们利用了雪花中一些很酷的数组函数,将所有不同的值用 array_agg 推入一个数组,这样我们就可以使用 array_position 函数对标签进行编码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Before

with distinct_values as (
    select array_agg(distinct cd_marital_status) as marital_status_array from CUSTOMER_DEMOGRAPHICS)
select array_position(cd_marital_status::variant,marital_status_array) 
from distinct_values,CUSTOMER_DEMOGRAPHICS

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After

preprocessing.MultiLabelBinarizer

在可迭代的可迭代和多标签格式之间转换。

真正适用的唯一方法是使用雪花数组存储多标签数据。我怀疑这是否普遍,但这也不是一个疯狂的想法。我将创建一个例子来匹配 doco 中的第二个例子。

示例表创建:

create or replace temporary table films as (
  select array_construct('sci-fi','thriller') as genres
  union
  select array_construct('comedy'));

select * from films

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们还需要一个简单的函数,给定一个完整的可能值数组,告诉我们每个值是否出现在给定的子集中。

create or replace function array_element_matches(MASTER_ARRAY array,ELEMENTS array)
  returns array
  language javascript
as
$$
  return MASTER_ARRAY.map(function(master_element){
    for (var i=0;i<ELEMENTS.length;i++){
      if (ELEMENTS[i]==master_element){
        return true;
      }
    }
    return false;
  });
$$
;

通过演示,该功能更容易解释:

select array_element_matches(
   array_construct('a','b','c','d','e'),
   array_construct('b','d')) as test

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用创建的函数,我们可以继续为我们的电影制作一个多标签二值化器。首先,我们将所有数组合并成一个数组,并获得一个不同值的列表。

然后我们可以使用函数来进行二值化。

with distinct_values as (
    select array_agg(distinct value::string) as all_genres_array from films
    ,lateral flatten(input => genres) g
    )
select all_genres_array,genres,array_element_matches(all_genres_array,genres) as genres_mlb
from distinct_values,films

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我只解释一下 array_agg +横向展平部分。当您对数组值调用 array_agg 时,您会得到包含数组的数组(例如[ [1,2],[3,4] ])。这些很难处理,所以我们需要把它们都放到一个大的数组中([1,2,3,4])。

preprocessing.MaxAbsScaler

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按最大绝对值缩放每个特征。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Before

这很简单,用每个值除以总的最大绝对值:

with aggregates as (
  select max(abs(WS_LIST_PRICE)) as max_abs_dep
  from WEB_SALES)
select WS_LIST_PRICE,max_abs_dep,WS_LIST_PRICE / max_abs_dep AS WS_LIST_PRICE_SCALED
from aggregates,WEB_SALES

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After

preprocessing.MinMaxScaler

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过将每个特征缩放到给定范围来变换特征。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Before

这类似于 MaxAbsScaler,除了我们使用最小值和最大值。

with aggregates as (
  select min(CD_DEP_COUNT) as min_dep,max(CD_DEP_COUNT) as max_dep
  from CUSTOMER_DEMOGRAPHICS)
select (CD_DEP_COUNT - min_dep)
        / (max_dep - min_dep)
from aggregates,CUSTOMER_DEMOGRAPHICS

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After

preprocessing.Normalizer

将样本分别标准化为单位标准。

这个公式需要一个除以零的保护措施,所以我们把它放在一个函数里。在这个例子中,我们将使用 L2 标准

create or replace function normalize_scale(x float,y float)
  returns float
  language javascript
as
$$
  var magnitude=Math.sqrt(X**2 + Y**2);
  if (magnitude==0){
    return 0;
  }else{
    return X/magnitude;
  }
$$
;

这是客户人口统计数据中的 DEP 就业人数和 DEP 大学人数。它们是离散值而不是连续值,但是你明白了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Before

然后,根据自身和 CD _ DEP _ 学院 _ 计数对 CD _ DEP _ 就业 _ 计数进行规范化:

select 
  normalize_scale(CD_DEP_EMPLOYED_COUNT,CD_DEP_COLLEGE_COUNT) as dep_employed_count_normalized,
  normalize_scale(CD_DEP_COLLEGE_COUNT,CD_DEP_EMPLOYED_COUNT) as dep_college_count_normalized
from CUSTOMER_DEMOGRAPHICS
limit 1000000

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

preprocessing.OneHotEncoder

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将分类整数特性编码为一个独热数值数组。

你是否对类别或标签进行编码无关紧要,这只是一个 if 语句。因为这将为每一列提供一个特性,所以您也可以只选择您计划使用的特定特性。

让我们从 CUSTOMER_DEMOGRAPHICS 返回 CD _ marriage _ STATUS:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Before

select 
    iff(cd_marital_status='S',true,false) as MARITAL_STATUS_S,
    iff(cd_marital_status='D',true,false) as MARITAL_STATUS_D,
    iff(cd_marital_status='W',true,false) as MARITAL_STATUS_W,
    iff(cd_marital_status='U',true,false) as MARITAL_STATUS_U
from CUSTOMER_DEMOGRAPHICS

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After

请注意,您可以在此处轻松滚动标签,例如,将离婚和丧偶视为同一要素。

preprocessing.OrdinalEncoder

将分类特征编码为一个整数数组。

在关系数据库中这真的没有意义,它不像你按列付费。如果在一个数组中有固定数量的特征,只需将它们分成各自的列,并分别编码。

preprocessing.PolynomialFeatures

生成多项式和交互特征。

这些可以通过添加您计划使用的计算列(使用乘法、平方等)来轻松完成。

preprocessing.PowerTransformer

按特征应用幂变换,使数据更像高斯。

“yeo-johnson”或“box-cox”是可用于建立正态分布的两个函数。虽然它们不太复杂,但是有一些参数需要自动选择,这是一个需要进行一些构建的领域。Scikit-learn 包含许多参数优化函数(在本例中,是 Brent 的方法),这将是一个很难实现的函数。也许在以后的帖子里?

preprocessing.QuantileTransformer

使用分位数信息变换特征。

简而言之,scikit-learn 实现:

  1. 计算一组百分位数(默认为 1000)
  2. 使用这些百分位数的线性插值将每个值映射到 0–1 的范围内。

我会试着先想象一下。使用 CUSTOMER 表中的 C_BIRTH_YEAR 列,我们可以想象在 x 轴上展开的年份。

选择 10 作为一个简单的百分位数,我们可以计算这些,并在 y 轴上绘制。第一个点(10%,或 0.1)表示出生年份,您必须选择将 10%的值放在它的左边,将 90%的值放在它的右边。如果您一次做一个,您可以使用雪花的 PERCENTILE_*函数之一,但是我们需要在一个查询中生成它们。

-- Make a 0-percentile row containing the minimum value 
-- (i.e. 100% of values fall to its right)
select 0 as quantile,(select min(SS_LIST_PRICE) from STORE_SALES) as quantile_value
union all
-- generate 10 percentile values (10% increments) and for each, determine the maximum value that  
-- will divide the dataset row counts by that percentage
select quantile,max(case when rownum/numrows <= quantile then SS_LIST_PRICE end) as quantile_value
from 
(
  select 
  row_number() over (partition by null order by null) -1 as seq,
  0.1+(seq/10) as quantile
  from table(generator(rowcount => 10)) v 
  order by 1
) quantiles,
(
  select SS_LIST_PRICE,
    row_number() over (partition by NULL order by SS_LIST_PRICE) as rownum,
    count(*) over (partition by NULL) as numrows
  from STORE_SALES sample(10000 rows)
  where SS_LIST_PRICE is not null
) totals
group by quantile
order by quantile_value

这给了我们标价百分比:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在让我们把这些都放在 2D 图表上:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一旦我们计算出我们的百分位数,就需要遍历 x 轴上的每个 SS_LIST_PRICE 值,确定哪两个蓝点(百分位数)离它最近,然后使用线性插值公式来确定它在 y 轴上的值(红色问号)。

首先,我们需要一个线性插值公式(通常被称为“lerp”的字符计数意识):

create or replace function linear_interpolation(x1 float,y1 float,x2 float,y2 float, x float)
  returns float
  as
  $$
    y1 + ((x-x1)/(x2-x1)) * (y2-y1)
  $$
  ;

然后我们进一步扩展我们的查询,将它们联系在一起:

with quantile_values as(
  -- Make a 0-percentile row containing the minimum value (i.e. 100% of values fall to its right)
  select 0 as quantile,(select min(SS_LIST_PRICE) from STORE_SALES) as quantile_value
  union all
  -- generate 10 percentile values (10% increments) and for each, determine the maximum value that  
  -- will divide the dataset row counts by that percentage
  select quantile,max(case when rownum/numrows <= quantile then SS_LIST_PRICE end) as quantile_value
  from 
  (
    select 
    row_number() over (partition by null order by null) -1 as seq,
    0.1+(seq/10) as quantile
    from table(generator(rowcount => 10)) v 
    order by 1
  ) quantiles,
  (
    select SS_LIST_PRICE,
      row_number() over (partition by NULL order by SS_LIST_PRICE) as rownum,
      count(*) over (partition by NULL) as numrows
    from STORE_SALES sample(1000 rows)
    where SS_LIST_PRICE is not null
  ) totals
  group by quantile
  order by quantile_value
)
select SS_LIST_PRICE as x,
(select max(b.quantile) from quantile_values b where b.quantile_value<a.SS_LIST_PRICE) as y1,
(select min(b.quantile) from quantile_values b where b.quantile_value>=a.SS_LIST_PRICE) as y2,
(select max(b.quantile_value) from quantile_values b where b.quantile_value<a.SS_LIST_PRICE) as x1,
(select min(b.quantile_value) from quantile_values b where b.quantile_value>=a.SS_LIST_PRICE) as x2,
coalesce(CONSORTIUM_SHARING.MOBILE.linear_interpolation(x1,y1,x2,y2,x),0) as y
from STORE_SALES a sample(1000 rows)
where SS_LIST_PRICE is not null
order by SS_LIST_PRICE

注意,从性能的角度来看,在最终查询中使用 min & max 并不理想,这就是我这次必须对表进行采样的原因。相反,我们可以构建自己的用户定义的表函数,利用我们所知道的值的顺序,一次性传递数据。但是我们现在有用来说明的,我们剩下的是从 0 到 1 的分布:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After

preprocessing.RobustScaler

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用对异常值稳健的统计数据缩放要素。

这种方法使用四分位范围,所以我们不是取最小值和最大值,而是取中间值,然后在两边再取一次。

在本例中,我们将在 WEB_RETURNS 表中缩放 WR_RETURN_AMT。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Before

with med as (select 
  median(WR_RETURN_AMT) as median_ret_amt 
    from WEB_RETURNS),
  lower_med as (select median(WR_RETURN_AMT) as lower_median 
    from WEB_RETURNS, med where WR_RETURN_AMT< median_ret_amt),
  upper_med as (select median(WR_RETURN_AMT) as upper_median 
    from WEB_RETURNS, med where WR_RETURN_AMT> median_dep)
select (WR_RETURN_AMT- lower_median) / (upper_median - lower_median) as wr_return_amount_scaled
from lower_med,upper_med,WEB_RETURNS

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

After

preprocessing.StandardScaler

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过移除平均值并缩放至单位方差来标准化特征。

使用与前面示例相同的 WEB_RETURNS 列。

with aggregates as (
  select avg(WR_RETURN_AMT) as avg_dep,
  stddev(WR_RETURN_AMT) as stddev_dep
  from WEB_RETURNS)
select (WR_RETURN_AMT - avg_dep)
        / (stddev_dep) as wr_return_amount_scaled
from aggregates,WEB_RETURNS

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

通过写这篇文章,我学到了一些东西。

有用!

我认为这是一次巨大的成功。如果你把它们写成查询,会有一些样板文件,但是如果它们是由前端应用程序发出的,或者是代码库中的下推语句,那就不是问题了。电力变压器是可行的,但我会等到我需要它。

大部分功能都不吓人

滑动和挤压主要涉及标准聚合。长时间用不同的名字称呼他们,很容易忘记这一点。

然而,当从像 Python 这样的命令式语言转向像 SQL 这样的更具声明性的语言时,它确实倾向于要求重新思考,而不是直接移植代码。

Tableau 是一个很好的搭档

观想是对你试图实现的目标的最好强化。使用雪花,您可以轻松地在非常大的数据集上进行特征工程,而无需采样,只需记住,要求 Tableau 在图表上放置数十亿个点不是一个好主意。但是对最终结果进行采样要比对初始输入进行采样好得多!

一如既往,欢迎评论和反馈。

SQL 和 Python 中的特征工程:一种混合方法

原文:https://towardsdatascience.com/feature-engineering-in-sql-and-python-a-hybrid-approach-b52347cd2de4?source=collection_archive---------19-----------------------

设置您的工作站,减少工作场所的混乱,保持一个干净的命名空间,并毫不费力地保持您的数据集最新

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在了解 Pandas 之前,我很早就知道 SQL,Pandas 忠实地模拟 SQL 的方式引起了我的兴趣。一般来说,SQL 是为分析人员设计的,他们将数据整理成信息丰富的报告,而 Python 是为数据科学家设计的,他们使用数据来构建(并过度拟合)模型。尽管它们的功能几乎相同,但我认为这两种工具对于数据科学家高效工作都是必不可少的。根据我和熊猫相处的经验,我注意到了以下几点:

  • 在探索不同的特性时,我最终得到了许多 CSV 文件。
  • 当我在一个大的数据帧上进行聚合时,Jupyter 内核就会死亡。
  • 在我的内核中,有多个数据帧有令人困惑的(长的)名字。
  • 我的特征工程代码看起来很难看,分散在许多单元中。

当我直接在 SQL 中开始特性工程时,这些问题自然就解决了。所以在这篇文章中,我将通过一个带回家的挑战数据集来分享一些我最喜欢的技巧。如果你懂一点 SQL,是时候好好利用它了。

安装 MySQL

首先,您需要一台 SQL 服务器。我在这篇文章中使用 MySQL。你可以通过安装 MAMP、WAMP 或 XAMPP 等本地桌面服务器来获得 MySQL 服务器。网上有很多教程,值得不厌其烦的去做。

在设置好你的服务器后,确保你已经准备好三样东西:用户名,密码,端口号。通过终端输入以下命令登录(这里我们有用户名“root”,密码 1234567)。

mysql -uroot -p1234567

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后在 MySQL 控制台中创建一个名为“Shutterfly”的数据库(你可以随意命名)。这两个表将被加载到这个数据库中。

create database Shutterfly;

安装 sqlalchemy

您将需要 Pandas 和 sqlalchemy 来使用 Python 中的 SQL。我打赌你已经有熊猫了。然后通过激活您想要的环境来安装 sqlalchemy,启动 Jupyter notebook,并输入:

pip install sqlalchemy

sqlalchemy 模块还需要 MySQLdbmysqlclient 模块。根据你的操作系统,这可以使用不同的命令来安装。

将数据集加载到 MySQL 服务器

在这个例子中,我们将从两个 CSV 文件中加载数据,并直接在 MySQL 中设计特性。为了加载数据集,我们需要使用用户名、密码、端口号和数据库名实例化一个引擎对象。将创建两个表:在线订单。将在每个表上创建一个自然索引。

在 MySQL 控制台中,您可以验证表是否已经创建。

分割数据集

这可能看起来违背直觉,因为我们还没有构建任何特性。但它实际上非常简洁,因为我们所需要做的就是按照索引分割数据集。通过设计,我还包含了我们试图预测的标签(事件 2)。当加载特性时,我们将简单地用特性表连接索引。

在 MySQL 控制台中,您可以验证训练集和测试集是否已创建。

特征工程

这是最重要的部分。我直接用 Sublime 文本编写 SQL 代码,并通过将它们粘贴到 MySQL 控制台来调试我的代码。因为这个数据集是一个事件日志,所以我们必须避免将未来的信息泄露到每个数据点。正如您所想象的,每个特性都需要在历史上进行聚合!

连接表是最慢的操作,所以我们希望从每个连接中获得尽可能多的特征。在这个数据集中,我实现了四种类型的连接,产生了四组要素。细节并不重要,但是你可以在这里找到我所有的 SQL 片段。每个代码片段创建一个表。**索引被保留,并且必须与训练集和测试集中的响应变量正确匹配。**每个片段的结构如下:

要生成功能表,请打开一个新的终端,导航到包含 sql 文件的文件夹,并输入以下命令和密码。第一个代码片段创建了一些必要的索引来加速连接操作。接下来的四个片段创建了四个功能表。如果没有索引,连接将永远无法完成。使用索引,大约需要 20 分钟(在本地机器上还不错)。

mysql < add_index.sql -uroot -p1234567
mysql < feature_group_1.sql -uroot -p1234567
mysql < feature_group_2.sql -uroot -p1234567
mysql < feature_group_3.sql -uroot -p1234567
mysql < feature_group_4.sql -uroot -p1234567

现在,数据库中应该有下面的表。请注意,派生功能与原始事件日志分开存储,这有助于防止混淆和灾难。

加载功能

这里我写了一个从 MySQL 服务器获取数据的实用函数。

  • 该函数将表名“trn_set”(训练集)或“tst_set”(测试集)作为输入,如果您只需要数据的一个子集,还需要一个可选的 limit 子句。
  • 删除唯一列和大部分值缺失的列。
  • 日期列映射到月份,以帮助捕捉季节性影响。
  • 请注意特征表是如何连续连接的。这实际上是有效的,因为我们总是以一对一的映射来连接索引。

最后,让我们来看看 5 个培训示例及其特点。

现在您已经有了一个定义良好的数据集和特性集。您可以调整每个要素和缺失值的比例,以满足模型的要求。

对于基于树的方法,它对特征缩放是不变的,我们可以直接应用模型,只需专注于调整参数!在这里看一个普通的梯度推进机器的例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很高兴看到除了类别功能之外,所有有用的功能都被设计出来了。我们的努力得到了回报!此外,event2 最具预测性的特性是在 event2 中观察到多少 nulls 值。这是一个说明性的例子**,我们不能用中值或平均值来代替空值**,因为它们缺失的事实与响应变量相关!

摘要

如您所见,我们没有中间 CSV 文件,笔记本中有一个非常干净的名称空间,我们的功能工程代码减少到几个简单的 SQL 语句。在两种情况下,SQL 方法更加高效:

  • 如果您的数据集部署在云上,您可能能够运行分布式查询。如今,大多数 SQL server 都支持分布式查询。在 Pandas 中,您需要一些名为 Dask DataFrame 的扩展。
  • 如果你能负担得起实时拉取数据,你可以创建 SQL 视图而不是表。这样,每次用 Python 拉数据,你的数据都会一直是最新的

这种方法的一个基本限制是,您必须能够用 Python 直接连接到 SQL server。如果这是不可能的,您可能需要下载一个 CSV 文件格式的查询结果,并在 Python 中加载它。

我希望这篇文章对你有所帮助。虽然我并不提倡一种方法优于另一种方法,但有必要了解每种方法的优点和局限性,并在我们的工具包中准备好这两种方法。因此,我们可以应用在约束条件下最有效的方法。

特征工程技术

原文:https://towardsdatascience.com/feature-engineering-techniques-9a57e4901545?source=collection_archive---------12-----------------------

介绍一些可用于为机器学习分析准备原始特征的主要技术。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by “My Life Through A Lens” on Unsplash

介绍

在开始机器学习分析之前,特征工程是最重要的步骤之一。创建尽可能最好的机器学习/深度学习模型当然有助于实现良好的结果,但选择正确格式的正确功能来输入模型可以大大提高性能,从而带来以下好处:

  • 使我们能够使用更简单的机器学习模型来实现良好的模型性能。
  • 使用更简单的机器学习模型,增加了我们模型的透明度,从而使我们更容易理解如何进行预测。
  • 减少使用集成学习技术的需求。
  • 减少执行超参数优化的需求。

为了充分利用给定的数据,可以使用的其他常用技术是特征选择提取,我在以前的帖子中已经谈到过。

我们现在将介绍一些最常见的特征工程技术。大多数基本特征工程技术包括发现数据中的不一致通过组合/分割现有特征来创建新特征

本文使用的所有代码都可以在我的 GitHub 账户上的链接中找到。

对于这个示例,我决定创建一个简单的数据集,它受到数据分析过程中面临的一些最常见问题的影响(例如,缺失数字、异常值、缩放问题等)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 1: Dataset Head

对数变换

使用对数变换时,原始要素的分布被变换为更接近高斯分布。这可能特别有用,尤其是当使用机器学习模型时,例如线性判别分析(LDA)和朴素贝叶斯分类器,它们假设它们的输入数据遵循高斯分布。

在本例中,我将对数据集中所有可用的数字要素应用对数变换。此外,我还决定用各自的最小值减去原始特征,然后将它们加到一起,以确保这些列中的每个元素都是正的(对数只支持正值)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2: Logarithmically Transformed Dataset

归罪

插补是使用适当的值识别和替换数据集中缺失值的艺术。数据集中缺失值的存在可能是由许多可能的因素造成的,例如:隐私问题、使用传感器记录数据时的技术故障、人为错误等

有两种主要的插补类型:

  • 数字插补:数字特征中缺失的数字可以使用许多不同的技术进行插补。使用的一些主要方法是用受影响列的总体平均值或模式替换缺失值。如果你有兴趣了解更多先进技术,你可以在这里找到更多信息
  • 分类插补:对于分类特征,缺失值通常使用整体列模式替换。在某些特殊情况下,如果分类列结构没有很好地定义,那么最好替换丢失的值,创建一个新的类别,并将其命名为“未知”或“其他”。

我们现在可以,首先,通过运行下面几行来检查哪些特性受到 NaNs(不是一个数字)的影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 3: Percentage of NaNs in each Feature

处理缺失数字的最简单方法之一是删除受其影响的所有行。不过,最好设置一个阈值(例如 20%),只删除缺失数超过阈值的行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 4: Imputation by deleting features with excessive NaNs

另一个可能的解决方案是,对于我们的数值和分类数据,用列模式替换所有 nan。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 5: Imputation using column mode

处理日期

日期对象由于其格式,对于机器学习模型来说可能很难处理。因此,有时有必要将一个日期分成多列。同样的考虑可以应用于数据分析中的许多其他常见情况(例如,自然语言处理)。

在我们的示例中,我们现在要将日期列分成三个不同的列:年、月和日。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 6: Dealing with Dates

极端值

异常值是数据点的一小部分,这些数据点与特征中的其余观察值相距甚远。异常值可能被引入到数据集中,主要是因为收集数据时的错误,或者是因为我们的特定特征所特有的特殊异常。

使用四种主要技术来识别异常值:

  • 数据可视化:通过目视检查数据分布来确定异常值。
  • Z-Score :如果我们知道我们的特征分布是高斯分布,就使用 Z-Score。事实上,当使用高斯分布时,我们知道分布的大约 2 个标准偏差意味着大约 95%的数据将被覆盖,而远离平均值的 3 个标准分布将覆盖大约 99.7%的数据。因此,使用介于 2 和 3 之间的因子值,我们能够非常准确地删除所有异常值。如果你有兴趣了解更多关于高斯分布的信息,你可以在这里找到更多信息。
  • 百分位数:是另一种识别异常值的统计方法。当使用百分位数时,我们假设数据的某个顶部和底部百分比是异常值。使用这种方法的关键点是找到最佳百分比值。一种有用的方法是在应用百分位数法检查总体结果之前和之后,将数据可视化。
  • 封顶:我们不是删除异常值,而是用我们列中最高的正常值来替换它们。

其他常用于检测异常值的更高级技术有 DBSCAN隔离林

继续我们的例子,我们可以从左边的两个数字特征(X2,X3)开始。通过使用 Seaborn 创建一个简单的箱线图,我们可以清楚地看到 X2 有一些异常值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 7: Examining Outliers using Data Visualization

使用 Z 分数(因子为 2)和百分位数方法,我们现在可以测试在 X2 将识别出多少异常值。如下面的输出框所示,使用 Z 分数确定了 234 个值,而使用百分位数方法删除了 800 个值。

8000
7766
7200

此外,还可以通过限制异常值来处理异常值,而不是丢弃它们。

扔掉

宁滨是一种用于平滑噪声数据的常用技术,通过将数字或分类特征划分到不同的箱中。因此,这可以帮助我们降低过度拟合的风险(尽管可能会降低我们的模型精度)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 8: Binning Numeric and Categoric Data

分类数据编码

大多数机器学习模型目前不能处理分类数据,因此通常需要在将所有分类特征输入到机器学习模型之前将其转换为数字。

可以在 Python 中实现不同的技术,例如:One Hot Encoding(转换要素)和 Label Encoder(转换标注)。

一种热编码采用一个特征,并将它分割成与原始列中不同类别的数量一样多的列。然后,它给所有没有该特定类别的行分配一个 0,给所有有该类别的行分配一个 1。使用 Pandas***get _ dummies()***函数可以在 Python 中实现一个热编码。

Label Encoder 通过为所有分类案例分配不同的编号并将它们存储在一列中来替代所有分类案例。

不使用具有正常特征的标签编码器是非常优选的,因为一些机器学习模型可能会混淆,并认为具有比其他值更高的值的编码情况可能对它们更重要(按层次顺序考虑它们)。当使用一种热编码时,这种情况不会发生。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 9: Difference between One Hot Encoding and Label Encoding [1]

我们现在可以继续将数据集划分为特征( X )和标签( Y ),然后分别应用一个热编码和标签编码器。

缩放比例

在大多数数据集中,数字特征都有不同的范围(例如,身高与体重)。虽然,对于一些机器学习算法来说,将我们的输入特征限制在一个定义的范围内是很重要的。事实上,对于一些基于距离的模型,如朴素贝叶斯、支持向量机和聚类算法,如果它们都有不同的范围,那么几乎不可能比较不同的特征。

缩放特征的两种常见方式是:

  • **标准化:**缩放输入要素,同时考虑它们的标准偏差(使用标准化,我们的变换后的要素将看起来类似于正态分布)。这种方法可以降低异常值的重要性,但由于标准差的差异,可能会导致要素之间的不同范围。通过使用StandardScaler()可以在 scikit-learn 中实现标准化。
  • **归一化:**在 0 和 1 之间的范围内缩放所有特征,但是会增加异常值的影响,因为没有考虑每个不同特征的标准偏差。使用minmax scaler()可以在 scikit-learn 中实现归一化。

在本例中,我们将使用标准化,然后我们将处理异常值。如果您正在处理的数据集没有受到离群值的广泛影响,scikit-learn 还提供了另一个名为 的标准化函数,该函数可以在默认情况下减少离群值的影响。

自动化特征工程

为了使特征工程过程自动化,在过去几年中已经开发了不同的技术和软件包。当对我们的数据集进行第一次分析时,这些肯定会产生有用的结果,但是它们仍然不能完全自动化整个特征工程过程。关于数据的领域知识和数据科学家在对原始数据建模以最适合分析目的方面的专业知识是不可替代的。Python 中最流行的自动特征选择库之一是 Featuretools

结论

现在是时候通过使用随机森林分类器来最终测试我们抛光的数据集预测准确性了。如下所示,我们的分类器现在成功地实现了 100%的预测准确率。

1.40625
[[1204    0]
 [   0 1196]]
              precision    recall  f1-score   support

           0       1.00      1.00      1.00      1204
           1       1.00      1.00      1.00      1196

   micro avg       1.00      1.00      1.00      2400
   macro avg       1.00      1.00      1.00      2400
weighted avg       1.00      1.00      1.00      2400

希望您喜欢这篇文章,感谢您的阅读!

联系人

如果你想了解我最新的文章和项目,请在媒体上关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:

文献学

[1]什么是 One Hot Encoding,如何进行?迈克尔·德尔索尔,中号。访问地址:https://medium . com/@ michaeldelsole/what-one-hot-encoding-and-how-do-it-f0ae 272 f 1179

Python 中的特征工程技术

原文:https://towardsdatascience.com/feature-engineering-techniques-in-python-97977ecaf6c8?source=collection_archive---------19-----------------------

特征工程是每个机器学习项目中至关重要的一部分。在这篇文章中,我们将围绕一些技术来处理这项任务。请不要犹豫提出新的想法,我会尽可能保持这篇文章的更新。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

合并训练和测试

当执行特征工程时,为了有一个通用的模型,如果你有两个文件,只需将它们合并(训练和测试)就可以了。

df = pd.concat([train[col],test[col]],axis=0)
#The label column will be set as NULL for test rows# FEATURE ENGINEERING HEREtrain[col] = df[:len(train)]
test[col] = df[len(train):]

记忆减少

有时,列的类型编码不是最佳选择,例如,用 int32 编码只包含 0 到 10 的值的列。最流行的函数之一是使用一个函数,通过将列的类型转换为尽可能最好的类型来减少内存的使用。

移除异常值

移除异常值的常用方法是使用 Z 值。

如果您希望删除至少有一列包含异常值(用 Z 得分定义)的每一行,您可以使用以下代码:

from scipy import stats
df[(np.abs(stats.zscore(df)) < 3).all(axis=1)]

南诡计

一些基于树的算法可以处理 NAN 值,但他会在 NAN 和非 NAN 值之间有一个步骤,这有时可能是无意义的。一个常见的技巧是用低于所考虑的列中最低值的值填充所有 nan 值(例如-9999)。

df[col].fillna(-9999, inplace=True)

分类特征

您可以使用标注编码来处理分类要素,将其作为数字来处理。您也可以决定将它们视为类别。我建议两者都尝试一下,并通过这一行代码(在标签编码之后)保持交叉验证。

df[col] = df[col].astype('category')

组合/拆分

有时字符串变量在一个变量中包含多个信息。比如FRANCE_Paris。您将需要使用正则表达式或使用拆分方法来拆分它,例如:

new **=** df["localisation"].str.split("_", n **=** 1, expand **=** True)
df['country'] = new[0]
df['city']=new[1]

否则,两个(字符串或数字)列可以合并为一列。例如,包含法国某个部门(75 代表巴黎)和地区代码(001)的列可以变成邮政编码:75001)

df['zipcode'] = df['departement_code'].astype(str)
                +'_'
                +df['disctrict_code'].astype(str)

线性组合

特征工程的一个共同特点是应用简单的数学运算来创造新的特征。例如,如果我们有一个矩形的宽度和高度,我们可以计算面积。

df['area'] = df['width'] * df['height']

计数栏

创建列从流行的value_count方法创建列对于基于树的算法来说是一种强大的技术,用来定义一个值是稀有的还是常见的。

counts = df[col].value_counts.to_dict()df[col+'_counts'] = df[col].map(counts)

处理日期

为了分析事件,处理日期和解析日期的每个元素是至关重要的。

首先,我们需要转换日期列(通常被认为是熊猫的字符串列)。其中最重要的一个领域是知道如何使用format参数。强烈推荐将这个站点保存为书签!😃

例如,如果我们要用下面的格式转换一个日期列:30 Sep 2019我们将使用这段代码:

df['date'] =  pd.to_datetime(df[col], format='%d %b %Y')

一旦您的列被转换为datetime,我们可能需要提取新闻列中的日期部分:

df['year'] =  df['date'].year
df['month'] = df['date'].month
df['day'] = df['date'].day

聚合/组统计

为了继续检测稀有和常见的值,这对于机器学习预测非常重要,我们可以决定基于静态方法来检测一个值在子组中是稀有还是常见。例如,我们想通过计算每个子类的平均值来了解哪个智能手机品牌用户的通话时间最长。

temp = df.groupby('smartphone_brand')['call_duration']
       .agg(['mean'])
       .rename({'mean':'call_duration_mean'},axis=1)df = pd.merge(df,temp,on='smartphone_brand',how=’left’)

利用这种方法,ML 算法将能够辨别哪个呼叫具有与智能手机品牌相关的 call_duration 的非公共值。

标准化/规范化

规范化有时非常有用。

为了实现列自身的规范化:

df[col] = ( df[col]-df[col].mean() ) / df[col].std()

或者可以根据另一列对一列进行规范化。例如,如果您创建一个组统计数据(如上所述),表明每周call_duration的平均值。然后,您可以通过以下方式消除时间依赖性

df[‘call_duration_remove_time’] = df[‘call_duration’] — df[‘call_duration_week_mean’] 

新的变量call_duration_remove不再随着时间的推移而增加,因为我们已经针对时间的影响对其进行了标准化。

Ultime 特色工程技巧

每一篇专栏文章都为预处理和模型训练增加了时间计算。我强烈建议测试一个新特性,看看这些特性如何改进(或不改进…)您的评估指标。 如果不是这样,你应该删除创建/修改的特征。

特征工程时间

原文:https://towardsdatascience.com/feature-engineering-time-3934038e0dbe?source=collection_archive---------10-----------------------

计算圆形/周期性特征的统计数据。

在本帖中,我们将讨论一种处理周期性特征的数学方法。
这可用于计算平均值。
(例如 23:50,22:40,00:30 的平均时间是多少?)
它也是聚类的一个有用的距离度量。
(比如 00:01 比 00:10 更接近 23:59,能不能优雅地解决这个?)
同样的方法在特征工程中证明是有用的,例如 ML 模型的圆形特征**“一天中的时间”“一年中的一天”“方位角”**。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

问题:

  1. 根据事件时间对事件进行聚类。
  2. 当一天中的时间是重要特征时,学习一些结果的模型。
  3. 计算每个用户完成一个动作几次的典型时间。

一个有问题的问题:

你如何处理集群中跨越到第二天的事件?一些用户可能是夜猫子或来自另一个时区;如果他们的活动持续到午夜,数学就变得更难了。
如果用户在 23:57、23:58 和 23:59 发送消息,计算平均值是简单的。用户迟到一分钟怎么办?显然我们不想把 00:00 加到平均值上。
假设您正试图预测某个依赖于一天中某个时间的结果,一个简单的模型(回归)将在午夜遭遇中断。

尝试 1,暴力:

让我们假设我们的用户必须在某个时刻离线,在某个时刻有一个边界,在其附近没有任何活动。
然后我们可以“移动”到那个时区,添加几个小时来将日期边界放在空白处。

问题:

我们可能有遍布全球的用户,他们有不同的活动模式,我们需要找到每个用户的时移。往好里说,这是非常低效的,往坏里说,这可能是不可能的。更糟糕的是,如果我们的假设是错误的呢?如果我们看不到活动和不活动之间如此清晰的界限,会怎么样?

灵感:平均风向。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Wind direction off the East coast.

看着风的地图,我们对风的平均方向(和大小)有很好的直觉。

359 和 1 之间的任意区别丝毫不会困扰我们,因为我们取的是向量平均值。当我们用手指指向一个方向时,360°的数学跳跃并不重要。

我们如何将这种直觉形式化?让我们把方向描述为一个二维向量(x,y),而不是一个单一的标量,也就是“航向”。

是的,让我们做那件事,有时间

让我们将时间映射到 24 小时制的圆周上

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The geometric representation of time of day lends itself to geometric clustering, averaging. (Code to generate these if you want to play around with them is provided in a notebook linked below.)

如果我们想要聚类,我们在 2d 空间(x,y)中聚类。如果要取平均值,就取点(重心)的几何平均值。
如果我们想在模型中使用时间作为特征,我们使用向量,(x,y)。

当平均值不在圆上时会发生什么。

尽管多于一个点的任何平均值都在圆内;范围越大,平均值离圆周越远。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Left: with small variance, the center is very close to the circle’s edge and shows the average time; Center: the high variance of the points puts the center inside the circle. The direction of the center indicates the average time, in this case 3:00. Right: A measure that is analogous to standard deviation can be derived by examining the arc defined by the chord that is bisected by that center.

摘要

通过将一天中的时间映射到代表 24 小时的 2D 圆,我们可以缓解时针从 23 到 0 时出现的不连续性。
这也适用于一年中的,或者实际的方向

这个新的 2D 向量(可以简单地是模型中的两列)表现得非常自然,它定义了一个将 23:59 和 00:01 放在一起的度量。同样,它把 12 月 31 日和 1 月 1 日放在一起。NNE 和 NNW 不关心它们在 360 — (0)的不同侧

这些不连续点一开始是任意的,但是只要我们只用一个维度,它们就一定会出现在某个地方。在 2D,一切又变得美好起来。

代码:

你可以克隆这个笔记本来玩代码。(还包含 pySpark 计算平均值的可行示例。)

附录,数学和操作方法:

把时间映射到圆周上很简单。

  1. 把时间转换成一个单一的数字[0,24]{或者[0,1],这可能是免费的}所以 12:36 可能是 12.6 {或者 0.525}。
  2. 然后将这个数字乘以 15,得到 360。{或 360 度}
  3. 然后取 SinCos 得到 YX 坐标。(开心注:SQL 有三角函数,别忘了换算成弧度)
  4. 要计算平均值,只需独立取 XY 的平均值。
  5. 使用*(E(Y)、E( X ))变换回时间,然后反向变换(弧度到角度,取消拉伸到 24H)*

奖金,标准发展:

  1. 用给出的勾股定理 XY 计算半径, r
  2. 代表标准差的圆弧由arccos(r)给出。证明这一点是留给读者的练习。

特征工程-将数据集转换成机器可用的格式

原文:https://towardsdatascience.com/feature-engineering-translating-the-dataset-to-a-machine-ready-format-af4788d15d6c?source=collection_archive---------23-----------------------

创建可以帮助机器学习算法工作的功能

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这一步使您的数据为最终训练做好准备。特性工程的方法基于对数据的良好理解,正如我在之前关于 EDA 的文章中提到的。我将继续使用 Kaggle 上的住房数据集(在上一篇文章中用于解释 EDA),来说明我是如何对它进行特征工程的。

测试和训练数据集都被合并,以从特征工程开始。我按照以下步骤得出最终数据集:

剔除异常值

在上一篇文章中,我举例说明了如何识别异常值。第一步是删除所有带有离群值的索引,因为离群值往往会向机器传递关于数据集的不准确信息。

将分类变量转换为数字变量

其次,分类变量需要转换为数值变量,因为大多数机器学习算法只能读取数值。

请注意,一些分类变量(如 Alley、GarageQual、BsmtQual)缺少一些值,而其他变量则由“Po”、“Gd”、“TA”等值填充。数据描述文件有助于解释这些变量的含义。在这种情况下,缺失的数据仅仅意味着房子可能没有小巷、车库或地下室。因此,我使用下面的代码来估算缺失值,并将现有值转换为数字值:

combined.Alley = combined.Alley.map({np.nan:0, 'Grvl':1, 'Pave':2})
combined.BsmtCond = combined.BsmtCond.map({np.nan:0, 'Po':1, 'Fa':2, 'TA':3, 'Gd':4, 'Ex':5})*combined is the name of the merged dataset of train and test

这是可能的,因为赋值就像等级变量,例如“Fa”比“Po”好,“TA”比“Fa”好。

但是对于那些值不表示等级的分类变量,pandas 的 get_dummies 函数将行转换为列。

输入分类和数值特征的缺失值

但在此之前,重要的是估算所有分类变量的缺失值。例如,mischaracter 和 MasVnrType 缺少值,这可能表明它们不存在。有一个简单的方法来交叉检查这个。如果 MiscVal 和 MasVnrArea 是对应于缺失值的零,则假设是正确的,可以估算如下:

combined.MiscFeature.fillna('NA', inplace=True)
combined.MasVnrType.fillna('None', inplace=True)

对于 KitchenQual 或 SaleType 等其他特性,使用值的模式对它们进行估算更安全。

数字特征中缺失的值可以通过放置零来估算,具体取决于特征。例如,GarageCars 列中缺少值可能表示根本没有车库,因此零是最合适的值。

GarageYrBlt 也有缺失值,我用零来估算,因为不可能猜出缺失的年份!

处理时间变量

数据集中有 5 个时间变量。将它们转换成年龄变量似乎更有意义,因为它们提供了关于特征寿命的更多信息。类似于这样的事实,陈述“X 先生在 66 岁时去世”比陈述“X 先生在 2019 年去世”包含更多的信息。因此引入了三个年龄特征:

combined['Age'] = combined.YrSold - combined.YearBuilt
combined['AgeRemod'] = combined.YrSold - combined.YearRemodAdd
combined['AgeGarage'] = combined.YrSold - combined.GarageYrBlt 

“AgeGarage”列的值似乎很少,都在 2000 左右!这是因为 GarageYrBlt 的缺失值被估算为零,如前一节所述。根据以下代码,所有这些异常值都被替换为低于某个阈值的最大可能值:

max_AgeGarage = np.max(combined.AgeGarage[combined.AgeGarage < 1000])
combined['AgeGarage'] = combined['AgeGarage'].map(lambda x: max_AgeGarage if x > 1000 else x)

负值,如果有的话,应该被替换为零,并且原始时间特征现在可以被丢弃。

将分类列转换为虚拟列

分类特征现在可以用示例代码转换成虚拟列:

Foundation_dummies=pd.get_dummies(combined['Foundation'], prefix='Foundation_', drop_first=True)
combined=pd.concat([combined, Foundation_dummies], axis=1)
combined.drop('Foundation', axis=1, inplace=True)

prefix 参数有助于以后识别列。我们只需要保留 k 列中的 k-1 列,因此 drop_first 被设置为 True。要了解更多关于这些论点的信息,请阅读此处的。

虚拟数据集与原始数据集连接在一起,实际的列被删除。

数据集几乎建立起来了,但还没有。

完整的数据集现在有 209 列!数据太多,机器消化不了。更多的特征会导致更多的噪声,并最终导致过度拟合。

删除不重要的功能

问题是如何决定放弃哪个特性?幸运的是 XGBoost 有一个答案。

组合的数据集被切片以获得训练集,并且 xgboost 对象被拟合在其上。

X = combined[:-1459]
y = targets
y = y.drop(index_drop).reset_index(drop=True)xgb = XGBRegressor()
xgb.fit(X, y)

然后计算特征重要性并排序:

imp = pd.DataFrame(xgb.feature_importances_ ,columns = ['Importance'],index = X.columns)
imp = imp.sort_values(['Importance'], ascending = False)
print(imp)

结果是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有很多功能的重要性为零。为了确定到底应该保留多少特性,我使用了一个名为 RFECV 的函数。

RFECV 告诉我们应该保留多少和哪些特性。它有一个评分参数,我用 RMSE(均方根误差)来表示,因为 Kaggle 练习也将根据相同的条件进行评估。以下函数定义 rmse,并使其成为计分器:

def rmse(y_true, y_pred):
    return np.sqrt(np.mean((y_true-y_pred)**2))rmse = make_scorer(rmse, greater_is_better=False)

因为目标是减少 RMSE,所以“越大越好”的参数被设置为假。最后,RFECV 在 X 和 y 数据集上拟合,给出了最佳特征数 67!

rfecv = RFECV(estimator=xgb, step=1, cv=3, n_jobs=-1, scoring=rmse)
rfecv = rfecv.fit(X, y)
print("Optimal number of features : %d" % rfecv.n_features_)

绘制特征与交叉验证分数的关系为我们提供了下图:

plt.figure()
plt.xlabel("Number of features selected")
plt.ylabel("Cross validation score")
plt.xticks(np.arange(0,200,10))
plt.plot(range(1, len(rfecv.grid_scores_) + 1), rfecv.grid_scores_)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在大约 67 个特征之后,交叉验证分数几乎没有改善。这里的下一步是检查函数建议哪些列。

features_kept = X.columns.values[rfecv.support_] 
X = X[features_kept]

X 的特点是:

LotFrontage
LotArea
Alley
OverallQual
OverallCond
MasVnrArea
ExterQual
ExterCond
BsmtQual
BsmtCond
BsmtExposure
BsmtFinType1
BsmtFinSF1
BsmtFinSF2
BsmtUnfSF
TotalBsmtSF
HeatingQC
CentralAir
1stFlrSF
2ndFlrSF
GrLivArea
BsmtFullBath
FullBath
HalfBath
KitchenAbvGr
KitchenQual
Functional
FireplaceQu
GarageType
GarageFinish
GarageCars
GarageArea
GarageQual
GarageCond
PavedDrive
WoodDeckSF
OpenPorchSF
EnclosedPorch
ScreenPorch
PoolArea 
Fence
Age
AgeRemod
AgeGarage
MSZoning__FV
MSZoning__RL
MSZoning__RM
LotConfig__CulDSac
Street__Pave
Foundation__CBlock
Neighborhood__BrkSide
Neighborhood__ClearCr
Neighborhood__Crawfor
Neighborhood__OldTown
Neighborhood__Sawyer
Neighborhood__StoneBr
Condition1__Norm
Exterior1st__BrkComm
Exterior1st__BrkFace
Exterior1st__HdBoard
Exterior2nd__BrkFace
Exterior2nd__Wd Shng
HouseStyle__SLvl
SaleType__New
SaleCondition__Family
SaleCondition__Normal
MSSubClass__class2

这里有一些功能,根据我们在 EDA 中的分析,这些功能本不应该出现在这里。例如,我们已经推断出公摊面积不是预测销售价格的重要特征。

因此可以删除 PoolArea。

其次,有一些变量是相互关联的。因此,只保留其中一个是有意义的。运行命令时(参考上一篇关于 EDA 的文章):

print(corr['SalePrice'].sort_values(ascending=False))SalePrice        1.000000
OverallQual      0.790982
GrLivArea        0.708624
GarageCars       0.640409
GarageArea       0.623431
TotalBsmtSF      0.613581
1stFlrSF         0.605852
FullBath         0.560664
TotRmsAbvGrd     0.533723
YearBuilt        0.522897
YearRemodAdd     0.507101
GarageYrBlt      0.486362
MasVnrArea       0.477493
Fireplaces       0.466929
BsmtFinSF1       0.386420
LotFrontage      0.351799
WoodDeckSF       0.324413
2ndFlrSF         0.319334
OpenPorchSF      0.315856
HalfBath         0.284108
LotArea          0.263843
BsmtFullBath     0.227122
BsmtUnfSF        0.214479
BedroomAbvGr     0.168213
ScreenPorch      0.111447
PoolArea         0.092404
MoSold           0.046432
3SsnPorch        0.044584
BsmtFinSF2      -0.011378
BsmtHalfBath    -0.016844
MiscVal         -0.021190
Id              -0.021917
LowQualFinSF    -0.025606
YrSold          -0.028923
OverallCond     -0.077856
MSSubClass      -0.084284
EnclosedPorch   -0.128578
KitchenAbvGr    -0.135907

因此,GarageArea,1stFlrSF 可能会被删除,我们减少到 64 个功能。

这里的结论是,一个人不应该盲从任何函数的结果。我们应该每时每刻问自己,这是否有意义。因此,据说通过机器学习实现高精度不仅是一门科学,也是一门艺术。特征工程通常是一个迭代过程,在实施模型时,可能需要进一步修改数据集以实现更高的精度。

我继续使用这个数据集,并用不同的算法进行训练,这将在我的下一篇博客中介绍。

欢迎提出进一步改进功能工程的建议!

面向 NLP 和 Python 的 NLTK 特征工程

原文:https://towardsdatascience.com/feature-engineering-with-nltk-for-nlp-and-python-82f493a937a0?source=collection_archive---------22-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Herman Melville (1860), ‘Journal Up the Straits,’ Library of Congress

上周,我复习了自然语言处理(NLP)的自然语言工具包(NLTK)的一些基本功能。通过将这些基本函数应用到赫尔曼·梅尔维尔的《莫比·迪克》中,我继续了我的 NLP 之旅。文本文档由 Gutenberg 项目提供,该网站上的几本书可以通过 python NLTK 包获得。

我在之前的博客中详细描述了清理过程,我不得不清理两部电视剧的各种脚本。这一过程将根据手头的任务而变化。今天我只是用 NLTK 方法探索数据的文本。一般流程是:

  1. 加载语料库或文档的文本
  2. 对原始文本进行标记
  3. 删除停用词和标点符号
  4. 应用词干或词汇化

古腾堡项目的文本莫比迪克的解释已经相当干净了,也就是说,只有标点符号需要从文本中删除,所有的单词都需要小写。我还删除了序言和序言,因为它不是梅尔维尔的原始故事的一部分。

我在文件中保留的唯一标点符号是撇号。NLTK 允许您定制 regex 模式,将字符视为一个标记。例如,任何带撇号的单词都被视为一个标记, d’ye 是一个标记,而不是两个单独的标记。应用正则表达式模式的代码是:nltk.regexp_tokenize(raw_text, pattern),其中raw_text是代表文档的字符串,pattern是代表您希望应用的正则表达式模式的字符串。

探索文本

一个单词包是一个单词在文本中出现的次数。这个计数可以是文档范围的、语料库范围的或语料库范围的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

A visualization of the text data hierarchy

NLTK 提供了一个简单的方法,可以创建一个单词包,而不必手动编写代码来遍历一个标记列表。首先创建一个FreqDist对象,然后应用令牌列表。最后,您可以使用.most_common()方法查看最常见的令牌。

要查看删除停用词后文本中唯一单词的总数,只需查看频率分布列表的长度:len(moby_dick_freqdist)。每个令牌的计数都是名义上的,如果您只研究一个文档,这是很好的。如果你有几个文档(一个语料库),那么标准化计数对于比较不同长度的文档是必要的。这可以通过对频率分布列表中每个元组的值求和,然后将每个计数除以该总数来实现。

How to make a normalized frequency distribution object with NLTK

二元模型、n 元模型和 PMI 得分

每个标记(在上面的例子中,每个唯一的单词)代表文档中的一个维度。在停用词被移除后和目标变量被添加前,莫比迪克有 16939 个维度。为了减少文档的维数,我们可以将两个或多个单词组合起来,如果它们作为一个标记而不是两个单独的标记来传达大量信息。如果我们选择一对词,这将被称为二元组

让我们检查句子“我爱热狗。”有三对词,(“我”、“爱”)、(“爱”、“热”)、(“热”、“狗”)。前两个单词对是随机的,在一起不表达任何重要的意思。然而, hotdogs 这两个词合在一起传达了一种食物的名称。如果我们将这最后一对视为一个记号,那么句子的维度就减少了一个。这个过程被称为创建二元模型。一个 ngram 与一个二元模型不同,因为一个 ngram 可以将 n 个单词或字符作为一个令牌来处理。

NLTK 提供了一个二元模型方法。导入 NLTK 后,您可以将 bigram 对象nltk.collocations.BigramAssocMeasures()存储为一个变量。然后,您可以利用 NLTK 的 collector 和 scorer 方法来查看相关联的二元模型及其规范化的频率分数。

How to make bigrams with NLTK

score_ngrams()方法返回二元模型列表及其相关的归一化频率分布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The top five bigrams for Moby Dick

不是所有单词对都能传达大量信息。NLTK 提供了逐点交互信息 (PMI)计分器对象,该对象分配一个统计度量来比较每个二元模型。该方法还允许您筛选出出现次数少于最小次数的令牌对。在调用 bigram 方法后,您可以应用一个频率过滤器.apply_freq_filter(5),其中 5 是 bigram 必须出现的最小次数。最后,使用 PMI 参数score_ngrams(bigram_measures.pmi)调用 scorer 方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The top five bigrams by PMI score for Moby Dick

结论

NLTK 有许多强大的方法,允许我们用几行代码评估文本数据。二元模型、n 元模型和 PMI 分数允许我们降低语料库的维度,这在我们继续进行更复杂的任务时节省了我们的计算能量。一旦文档被清理,就可以很容易地应用 NLTK 方法。

特征工程与急流:NFL 数据碗

原文:https://towardsdatascience.com/feature-engineering-with-rapids-nfl-data-bowl-c8584bc266b5?source=collection_archive---------31-----------------------

在开始这篇文章之前,我想减轻你对从 pandas 转换到 RAPIDS cudf 的怀疑, RAPIDS cudf 使用与 pandas 相同的 API!

RAPIDS 正在将表格数据集上的传统数据科学工作流迁移到 GPU 上。最近, George Sief 发表了一篇关于走向数据科学的文章,表明 RAPIDS cudf 库可以在 5.12 毫秒内计算包含 1 亿行的给定列的平均值,而在 pandas 上则需要 82.2 毫秒。本文将在 Kaggle NFL 数据碗挑战赛的特性工程背景下,进一步探讨 RAPIDS 和 cudf 实现的加速。我们在使用来自 Digital Storm 的 2 个 NVIDIA Titan RTX 24GB GPU 的 Data Science PC 上,几乎每个设计的功能都实现了至少 10 倍的速度提升。

在本文中,我们将从 NFL 数据碗数据集构建新的要素,如身体质量指数、防守方向与距离、余量和紧急程度。我们还将通过将特性列中的每个值除以特性的最大值来标准化特性。在每种情况下,我们都将对 pandas 和 RAPIDS cudf 库进行基准测试!

你可以从本文中的代码访问 Jupyter 笔记本,这里我也制作了一个视频来解释这个如果你想继续下去的话!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

首先,我们将 NFL 数据碗数据集的大小扩大 16 倍,以展示 RAPIDS / cudf 在大型数据集上的强大功能。之所以选择 NFL 数据集,是因为它便于理解特征工程的概念。RAPIDS 确实最适合拥有数百万条记录的大型数据集。

# Increase the dataset size by stacking it on top of itself
pd_data = pd.concat([pd_data, pd_data], ignore_index=True)
# repeated 3 more times

现在我们将把熊猫数据帧转换成 cudf 和 dask-cudf 数据帧!

import cudf
import dask_cudf
cudf_data = cudf.from_pandas(pd_data)
dask_data = dask_cudf.read_csv('./cleaned_data.csv')

上面的代码突出了 RAPIDS 最好的部分之一。它完全模仿 pandas API,因此数据科学家不必担心迁移到新语法会带来的头痛。在这段代码中,我们用。from_pandas()语法和 dask 文档建议您直接从 csv 文件加载数据,所以我们这样做。

列值平均值

根据最近从 George Sief 开始的关于数据科学的教程,我们观察了计算数据帧中给定列的平均值所花费的时间:

pd_data['PlayerHeight'].mean() # 29.1 ms
cudf_data['PlayerHeight'].mean() # 466 µs
dask_data['PlayerHeight'].mean() # 1.46 ms

在这种情况下,我们看到 cudf 比 pandas 快了大约 60 倍。Cudf 在这里的表现优于 dask-cudf,但我怀疑这是因为 dask-cudf 对数据集进行了更好的优化,这种数据集一开始就不可能与 pandas 进行比较。

防守者进入禁区 vs 距离

特色创意致谢:https://www . ka ggle . com/CP mpml/initial-wrangling-Voronoi-areas-in-python

这个功能将会查看与防守方向相比第一次进攻所需的码数。

pd_data['DefendersInTheBox_vs_Distance'] = pd_data['DefendersInTheBox'] / pd_data['Distance'] # 36.9 mscudf_data['DefendersInTheBox_vs_Distance'] = cudf_data['DefendersInTheBox'] / cudf_data['Distance'] # 3.1 ms

身体质量指数

特色创意致谢:https://www . ka ggle . com/bgmello/neural-networks-feature-engineering-for-the-win

该功能将根据身高和体重计算跑步者的身体质量指数。这是我们获得最低加速的特性。

pd_data['BMI'] = 703 * (pd_data['PlayerWeight']/pd_data['PlayerHeight']**2) # 64.3 mscudf_data['BMI'] = 703 * (cudf_data['PlayerWeight'] / cudf_data['PlayerHeight']**2) # 15.7 ms

边缘

这个功能将计算得分差异,也许如果球队赢/输了一些金额,他们更有可能成功运行球。

pd_data['Margin'] = pd_data['HomeScoreBeforePlay'] - pd_data['VisitorScoreBeforePlay'] # 32 mscudf_data['Margin'] = cudf_data['HomeScoreBeforePlay'] - cudf_data['HomeScoreBeforePlay'] # 3.46 ms

紧急

此功能将计算季度利润给出的紧急程度。也许如果现在是第四节,比赛接近尾声,球员会把球跑得更远。

pd_data['Urgency'] = pd_data['Quarter'] * pd_data['Margin'] # 33.6ms
cudf_data['Urgency'] = pd_data['Quarter'] * pd_data['Margin'] #3.4ms

最大值标准化

一种惰性规范化技术是将一列中的所有值除以该列中的最大值。这样做是为了在[0,1]之间缩放值,并便于训练机器学习模型。

for col in pd_data:
  pd_data[col] /= pd_data[col].max() # 1.28 sfor col in cudf_data:
  cudf_data[col] /= cudf_data[col].max() # 117 ms

急流城特色工程:总结性思考

本笔记重点介绍了一些例子,在这些例子中,RAPIDS 实现了 ETL 操作的大规模加速,比如特性工程和标准化。这对于电子健康记录、保险数据、生物信息学和其他各种最好用表格数据格式描述的数据领域的应用程序来说是非常令人兴奋的!感谢阅读,请查看下面解释这一点的视频:

https://www.youtube.com/watch?v=A9lgUwA8RrY&t = 13s

特征提取技术

原文:https://towardsdatascience.com/feature-extraction-techniques-d619b56e31be?source=collection_archive---------0-----------------------

关于如何使用特征提取技术(如 PCA、ICA、LDA、LLE、t-SNE 和 AE)降低数据集维数的端到端指南。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(Source: https://blog.datasciencedojo.com/curse-of-dimensionality-python/)

介绍

如今,处理包含数百(甚至数千)个要素的数据集变得非常普遍。如果特征的数量变得相似(甚至更大!)多于数据集中存储的观察值的数量,那么这很可能导致机器学习模型遭受过度拟合。为了避免这种类型的问题,有必要应用正则化或降维技术(特征提取)。在机器学习中,数据集的维数等于用来表示它的变量的数量。

使用正则化当然有助于降低过拟合的风险,但是使用特征提取技术也可以带来其他类型的优势,例如:

  • 精度提高。
  • 过度拟合风险降低。
  • 在训练中加速。
  • 改进的数据可视化。
  • 增加我们模型的可解释性。

要素提取旨在通过从现有要素创建新要素(然后丢弃原始要素)来减少数据集中的要素数量。这些新的精简特征集应该能够概括原始特征集中包含的大部分信息。这样,可以从原始集合的组合中创建原始特征的概括版本。

另一种减少数据集中要素数量的常用技术是要素选择。要素选择和要素提取的区别在于,要素选择旨在对数据集中现有要素的重要性进行排序,并丢弃不太重要的要素(不创建新要素)。如果你有兴趣了解更多关于特性选择的信息,你可以在我之前的文章中找到更多关于特性选择的信息。

在本文中,我将以 Kaggle 蘑菇分类数据集为例,向您介绍如何应用特征提取技术。我们的目标是试图通过观察给定的特征来预测蘑菇是否有毒。这篇文章中使用的所有代码(以及更多!)可在 Kaggle我的 GitHub 账户上获得。

首先,我们需要导入所有必需的库。

下图显示了我们将在本例中使用的数据集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 1: Mushroom Classification dataset

在将这些数据输入到我们的机器学习模型之前,我决定将我们的数据分为特征(【X】)和标签(【Y),并对所有分类变量进行热编码。

随后,我决定创建一个函数( forest_test )将输入数据分成训练集和测试集,然后训练和测试一个随机的森林分类器。

我们现在可以使用整个数据集来使用该函数,然后在使用简化版本而不是整个数据集时,连续使用该函数来比较这些结果。

如下所示,使用所有特征训练随机森林分类器,在大约 2.2 秒的训练时间内达到 100%的准确度。在下面的每个例子中,每个模型的训练时间将在每个片段的第一行打印出来,供您参考。

2.2676709799999992
[[1274    0]
 [   0 1164]]
              precision    recall  f1-score   support

           0       1.00      1.00      1.00      1274
           1       1.00      1.00      1.00      1164

    accuracy                           1.00      2438
   macro avg       1.00      1.00      1.00      2438
weighted avg       1.00      1.00      1.00      2438

特征抽出

主成分分析

主成分分析是最常用的线性降维技术之一。当使用主成分分析时,我们将原始数据作为输入,并试图找到能够最好地概括原始数据分布的输入特征的组合,从而降低其原始维数。PCA 能够通过查看成对距离最大化方差和最小化重建误差来做到这一点。在 PCA 中,我们的原始数据被投影到一组正交轴上,每个轴按照重要性排序。

PCA 是一种无监督学习算法,因此它不关心数据标签,而只关心变化。这在某些情况下会导致数据的错误分类。

在本例中,我将首先在整个数据集中执行 PCA,将数据缩减为二维,然后用新要素及其各自的标注构建一个数据框。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2: PCA Dataset

使用我们新创建的数据框,我们现在可以在 2D 散点图中绘制我们的数据分布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 3: 2D PCA Visualization

我们现在可以重复同样的过程,保留 3 个维度,使用 Plotly 创建动画(请随意与下面的动画互动!).

在使用 PCA 时,我们还可以使用***explained _ variance _ ratio _***sci kit-learn 函数来探究原始数据方差保留了多少。一旦计算出方差比,我们就可以继续创建有趣的可视化图形。

使用由 PCA 构建的 3 个特征的集合(而不是整个数据集)再次运行随机森林分类器导致 98%的分类准确度,而仅使用 2 个特征 95%的准确度。

[10.31484926  9.42671062  8.35720548]
2.769664902999999
[[1261   13]
 [  41 1123]]
              precision    recall  f1-score   support

           0       0.97      0.99      0.98      1274
           1       0.99      0.96      0.98      1164

    accuracy                           0.98      2438
   macro avg       0.98      0.98      0.98      2438
weighted avg       0.98      0.98      0.98      2438

此外,使用我们的二维数据集,我们现在还可以可视化随机森林使用的决策边界,以便对每个不同的数据点进行分类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 4: PCA Random Forest Decision Boundary

独立成分分析

ICA 是一种线性降维方法,它将独立分量的混合作为输入数据,其目的是正确识别它们中的每一个(删除所有不必要的噪声)。如果两个输入特征的线性和非线性相关性都等于零[1],则可以认为这两个输入特征是独立的。

独立分量分析通常用于医疗应用,如 EEG 和 fMRI 分析,以将有用的信号与无用的信号分开。

作为 ICA 应用程序的一个简单示例,让我们考虑一个音频注册,其中有两个不同的人在说话。例如,使用 ICA,我们可以尝试识别配准中的两个不同的独立成分(两个不同的人)。通过这种方式,我们可以让我们的无监督学习算法识别对话中不同的说话者。

使用 ICA,我们现在可以再次将数据集减少到只有三个特征,使用随机森林分类器测试其准确性,并绘制结果。

2.8933812039999793
[[1263   11]
 [  44 1120]]
              precision    recall  f1-score   support

           0       0.97      0.99      0.98      1274
           1       0.99      0.96      0.98      1164

    accuracy                           0.98      2438
   macro avg       0.98      0.98      0.98      2438
weighted avg       0.98      0.98      0.98      2438

从下面的动画中我们可以看到,尽管 PCA 和 ICA 导致了相同的精度结果,但它们构建了两种不同的三维空间分布。

线性判别分析(LDA)

LDA 是监督学习降维技术和机器学习分类器。

LDA 的目标是最大化每个类的均值之间的距离,并最小化类本身内的扩散。因此,LDA 使用类内和类间作为度量。这是一个很好的选择,因为在低维空间中投影数据时,最大化每个类的均值之间的距离可以获得更好的分类结果(由于不同类之间的重叠减少)。

使用 LDA 时,假设输入数据遵循高斯分布(如本例所示),因此将 LDA 应用于非高斯数据可能会导致较差的分类结果。

在本例中,我们将运行 LDA 将数据集缩减为一个要素,测试其准确性并绘制结果。

Original number of features: 117
Reduced number of features: 1

因为我们的数据分布紧密遵循高斯分布,所以 LDA 表现得非常好,在这种情况下,使用随机森林分类器实现了 100%的准确性。

1.2756952610000099
[[1274    0]
 [   0 1164]]
              precision    recall  f1-score   support

           0       1.00      1.00      1.00      1274
           1       1.00      1.00      1.00      1164

    accuracy                           1.00      2438
   macro avg       1.00      1.00      1.00      2438
weighted avg       1.00      1.00      1.00      2438

正如我在本节开始时提到的,LDA 也可以用作分类器。因此,我们现在可以测试 LDA 分类器在这种情况下的表现。

0.008464782999993758
[[1274    0]
 [   2 1162]]
              precision    recall  f1-score   support

           0       1.00      1.00      1.00      1274
           1       1.00      1.00      1.00      1164

    accuracy                           1.00      2438
   macro avg       1.00      1.00      1.00      2438
weighted avg       1.00      1.00      1.00      2438

最后,我们现在可以可视化我们的两类分布看起来像创建我们的一维数据的分布图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 5: LDA Classes Separation

局部线性嵌入(LLE)

到目前为止,我们已经考虑了诸如 PCA 和 LDA 的方法,它们能够在不同特征之间的线性关系的情况下执行得非常好,我们现在将继续考虑如何处理非线性情况。

局部线性嵌入是一种基于流形学习的降维技术。流形是嵌入在更高维度空间中的 D 维对象。流形学习的目标是让这个对象可以在它原来的三维空间中表现,而不是在一个不必要的更大的空间中表现。

用于解释机器学习中流形学习的一个典型例子是瑞士滚动流形(图 6)。给我们一些数据作为输入,这些数据的分布类似于一个卷(在三维空间中),然后我们可以展开它,以便将我们的数据减少到二维空间中。

流形学习算法的一些例子是:Isomap、局部线性嵌入、修改的局部线性嵌入、Hessian 特征映射等

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 6: Manifold Learning [2]

现在,我将向您介绍如何在我们的示例中实现 LLE。根据 Scikit-learn 文档[3]:

局部线性嵌入(LLE)寻求数据的低维投影,其保持局部邻域内的距离。它可以被认为是一系列局部主成分分析,它们被全局比较以找到最佳非线性嵌入。

现在,我们可以在数据集上运行 LLE,将数据维数降低到 3 维,测试整体准确性并绘制结果。

2.578125
[[1273    0]
 [1143   22]]
              precision    recall  f1-score   support

           0       0.53      1.00      0.69      1273
           1       1.00      0.02      0.04      1165

   micro avg       0.53      0.53      0.53      2438
   macro avg       0.76      0.51      0.36      2438
weighted avg       0.75      0.53      0.38      2438

t 分布随机邻居嵌入(t-SNE)

t-SNE 是一种非线性降维技术,通常用于可视化高维数据集。t-SNE 的一些主要应用是自然语言处理(NLP),语音处理等

t-SNE 通过最小化由原始高维空间中的输入特征的成对概率相似性构成的分布与其在缩减的低维空间中的等价物之间的差异来工作。然后,t-SNE 利用 Kullback-Leiber (KL) 散度来衡量两种不同分布的差异。然后使用梯度下降最小化 KL 散度。

当使用 t-SNE 时,高维空间使用高斯分布建模,而低维空间使用学生的 t-分布建模。这样做是为了避免由于转换到低维空间而导致的相邻点距离分布的不平衡。

我们现在准备使用 TSNE,将数据集减少到只有 3 个要素。

[t-SNE] Computing 121 nearest neighbors...
[t-SNE] Indexed 8124 samples in 0.139s...
[t-SNE] Computed neighbors for 8124 samples in 11.891s...
[t-SNE] Computed conditional probabilities for sample 1000 / 8124
[t-SNE] Computed conditional probabilities for sample 2000 / 8124
[t-SNE] Computed conditional probabilities for sample 3000 / 8124
[t-SNE] Computed conditional probabilities for sample 4000 / 8124
[t-SNE] Computed conditional probabilities for sample 5000 / 8124
[t-SNE] Computed conditional probabilities for sample 6000 / 8124
[t-SNE] Computed conditional probabilities for sample 7000 / 8124
[t-SNE] Computed conditional probabilities for sample 8000 / 8124
[t-SNE] Computed conditional probabilities for sample 8124 / 8124
[t-SNE] Mean sigma: 2.658530
[t-SNE] KL divergence after 250 iterations with early exaggeration: 65.601128
[t-SNE] KL divergence after 300 iterations: 1.909915
143.984375

可视化结果要素的分布,我们可以清楚地看到我们的数据是如何很好地分离的,即使是在缩小的空间中进行转换。

使用 t-SNE 缩减子集测试我们的随机森林准确性证实,现在我们的类可以很容易地分开。

2.6462027340000134
[[1274    0]
 [   0 1164]]
              precision    recall  f1-score   support

           0       1.00      1.00      1.00      1274
           1       1.00      1.00      1.00      1164

    accuracy                           1.00      2438
   macro avg       1.00      1.00      1.00      2438
weighted avg       1.00      1.00      1.00      2438

自动编码器

自动编码器是一系列机器学习算法,可用作降维技术。自动编码器和其他降维技术的主要区别在于,自动编码器使用非线性变换将数据从高维投影到低维。

存在不同类型的自动编码器,例如:

  • 去噪自动编码器
  • 变型自动编码器
  • 卷积自动编码器
  • 稀疏自动编码器

在这个例子中,我们将从构建一个基本的自动编码器开始(图 7)。自动编码器的基本架构可以分为两个主要部分:

  1. 编码器:获取输入数据并进行压缩,以去除所有可能的噪音和无用信息。编码器级的输出通常被称为瓶颈或潜在空间。
  2. 解码器:将编码的潜在空间作为输入,并尝试仅使用其压缩形式(编码的潜在空间)再现原始自动编码器输入。

如果所有的输入特征都是相互独立的,那么自动编码器会发现将输入数据编码和解码到低维空间特别困难。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 7: Autoencoder Architecture [4]

可以使用 Keras API 在 Python 中实现自动编码器。在这种情况下,我们在编码层中指定我们希望将输入数据减少到的特征数量(在本例中为 3)。从下面的代码片段中我们可以看到,自动编码器将 X(我们的输入特征)作为我们的特征和标签(X,Y)。

对于这个例子,我决定使用 ReLu 作为编码阶段的激活函数,使用 Softmax 作为解码阶段的激活函数。如果我没有使用非线性激活函数,那么 Autoencoder 会尝试使用线性变换来减少输入数据(因此给我们一个类似于使用 PCA 的结果)。

我们现在可以重复与前面的例子类似的工作流程,这次使用一个简单的自动编码器作为我们的特征提取技术。

1.734375
[[1238   36]
 [  67 1097]]
              precision    recall  f1-score   support

           0       0.95      0.97      0.96      1274
           1       0.97      0.94      0.96      1164

   micro avg       0.96      0.96      0.96      2438
   macro avg       0.96      0.96      0.96      2438
weighted avg       0.96      0.96      0.96      2438

希望您喜欢这篇文章,感谢您的阅读!

联系人

如果你想了解我最新的文章和项目,请在媒体上关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:

文献学

[1]用独立分量分析(ICA)深入探讨降维,Paperspace。访问网址:https://blog . paper space . com/dimension-reduction-with-independent-components-analysis/

[2]迭代非线性流形造型降维,ResearchGate。访问:https://www . research gate . net/publication/220270207 _ Iterative _ Non-linear _ Dimensionality _ Reduction _ with _ Manifold _ sculpture

[3]流形学习,Scikit-learn 文档。访问:https://sci kit-learn . org/stable/modules/Manifold . html # target text = Manifold % 20 learning % 20 is % 20 an % 20 approach,sets % 20 is % 20 only % 20 artificial % 20 high。

[4]可变自动编码器是美丽的。访问地点:http://www.compthree.com/blog/autoencoder/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值