基於 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),歡迎關注!