初学者使用Pandas的特征工程

2021-04-21 10:29:14 浏览数 (1)

磐创AI分享

作者 | PROCRASTINATOR

编译 | Flin

来源 | analyticsvidhya

概述

  • 特征工程是数据科学生命周期中最关键的步骤之一。
  • 我们将讨论pandas如何仅凭一个线性函数使执行特征工程变得更加容易。

介绍

Pandas是用于Python编程语言的开源高级数据分析和处理库。使用pandas,可以轻松加载,准备,操作和分析数据。它是用于数据分析操作的最优选和广泛使用的库之一。

pandas具有简单的语法和快速的操作。它可以轻松处理多达1万条数据。使用pandas Dataframe,可以轻松添加/删除列,切片,建立索引以及处理空值。

现在,我们已经了解了pandas的基本功能,我们将专注于专门用于特征工程的pandas。

![](http://qiniu.aihubs.net/47522Feature Engineering with Pandas.png)

顾名思义,特征工程是一种根据现有数据创建新特征的技术,可以帮助你深入了解数据。建议全面执行EDA的主要原因之一是,我们可以对数据和创建新特征的范围有适当的了解。

特征工程主要有两个原因:

  • 根据机器学习算法的要求准备和处理可用数据。大多数机器学习算法与分类数据不兼容。因此,我们需要将该列转换为数字,以便所有有效信息都可以输入到算法中。
  • 改善机器学习模型的性能。每个预测模型的最终目标都是获得最佳性能。改善性能的一些方法是使用正确的算法并正确调整参数。但是就我个人而言,我认为创建新特性对改善性能有最大的帮助,因为我们试图为算法提供新信号,而这是之前所没有的。

注意:在本文中,我们将仅了解每种工程方法和功能背后的基本原理。提到的功能范围不仅限于执行这些任务,还可以用于其他数据分析和预处理技术。

目录

  • 了解数据
  • 用于标签编码的replace()
  • 用于热编码的get_dummies()
  • 用于分箱的cut() 和qcut()
  • 用于文本提取的apply()
  • 用于频率编码的value_counts() 和apply()
  • 用于聚合功能的 groupby() 和transform()
  • 用于基于日期和时间特征的Series.dt()
了解数据

为了更好地理解该概念,我们将处理Big Mart销售预测数据。

问题是:在给定某些变量的情况下,要预测在不同城市的不同商店中存在的产品的销售情况。问题中包含的数据大多与商店和产品有关。

Big Mart销售预测:https://datahack.analyticsvidhya.com/contest/practice-problem-big-mart-sales-iii

让我们导入数据和库,并检查前几行以更好地理解它。

https://gist.github.com/Kamaldeep0077/05a6de2cf5fcf61208b666410fa37bcf

数据具有8,523行和12列。目标变量是Item_Outlet_Sales。

注意:变量中有一些缺失值,例如Item_weightOutlet_Size。估算这些缺失的值超出了我们的讨论范围,我们将只关注使用pandas函数来设计一些新特性。

用于标签编码的replace()

pandas中的replace函数动态地将当前值替换为给定值。新值可以作为列表,字典,series,str,float和int传递。

注意:应该始终对有序数据执行标签编码,以保持算法的模式在建模阶段学习。

使用replace() 进行标签编码的优点是我们可以手动指定类别中每个组的排名/顺序。

在这里,我们将对具有三个唯一组的Outlet_Loaction_Tier进行标签编码。

代码语言:javascript复制
data['Outlet_Location_Type_Encoded']  = data['Outlet_Location_Type'] 
                                            .replace({'Tier 1': 1, 'Tier 2': 2, 'Tier 3': 3})

data[['Outlet_Location_Type', 'Outlet_Location_Type_Encoded']].head()

在这里,我们以正确的顺序成功地将该列转换为标签编码的列。

用于独热编码的get_dummies()

获取虚拟变量是pandas中的一项功能,可帮助将分类变量转换为独热变量。

独热编码方法是将类别自变量转换为多个二进制列,其中1表示属于该类别的观察结果。

独热编码被明确地用于没有自然顺序的类别变量。示例:Item_Type。如果对此类类别变量执行标签编码,我们就给出了奶制品高于软饮料的模型信号。

代码语言:javascript复制
Outlet_Type_Dumm = pd.get_dummies(data=data['Outlet_Type'], columns=['Outlet_Type'], drop_first=True)

pd.concat([data['Outlet_Type'], Outlet_Type_Dumm], axis=1).head()

注意:在代码中,我使用了参数drop_first,它删除了第一个二进制列(在我们的示例中为Grocery Store),以避免完全多重共线性。

在此,每个新的二进制列的值1表示该子类别在原始Outlet_Type列中的存在。

用于分箱的cut() 和qcut()

分箱是一种将连续变量的值组合到n个箱中的技术。合并也可以称为离散化技术,因为我们将连续变量划分为离散变量。

对于某些机器学习算法,有时使用离散变量而不是连续变量会更好。例如:如果将年龄等连续变量转换成年龄段,则可以更好地使用它,并且可以更好地解释该变量。合并连续变量也有助于消除异常值的影响。

pandas具有两个对变量进行分箱的功能,即cut() 和qcut() 。

qcut() : qcut是基于分位数的离散化函数,它试图将bins分成相同的频率组。如果尝试将连续变量划分为五个箱,则每个箱中的观测数量将大致相等。

让我们尝试使用qcut函数对大型超市的Item_MRP变量进行装箱:

代码语言:javascript复制
#name of groups
groups = ['Low', 'Med', 'High', 'Exp']

data['Item_MRP_Bin_qcut'] = pd.qcut(data['Item_MRP'], q=4, labels=groups)
data[['Item_MRP', 'Item_MRP_Bin_qcut']].head()

当我们检查这个新变量的频率时:

代码语言:javascript复制
# Count of each category 
pd.DataFrame(data['Item_MRP_Bin_qcut'].value_counts())

正如预期的那样,该列的每个子类别的观察分布大致相等。

cut() : cut函数还用于离散化连续变量。使用qcut函数,我们的目的是使每个bin中的观察数保持相等,并且我们没有指定要进行拆分的位置,最好仅指定所需的bin数

在case cut函数中,我们显式提供bin边缘。不能保证每个bin中观测值的分布都是相等的。

如果我们要对像年龄这样的连续变量进行分类,那么根据频率对它进行分类将不是一个合适的方法。

相反,我们想具体地划分儿童年龄,例如从0-14岁到青少年,从15-24岁到60岁以上。在这种情况下,使用cut函数比使用qcut函数更有意义。

让我们尝试使用cut函数对大型超市的Item_MRP变量进行装箱:

代码语言:javascript复制
#define bins 
bins = [0, 70, 140, 210, 280]

#name of groups
groups = ['Low', 'Med', 'High', 'Exp']

data['Item_MRP_Bin_cut'] = pd.cut(data['Item_MRP'], bins=bins, labels=groups)

data[['Item_MRP', 'Item_MRP_Bin_cut']].head()

当我们检查这个新变量的频率时:

代码语言:javascript复制
# Count of each category 
pd.DataFrame(data['Item_MRP_Bin_cut'].value_counts())

在这里,我们明确提供了这些箱,并且我们可以清楚地看到每个箱中都有不同数量的观察值。

用于文本提取的apply()

pandas的apply() 函数允许在pandas系列上传递函数并将其传递到变量的每个点。

它接受一个函数作为参数,然后将其应用于数据框的行或列。

我们可以将任何函数传递给apply函数的参数,但是我主要使用lambda函数, 这有助于我在单个语句中编写循环和条件。

使用apply和lambda函数,我们可以从列中存在的唯一文本中提取重复凭证。

例如,我们可以从给定的个人名称中提取标题,或者从Html链接中提取网站名称。这些类型的信号有助于在模型构建阶段改善模型性能。

在我们的大卖场销售数据中,我们有一个Item_Identifier列,它是每个产品的唯一产品ID。此变量的前两个字母具有三种不同的类型,即DR,FD和NC,分别代表饮料,食品和非消耗品。我们可以提取这些字母并将它们用作Item_Code的新变量。

代码语言:javascript复制
data['Item_Code'] = data['Item_Identifier'].apply(lambda x: x[0:2])
data[['Item_Identifier', 'Item_Code']].head()

我们已经成功地使用了lambda函数apply创建了一个新的分类变量。

用于频率编码的value_counts() 和apply()

如果名义分类变量中包含许多类别,则不建议使用独热编码。我们不喜欢独热编码的主要原因有两个。

首先,它不必要地增加了尺寸,并且随着尺寸的增加,计算时间也会增加。另一个原因是独热编码二进制变量的稀疏性增加。变量的最大值为0,这会影响模型的性能。

这就是为什么如果我们有一个带有很多类别的名义类别变量,那么我们更喜欢使用频率编码。

频率编码是一种编码技术,用于将分类特征值编码到相应频率的编码技术。这将保留有关分布值的信息。我们将频率归一化,从而得到唯一值的和为1。

在这里,在Big Mart Sales数据中,我们将对Item_Type变量使用频率编码,该变量具有16个唯一的类别。

代码语言:javascript复制
# Frequency encoding using value_counts function 
Item_Type_freq = data['Item_Type'].value_counts(normalize=True)

# Mapping the encoded values with original data 
data['Item_Type_freq'] = data['Item_Type'].apply(lambda x : Item_Type_freq[x])

print('The sum of Item_Type_freq variable:', sum(Item_Type_freq))
data[['Item_Type', 'Item_Type_freq']].head(6)
用于聚合功能的 groupby() 和transform()

Groupby是我的首选功能,可以在数据分析,转换和预处理过程中执行不同的任务。Groupby是一个函数,可以将数据拆分为各种形式,以获取表面上不可用的信息。

GroupBy允许我们根据不同的功能对数据进行分组,从而获得有关你数据的更准确的信息。

关于groupby函数的最有用的事情是,我们可以将其与其他函数(例如Apply,Agg,Transform和Filter)结合使用,以执行从数据分析到特征工程的任务。

为了达到我们的目的,我们将使用具有转换功能的groupby来创建新的聚合功能。

在这里,我们将对变量Item_Identifier和Item_Type进行分组,以查看Item_Outlet_Sales均值。

注意:我们可以对任何类别变量执行groupby函数,并执行任何聚合函数,例如mean, median, mode, count等。

代码语言:javascript复制
data['Item_Outlet_Sales_Mean'] = data.groupby(['Item_Identifier', 'Item_Type'])['Item_Outlet_Sales']
                                     .transform(lambda x: x.mean())

data[['Item_Identifier','Item_Type','Item_Outlet_Sales','Item_Outlet_Sales_Mean']].tail()

从第一行,我们可以理解,如果Item_Identifier为FD22,Item_Type为Snack Foods,则平均销售额将为3232.54。

这就是我们如何创建多个列的方式。在执行这种类型的特征工程时要小心,因为在使用目标变量创建新特征时,模型可能会出现偏差。

用于基于日期和时间特征的Series.dt()

日期和时间特征是数据科学家的金矿。

我们仅通过一个日期-时间变量就能检索到的信息量起初是令人惊讶的,但一旦掌握了它,下次我们在数据集中看到一个日期-时间变量时,你就会立即着手处理它。

12-07-2020 01:00:45,看看这个日期,想想这个特定日期的所有可能组成部分。乍一看,我们可以知道我们有一天,月份,年份,小时,分钟和秒。

但是,如果你强调日期,则会发现你还可以计算一周中的某天,一年中的某个季度,一年中的某周,一年中的某天等等。我们可以通过这一日期时间变量创建的新变量的数量没有限制。

但是,并非每个变量都对模型有用,使用所有变量都意味着增加尺寸,甚至向模型馈入噪声。因此,仅提取与数据问题相关的那些变量至关重要。

现在我们有了可以提取哪些变量的想法,剩下唯一的事情就是提取这些特征。为了简化此过程,pandas提供了dt函数,我们可以使用该函数提取上面命名的所有特征以及更多特征。我强烈建议阅读pd.Series.dt文档,以了解每个功能的作用。

注意:到目前为止,我们正在处理的数据集没有任何日期时间变量。在这里,我们使用 NYC Taxi Trip Duration 数据来演示如何通过日期时间变量提取特征。

NYC Taxi Trip Duration:https://www.kaggle.com/c/nyc-taxi-trip-duration/data

在这里,浏览一下数据集:

我们将使用pickup_datetime通过pandas提取特征。

代码语言:javascript复制
data['pickup_year'] = data['pickup_datetime'].dt.year
data['pickup_dayofyear']  = data['pickup_datetime'].dt.day
data['pickup_monthofyear'] = data['pickup_datetime'].dt.month
data['pickup_hourofday'] = data['pickup_datetime'].dt.hour
data['pickup_dayofweek'] = data['pickup_datetime'].dt.dayofweek
data['pickup_weekofyear'] = data['pickup_datetime'].dt.weekofyear

仅通过单个日期时间变量,我们就可以创建六个新变量,这些变量在模型构建时肯定会非常有用,这并不奇怪。

注意:我们可以使用pandas dt函数创建新功能的方式有50多种。它取决于问题陈述和日期时间变量(每天,每周或每月的数据)的频率来决定要创建的新变量。

尾注

那就是pandas的力量;仅用几行代码,我们就创建了不同类型的新变量,可以将模型的性能提升到另一个层次。

没有传统的方式或类型可以创建新特征,但是pandas具有多种函数,可以使你的工作更加舒适。

我强烈建议你选择任何数据集,并自行尝试所有列出的技术,并在下面评论多少以及哪种方法对你的帮助最大。继续进行讨论将很有趣。

0 人点赞