基于 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 地址:
本文首发于公众号:AI有道(ID: redstonewill),欢迎关注!
未经允许不得转载:红色石头的个人博客 » 专栏 | 基于 Jupyter 的特征工程手册:数据预处理(四)