专栏 | 基于 Jupyter 的特征工程手册:数据预处理(四)

基于 Jupyter 的特征工程手册:数据预处理的上一篇:

专栏 | 基于 Jupyter 的特征工程手册:数据预处理(一)

专栏 | 基于 Jupyter 的特征工程手册:数据预处理(二)

专栏 | 基于 Jupyter 的特征工程手册:数据预处理(三)

项目地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook

本项目将探讨数据预处理部分:介绍了如何利用 scikit-learn 处理静态的连续变量,利用 Category Encoders 处理静态的类别变量以及利用 Featuretools 处理常见的时间序列变量。

目录

特征工程的数据预处理我们将分为三大部分来介绍:

  • 静态连续变量
  • 静态类别变量
  • 时间序列变量

本文将介绍 1.3 时间序列变量的数据预处理。下面将结合 Jupyter,使用 sklearn,进行详解。

1.3 Time Series Variables 时间序列变量

有时我们的数据集或者所研究的问题可能与时间有关。在这种情况下,我们可以利用python中的Featuretools包来实现自动化特征工程。具体来说,我们可以跨时间“积累”信息。

# 合成一些样本数据,记录用户在每一次登陆网站后的行为,比如用户的停留时间,购买的产品等
import numpy as np
import pandas as pd
import featuretools as ft
import datetime

raw_dataset = np.array([['001',100, 'Apple', '2020/01/01', 'male', 35, 1],
                        ['002',20, np.nan, '2020/01/01', 'female', 30, 0],
                        ['003',50, 'Orange','2020/01/01', 'male', 20, 1],
                        ['001', 13, np.nan, '2020/01/03', 'male', 35, 0],
                        ['002', 30, 'Apple', '2020/01/03','female', 30, 1], 
                        ['001', 90, 'Orange', '2020/01/06', 'male', 35, 1], 
                        ['003', 5, 'Orange', '2020/01/07', 'male', 20, 1]])

dataset = pd.DataFrame(raw_dataset, columns = ['Customer ID', 'Seconds Stay', 'Item Purchase',
                                               'Time', 'Sex', 'Age', 'Target'])
dataset['Seconds Stay'] = dataset['Seconds Stay'].astype(int)
dataset['Target'] = dataset['Target'].astype(int)
dataset['Age'] = dataset['Age'].astype(int)
dataset['Time'] = pd.to_datetime(dataset['Time'])
dataset['Item Purchase'] = dataset['Item Purchase'].replace("nan", np.nan)

dataset # 在Item Purchase中,NaN即该客户没有购买

1.3.1 Time Series Categorical Features 时间序列类别变量

一个可能的数据科学问题是:在上述的合成数据中,我们如何预测客户的购买行为。具体而言,我们可能想要预测客户002在2020-01-08登陆的时候是否会购买。

上述数据中的类别变量有Item Purchase。基于这个类别变量我们可以进行特征工程并合成以下新变量:每一次登陆前他有过几次购买行为,每一次登陆前他最喜欢的产品是什么,每一次登陆前他购买过多少独特的商品等等。

而这一些变量,都可以通过Featuretools包简单实现。

Featuretools包提供了以下一些有用的特征变换:

  • COUNT:在给定时间之前的变量计数值,不包括缺失值
  • Mode: 在给定时间之前的变量众数
  • NumUnique:在给定时间之前的唯一值计数,不包括缺失值
  • Entropy: 在给定时间之前类别变量的熵
  • First: 在给定时间之前变量出现的第一个值
  • Last: 在给定时间之前出现的变量最后一个值
  • Featuretools包还提供了很多其他的变换,具体可见官方网站:

https://docs.featuretools.com/en/stable/api_reference.html#aggregation-primitives

1.3.2 Time Series Continuous Features 时间序列连续变量

与前述问题一致,我们可能想要预测客户002在2020-01-08登陆的时候是否会购买。

上述数据中的连续变量有Seconds Stay,即客户每次登陆后停留了多久。基于这个连续变量我们可以进行特征工程并合成以下新变量:每一次登陆前他的平均停留时间,每一次登陆前他的停留时间的标准差,每一次登陆前他的停留时间的滑动平均等。

而这一些变量,同样可以通过Featuretools包简单实现。

Featuretools包提供了以下一些有用的特征变换:

  • COUNT: 在给定时间之前的变量计数值,不包括缺失值
  • First: 在给定时间之前变量出现的第一个值
  • Last: 在给定时间之前变量出现的最后一个值
  • Mean: 在给定时间之前该变量的平均值,不包括缺失值
  • Sum: 在给定时间之前该变量的求和,不包括缺失值
  • Min: 在给定时间之前该变量的最小值,不包括缺失值
  • Max: 在给定时间之前该变量的最大值,不包括缺失值
  • Std: 在给定时间之前该变量的标准差,不包括缺失值
  • Median: 在给定时间之前该变量的中位数,不包括缺失值
  • Trend: 在给定时间之前该变量的趋势,即线性斜率
  • Featuretools包还提供了很多其他的变换,具体可见官方网站:

