簡介
自回歸
多變量時間序列包含兩個或多個變量,研究這些數(shù)據(jù)集的目的是預(yù)測一個或多個變量,參見下面的示例。

上圖是包含9個變量的多變量時間序列。這些是智能浮標(biāo)捕捉到的海洋狀況。
大多數(shù)預(yù)測模型都是基于自回歸的。這相當(dāng)于解決了一個監(jiān)督學(xué)習(xí)回歸任務(wù)。該序列的未來值是目標(biāo)變量。輸入的解釋變量是每個變量最近的過去值。
自回歸在一個主要假設(shè)下工作。最近的過去值包含了關(guān)于未來的足夠信息。但這可能不一定是真的。我們可以嘗試從最近的數(shù)據(jù)中提取更多的信息。例如,滾動匯總統(tǒng)計信息有助于描述最近的動態(tài)。
自動化特征工程
特征工程包括提取和生成解釋變量,這是任何數(shù)據(jù)科學(xué)項目的關(guān)鍵。特征的質(zhì)量是模型性能的一個核心方面,所以數(shù)據(jù)科學(xué)家在這個過程中花費了大量的時間。
特性工程通常是一個特別的過程:數(shù)據(jù)科學(xué)家基于他們的領(lǐng)域知識和專業(yè)知識創(chuàng)建特性,如果該過程的能夠自動化化處理將會為我們節(jié)省很多的時間。讓我們看看如何在多元時間序列中做到這一點。
基線模型
讀取數(shù)據(jù)
我們將使用從智能浮標(biāo)收集的多元時間序列作為本文的數(shù)據(jù)集 [1]。 這個浮標(biāo)位于愛爾蘭海岸。 它捕獲了 9 個與海洋條件相關(guān)的變量。 其中包括海水溫度、波浪高度和海水流速等。 上面的圖 1 顯示了 2022 年第一個月的情況。
以下是使用 pandas 讀取這些數(shù)據(jù)的方法:
import pandas as pd
# skipping second row, setting time column as a datetime column
# dataset available here: https://github.com/vcerqueira/blog/tree/main/data
buoy = pd.read_csv('data/smart_buoy.csv',
skiprows=[1],
parse_dates=['time'])
# setting time as index
buoy.set_index('time', inplace=True)
# resampling to hourly data
buoy = buoy.resample('H').mean()
# simplifying column names
buoy.columns = [
'PeakP', 'PeakD', 'Upcross',
'SWH', 'SeaTemp', 'Hmax', 'THmax',
'MCurDir', 'MCurSpd'
]
這個數(shù)據(jù)集研究的目標(biāo)是預(yù)測SWH(顯著波高)變量的未來值。這個變量常被用來量化海浪的高度。這個問題的一個用例是估計海浪發(fā)電的大小,因為這種能源是一種越來越受歡迎的替代不可再生能源。
自回歸模型
時間序列是多元的,所以可以使用ARDL(Auto-regressive distributed lags)方法來解決這個任務(wù)。我們在之前也介紹過則個方法。下面是這個方法的實現(xiàn):
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_percentage_error as mape
from sklearn.multioutput import MultiOutputRegressor
from lightgbm import LGBMRegressor
# https://github.com/vcerqueira/blog/blob/main/src/tde.py
from src.tde import time_delay_embedding
target_var = 'SWH'
colnames = buoy.columns.tolist()
# create data set with lagged features using time delay embedding
buoy_ds = []
for col in buoy:
col_df = time_delay_embedding(buoy[col], n_lags=24, horizon=12)
buoy_ds.append(col_df)
# concatenating all variables
buoy_df = pd.concat(buoy_ds, axis=1).dropna()
# defining target (Y) and explanatory variables (X)
predictor_variables = buoy_df.columns.str.contains('\(t\-')
target_variables = buoy_df.columns.str.contains(f'{target_var}\(t\+')
X = buoy_df.iloc[:, predictor_variables]
Y = buoy_df.iloc[:, target_variables]
# train/test split
X_tr, X_ts, Y_tr, Y_ts = train_test_split(X, Y, test_size=0.3, shuffle=False)
# fitting a lgbm model without feature engineering
model_wo_fe = MultiOutputRegressor(LGBMRegressor())
model_wo_fe.fit(X_tr, Y_tr)
# getting forecasts for the test set
preds_wo_fe = model_wo_fe.predict(X_ts)
# computing the MAPE error
mape(Y_ts, preds_wo_fe)
# 0.238
首先將時間序列轉(zhuǎn)化為一個自回歸問題。這是通過函數(shù)time_delay_embedding完成的。預(yù)測的目標(biāo)是預(yù)測未來12個SWH值(horiznotallow=12)。解釋變量是序列中每個變量的過去的24個值(n_lag =24)。
我們這里直接使用LightGBM對每個預(yù)測層位進行訓(xùn)練。這種方法法是一種常用的多步超前預(yù)測方法。它在scikit-learn中也有實現(xiàn),名為MultiOutputRegressor。
上面的代碼構(gòu)建和測試一個自回歸模型。解釋變量只包括每個變量最近的過去值。結(jié)果的平均絕對百分比誤差為0.238。
我們把這個結(jié)果作為基類對比,讓我們看看是否可以通過特性工程來提高。
多元時間序列的特征工程
本文本將介紹兩種從多元時間序列中提取特征的方法:
- 單變量特征提取。計算各變量的滾動統(tǒng)計。例如,滾動平均可以用來消除虛假的觀測;
- 二元特征提取。計算變量對的滾動統(tǒng)計,以總結(jié)它們的相互作用。例如,兩個變量之間的滾動協(xié)方差。
單變量特征提取
我們可以總結(jié)每個變量最近的過去值。例如,計算滾動平均來總結(jié)最近的情況?;蛘邼L動差量來了解最近的分散程度。
import numpy as np
SUMMARY_STATS = {
'mean': np.mean,
'sdev': np.std,
}
univariate_features = {}
# for each column in the data
for col in colnames:
# get lags for that column
X_col = X.iloc[:, X.columns.str.startswith(col)]
# for each summary stat
for feat, func in SUMMARY_STATS.items():
# compute that stat along the rows
univariate_features[f'{col}_{feat}'] = X_col.apply(func, axis=1)
# concatenate features into a pd.DF
univariate_features_df = pd.concat(univariate_features, axis=1)
如果能需要添加更多的統(tǒng)計數(shù)據(jù)。可以向SUMMARY_STATS字典添加函數(shù)來實現(xiàn)這一點。將這些函數(shù)放在一個字典中可以保持代碼整潔。
二元特征提取
單變量統(tǒng)計漏掉了不同變量之間潛在的相互作用。所以我們可以使用二元特征提取過程捕獲這些信息。
這個想法是為不同的變量對計算特征??梢允褂枚y(tǒng)計總結(jié)了這些對的聯(lián)合動態(tài)。
有兩種方法可以做到這一點:
- 滾動二元統(tǒng)計。計算以變量對作為輸入的統(tǒng)計信息。例如,滾動協(xié)方差或滾動相關(guān)性滾動二元統(tǒng)計的例子包括協(xié)方差、相關(guān)性或相對熵。
- 滾動二元變換,然后單變量統(tǒng)計。這將一對變量轉(zhuǎn)換為一個變量,并對該變量進行統(tǒng)計。例如,計算元素相互關(guān)系,然后取其平均值。有許多二元轉(zhuǎn)換的方法。例如,百分比差異、相互關(guān)聯(lián)或成對變量之間的線性卷積。通過第一步操作后,用平均值或標(biāo)準偏差等統(tǒng)計數(shù)據(jù)對這些轉(zhuǎn)換進行匯總。
下面是用于性完成這兩個過程的代碼:
import itertools
import pandas as pd
from scipy.spatial.distance import jensenshannon
from scipy import signal
from scipy.special import rel_entr
from src.feature_extraction import covariance, co_integration
BIVARIATE_STATS = {
'covariance': covariance,
'co_integration': co_integration,
'js_div': jensenshannon,
}
BIVARIATE_TRANSFORMATIONS = {
'corr': signal.correlate,
'conv': signal.convolve,
'rel_entr': rel_entr,
}
# get all pairs of variables
col_combs = list(itertools.combinations(colnames, 2))
bivariate_features = []
# for each row
for i, _ in X.iterrows():
# feature set in the i-th time-step
feature_set_i = {}
for col1, col2 in col_combs:
# features for pair of columns col1, col2
# getting the i-th instance for each column
x1 = X.loc[i, X.columns.str.startswith(col1)]
x2 = X.loc[i, X.columns.str.startswith(col2)]
# compute each summary stat
for feat, func in BIVARIATE_SUMMARY_STATS.items():
feature_set_i[f'{col1}|{col2}_{feat}'] = func(x1, x2)
# for each transformation
for trans_f, t_func in BIVARIATE_TRANSFORMATIONS.items():
# apply transformation
xt = t_func(x1, x2)
# compute summary stat
for feat, s_func in SUMMARY_STATS.items():
feature_set_i[f'{col1}|{col2}_{trans_f}_{feat}'] = s_func(xt)
bivariate_features.append(feature_set_i)
bivariate_features_df = pd.DataFrame(bivariate_features, index=X.index)
字典bivariate_transforms或BIVARIATE_STATS中添加其他的函數(shù),可以添加額外的轉(zhuǎn)換或統(tǒng)計信息。
在提取所有特征之后,我們將將它們連接到原始解釋變量。訓(xùn)練和測試的過程和之前的是一樣的,只不過我們增加了一些人工生成的變量。
# concatenating all features with lags
X_with_features = pd.concat([X, univariate_features_df, bivariate_features_df], axis=1)
# train/test split
X_tr, X_ts, Y_tr, Y_ts = train_test_split(X_with_features, Y, test_size=0.3, shuffle=False)
# fitting a lgbm model with feature engineering
model_w_fe = MultiOutputRegressor(LGBMRegressor())
model_w_fe.fit(X_tr, Y_tr)
# getting forecasts for the test set
preds_w_fe = model_w_fe.predict(X_ts)
# computing MAPE error
print(mape(Y_ts, preds_w_fe))
# 0.227
得到了0.227的平均絕對百分比誤差,這是一個小小的提高,因為我們的基線是0.238。
特征選擇
以上提取過程共得到了558個解釋變量。根據(jù)變量和匯總統(tǒng)計信息的數(shù)量,這可能會產(chǎn)生高維問題。因此,從數(shù)據(jù)集中刪除糟糕或冗余的特征是很重要的。
我們將找到一些重要特征并重新訓(xùn)練
# getting the importance of each feature in each horizon
avg_imp = pd.DataFrame([x.feature_importances_
# getting the top 100 features
n_top_features = 100
importance_scores = pd.Series(dict(zip(X_tr.columns, avg_imp)))
top_features = importance_scores.sort_values(ascending=False)[:n_top_features]
top_features_nm = top_features.index
# subsetting training and testing sets by those features
X_tr_top = X_tr[top_features_nm]
X_ts_top = X_ts[top_features_nm]
# re-fitting the lgbm model
model_top_features = MultiOutputRegressor(LGBMRegressor())
model_top_features.fit(X_tr_top, Y_tr)
# getting forecasts for the test set
preds_top_feats = model_top_features.predict(X_ts_top)
# computing MAE error
mape(Y_ts, preds_top_feats)
# 0.229
可以看到前100個特性與完整的558個特性的性能相似。以下是前15個特征的重要性(為了簡潔起見省略了其他特征):

可以看到最重要的特征是目標(biāo)變量的第一個滯后值。一些提取的特征也出現(xiàn)在前15名中。例如第三個特征SWH|Hmax_js_div。這表示目標(biāo)變量的滯后與Hmax的滯后之間的Jensen-Shannon散度。第五個特性是SeaTemp_sdev,表示海洋溫度的標(biāo)準偏差滯后。
另一種去除冗余特征的方法是應(yīng)用相關(guān)性過濾器。刪除高度相關(guān)的特征以減少數(shù)據(jù)的維數(shù),這里我們就不進行演示了。
總結(jié)
本文側(cè)重于多變量時間序列的預(yù)測問題。特征提取過程應(yīng)用于時間序列的多個子序列,在每個時間步驟中,都要用一組統(tǒng)計數(shù)據(jù)總結(jié)過去24小時的數(shù)據(jù)。
我們也可以用這些統(tǒng)計來一次性描述整個時間序列。如果我們目標(biāo)是將一組時間序列聚類,那么這可能是很有用。用特征提取總結(jié)每個時間序列。然后對得到的特征應(yīng)用聚類算法。
用幾句話總結(jié)本文的關(guān)鍵點:
- 多變量時間序列預(yù)測通常是一個自回歸過程
- 特征工程是數(shù)據(jù)科學(xué)項目中的一個關(guān)鍵步驟。
- 可以用特征工程改進多元時間序列數(shù)據(jù)。這包括計算單變量和雙變量轉(zhuǎn)換和匯總統(tǒng)計信息。
- 提取過多的特征會導(dǎo)致高維問題??梢允褂锰卣鬟x擇方法來刪除不需要的特征。