https://docs.featuretools.com/en/stable/api_reference.html#aggregation-primitives

1.3.3 Implementation 代码实现

# 原始数据集
dataset

1.3.3.1 Create EntitySet 生成实体集

# 首先,我们需要创建EntitySet即实体集
# 它是数据集中实体的集合,包含实体之间的关系
# 它们的创建能帮助Featuretool了解数据的结构,从而实现自动时间序列特征工程

es = ft.EntitySet(id="customer_data") # 首先生成一个空白的实体集

# 数据集中一个实体即每一个客户,我们有客户层面的数据,例如客户的性别,客户的年龄
df_customer = dataset[['Customer ID', 'Sex', 'Age']].drop_duplicates()
df_customer

# 现在我们将这个实体加入到实体集中
es = es.entity_from_dataframe(entity_id="Customer",
                              dataframe=df_customer,
                              index= 'Customer ID') 
# 在这个实体中,Customer ID是将每一个顾客区别开的索引

es['Customer']

Entity: Customer
Variables:
Customer ID (dtype: index)
Sex (dtype: categorical)
Age (dtype: numeric)
Shape:
(Rows: 3, Columns: 3)

# 第二个实体即每一次发生的交易,我们也有交易层面的数据,即每一次的停留时间,购买的产品名称
es = es.entity_from_dataframe(entity_id="Transaction",
                              dataframe=dataset[["Customer ID","Seconds Stay", 
                                                 "Item Purchase", 
                                                 "Time","Target"]].reset_index(),
                              index="index", 
                              # 在这个实体中,索引为‘index’
                              time_index="Time", # 时间索引为‘Time’
                              variable_types={"Item Purchase": ft.variable_types.Categorical, 
                                              "Seconds Stay": ft.variable_types.Numeric,
                                              "Target": ft.variable_types.Numeric})
es['Transaction']

Entity: Transaction
Variables:
index (dtype: index)
Customer ID (dtype: categorical)
Time (dtype: datetime_time_index)
Item Purchase (dtype: categorical)
Seconds Stay (dtype: numeric)
Target (dtype: numeric)
Shape:
(Rows: 7, Columns: 6)

# 现在,我们添加实体之间的关系
relationship = ft.Relationship(es["Customer"]["Customer ID"],
                               es["Transaction"]["Customer ID"])
# 每一个用户都有一些交易与之关联
# 故我们称用户为母实体
# 每一次交易为子实体

es = es.add_relationship(relationship)
es

Entityset: customer_data
Entities:
Customer [Rows: 3, Columns: 3]
Transaction [Rows: 7, Columns: 6]
Relationships:
Transaction.Customer ID -> Customer.Customer ID

1.3.3.2 Set up cut-time 设置时间截断

关于时间截断的更多信息可以在Featuretools的官网网站中获得:

https://docs.featuretools.com/en/stable/automated_feature_engineering/handling_time.html

# 创建时间切割
# 通常在时间序列的数据集中
# 其可能包含很多时间节点
# 如果处理不慎就可能导致未来的信息泄漏

# 故我们需要设置Cut-time 时间切割
# 即让Featuretool明白其需要考虑可能的信息泄漏问题
# 我们可以在Featuretools中加入一个用于指示对应切割时间的数据集
# 其指定了每一行可使用信息的最后时间点
# 即对每一行数据,在进行对应的特征工程时,我们仅仅会考虑这一行时间切割点之前的信息

ct = dataset[['Customer ID','Time']].copy() # the cut-off dataframe
ct

# 但是在我们的这一问题中
# 每一行的截止时间应在交易发生时间之前
# 此次交易的信息是不应该被涵盖在特征工程中
# 因为我们没有当前行信息来预测当前行的购买
# 例如,直到顾客完成购买后,我们才知道他购买了哪种产品

ct['Time'] = ct['Time'] + datetime.timedelta(seconds = -1) 
# 将时间截断设置为每一次登陆时间的前1秒

ct

1.3.3.3 Auto Feature Engineering 自动特征工程

一种使用Featuretools的策略是让其自动生成所有可能的特征。这种策略中我们无需指定我们要为每个原始特征进行的转换。随后我们可以从这些自动生成的特征中筛选我们想要的特征。但这种策略往往会比较占用内存和运行时间。

# 开始自动特征工程,我们无需指定我们要为每个原始特征进行的转换
fm, features = ft.dfs(entityset=es,
                      target_entity='Customer', 
                      # 我们想要在每一个客户层面累计信息
                      # 因为我们想要预测的是未来每个客户的可能购买行为
                      max_depth=2, # 设置深度为 2, 即交叉项最后包含两种原始变量
                      cutoff_time=ct,
                      cutoff_time_in_index=True)

# 我们甚至可以指定训练窗口来实现滑动平均的效果,此处不进行展示
fm; # 特征工程后的结果
fm.columns

Index([‘Sex’, ‘Age’, ‘SUM(Transaction.Target)’,
‘SUM(Transaction.Seconds Stay)’, ‘STD(Transaction.Target)’,
‘STD(Transaction.Seconds Stay)’, ‘MAX(Transaction.Target)’,
‘MAX(Transaction.Seconds Stay)’, ‘SKEW(Transaction.Target)’,
‘SKEW(Transaction.Seconds Stay)’, ‘MIN(Transaction.Target)’,
‘MIN(Transaction.Seconds Stay)’, ‘MEAN(Transaction.Target)’,
‘MEAN(Transaction.Seconds Stay)’, ‘COUNT(Transaction)’,
‘NUM_UNIQUE(Transaction.Item Purchase)’,
‘MODE(Transaction.Item Purchase)’, ‘NUM_UNIQUE(Transaction.YEAR(Time))’,
‘NUM_UNIQUE(Transaction.WEEKDAY(Time))’,
‘NUM_UNIQUE(Transaction.MONTH(Time))’,
‘NUM_UNIQUE(Transaction.DAY(Time))’, ‘MODE(Transaction.YEAR(Time))’,
‘MODE(Transaction.WEEKDAY(Time))’, ‘MODE(Transaction.MONTH(Time))’,
‘MODE(Transaction.DAY(Time))’],
dtype=’object’)

features
# 特征工程生成的新变量名称与对应的合成方式
# 自动功能工程可能会生成一些毫无意义的变量
# 这需要我们的人工筛选

[,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
]

另一种策略即指定每一个原始变量我们需要什么对应的变换。在这种策略下,我们可以更好地控制特征工程的结果并节省时间与内存。

from featuretools.primitives import TimeSinceLast
# import TimeSinceLast
# 这样我们可以控制时间维度的单位

operation_dict = {("count", TimeSinceLast(unit = "hours")): {"include_variables": {"Transaction": ["index"]}},
                  ("entropy", 
                   "num_unique",
                   "mode"): {"include_variables": {"Transaction": ["Item Purchase"]}},
                  ("mean", 
                   "max",
                   "median",
                   "skew",
                   "std"): {"include_variables": {"Transaction": ["Seconds Stay","Target"]}},
                   "last": {"include_variables": {"Transaction": ["Item Purchase","Seconds Stay",
                                                                  "Target"]}}
                 }

# 新增一个计数变换来显示每次交易之前共有多少次登陆次数
# 新增一个TimeSinceLast变换,以显示此次登陆距离上一次登陆的时间间隔
# 新增对类别变量Item Purchase的熵,唯一值计数,众数及最近一次登陆购买的产品的统计
# 新增数值变量Seconds Stay 及目标变量(滞后项)的均值,最大值,中位数,偏度,标准差和最近值
fm, features = ft.dfs(entityset=es,
                      target_entity='Customer',
                      max_depth=2,
                      cutoff_time=ct,
                      cutoff_time_in_index=True, 
                      agg_primitives = ['count','entropy', 'num_unique','mode','last',
                                        'mean','max','median','skew','std',
                                        TimeSinceLast(unit = "hours")],
                      # 为简单起见,我们不包含transform primative
                      # 即将实体中的一个或多个变量作为输入
                      # 并为该实体输出一个新变量的变换

                      # aggregation primitive & transform primitive的区别可见:
                      # https://docs.featuretools.com/en/latest/automated_feature_engineering/primitives.html

                      primitive_options= operation_dict # 指明对每一个原始变量我们想要的变换
                      )
features # 新变量名称及对应的变换

[,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
]

fm # 特征工程的结果

好了,以上就是关于时间序列变量的数据预处理介绍。建议读者结合代码,在 Jupyter 中实操一遍。

特征工程:数据预处理完成!

目前该项目完整中文版已制作完成!

中文版 Jupyter 地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook/tree/master/%E4%B8%AD%E6%96%87%E7%89%88


本文首发于公众号:AI有道(ID: redstonewill),欢迎关注!

未经允许不得转载:红色石头的个人博客 » 专栏 | 基于 Jupyter 的特征工程手册:数据预处理(四)

赞 (0) 打赏

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