基于時(shí)序數(shù)據(jù)的回歸預(yù)測(cè)問(wèn)題,在工作中經(jīng)常遇到的。它與一般的監(jiān)督學(xué)習(xí)的回歸模型的區(qū)別在于數(shù)據(jù)本身是基于時(shí)序的。而常用的時(shí)序預(yù)測(cè)模型,比如arima等,添加其他特征時(shí)又不方便,不得不求助于經(jīng)典的監(jiān)督學(xué)習(xí)預(yù)測(cè)模型。本文初步介紹了對(duì)時(shí)序數(shù)據(jù)建模時(shí),如何構(gòu)建有效的特征工程。
原文翻譯自: Basic Feature Engineering With Time Series Data in Python
時(shí)間序列數(shù)據(jù),在我們可以為之使用機(jī)器學(xué)習(xí)算法建模之前,必須先重新構(gòu)建為一個(gè)監(jiān)督數(shù)據(jù)集。
在時(shí)間序列中,沒(méi)有輸入輸出特征的概念。相反,我們必須選擇要預(yù)測(cè)的變量,并使用的特征工程構(gòu)建所有的輸入,以預(yù)測(cè)未來(lái)的時(shí)間序列。
這篇教程中,你將學(xué)會(huì)如何用Python,構(gòu)建特征工程,使用機(jī)器學(xué)習(xí)算法對(duì)時(shí)間序列問(wèn)題建模。
看完本教程,你將知道:
- 基于時(shí)間序列的特征工程的原理與目標(biāo)
- 如何提取基本的時(shí)序輸入特征
- 如何提取更復(fù)雜的lag和sliding window的統(tǒng)計(jì)特征
時(shí)序的特征工程
一個(gè)時(shí)序數(shù)據(jù)集必須被轉(zhuǎn)換,才能用監(jiān)督學(xué)習(xí)算法建模。
如下的一個(gè)時(shí)序數(shù)據(jù):
time 1, value 1
time 2, value 2
time 3, value 3
轉(zhuǎn)為如下的格式:
input 1, output 1
input 2, output 2
input 3, output 3
這樣,我們才能訓(xùn)練一個(gè)監(jiān)督學(xué)習(xí)算法。
機(jī)器學(xué)習(xí)領(lǐng)域中,輸入變量成為特征,我們的任務(wù)是從時(shí)序數(shù)據(jù)中,構(gòu)造或挖掘新的輸入特征。理想情況下,我們只想要那些最能幫助模型擬合輸入(X)和輸出(Y)的特征。
本教程中,我們將介紹可以從時(shí)間序列提取的三類(lèi)特征:
- 日期時(shí)間特征:這些是觀察樣本的時(shí)間步長(zhǎng)的基本屬性
- Lag特征:這些是先前時(shí)間點(diǎn)的值
- WIndow特征:這些是先前時(shí)間觀察值的統(tǒng)計(jì)值
在我們深入研究從時(shí)間序列數(shù)據(jù)挖掘輸入特征的方法之前,先回顧一下特征工程的目標(biāo)。
特征工程的目標(biāo)
特征工程的目標(biāo)是,為輸入特征和輸出提供魯棒且簡(jiǎn)單的關(guān)系,便于監(jiān)督學(xué)習(xí)算法建模。
實(shí)際上,我們正變得復(fù)雜。
復(fù)雜性體現(xiàn)在輸入與輸出數(shù)據(jù)的關(guān)系。在時(shí)序數(shù)據(jù)場(chǎng)景下,沒(méi)有輸入變量和輸出變量的概念;我們必須也要?jiǎng)?chuàng)造這些,從頭開(kāi)始構(gòu)建有監(jiān)督的學(xué)習(xí)問(wèn)題。
或許,我們可以依靠復(fù)雜模型的能力去破譯問(wèn)題的復(fù)雜性。如果我們能更好地揭示數(shù)據(jù)中的輸入與輸出的關(guān)系,就更容易地使模型工作(甚至使用更簡(jiǎn)單的模型)。
問(wèn)題是,我們不知道待解決問(wèn)題中的輸入與輸出的潛在的固有的函數(shù)關(guān)系。如果知道的話(huà),也許就不需要機(jī)器學(xué)習(xí)了。
因此,唯一的反饋就是監(jiān)督數(shù)據(jù)集或者問(wèn)題“視圖”上的模型性能及表現(xiàn)。實(shí)際上,最好的默認(rèn)策略是使用所有可用的知識(shí)從時(shí)序數(shù)據(jù)中構(gòu)建許多優(yōu)秀的數(shù)據(jù)集,并利用模型的性能(或其他的項(xiàng)目要求)來(lái)確定好的特征和“視圖”。
為清楚起見(jiàn),我們將重點(diǎn)關(guān)注示例中的單變量(一個(gè)變量)時(shí)間序列數(shù)據(jù)集,但這些方法同樣適用于多變量時(shí)間序列問(wèn)題。接下來(lái),讓我們看一下我們將在本教程中使用的數(shù)據(jù)集。
Minimum Daily Temperatures數(shù)據(jù)集
本文將使用Minimum Daily Temperatures數(shù)據(jù)集。該數(shù)據(jù)集描述了澳大利亞墨爾本10年(1981-1990)的每天的最低溫度。單位為攝氏度,有3,650個(gè)觀測(cè)值。數(shù)據(jù)來(lái)源為澳大利亞氣象局。
下面是前5行數(shù)據(jù)的示例,包括標(biāo)題行。
"Date","Temperature"
"1981-01-01",20.7
"1981-01-02",17.9
"1981-01-03",18.8
"1981-01-04",14.6
"1981-01-05",15.8
下面是從Data Market獲取的整個(gè)數(shù)據(jù)集的圖表。

數(shù)據(jù)集顯示了增長(zhǎng)的趨勢(shì),可能還有一些季節(jié)性成分。
下載并了解數(shù)據(jù)集的更多
提醒:下載的文件包含一些問(wèn)號(hào)(“?”)字符,在使用數(shù)據(jù)集之前必須刪除這些字符。在文本編輯器中打開(kāi)文件并刪除“?”字符。同時(shí)刪除文件中的任何頁(yè)腳信息。
日期時(shí)間特征
讓我們從一些最簡(jiǎn)單的特征開(kāi)始。
這些是每次觀察的日期/時(shí)間的特征。事實(shí)上,這些僅僅是特征提取的簡(jiǎn)單的開(kāi)始,后面慢慢進(jìn)入復(fù)雜的專(zhuān)有領(lǐng)域
我們首先提取的兩個(gè)特征是,每個(gè)觀察的整型的月份和日。我們可以想象,有監(jiān)督的學(xué)習(xí)算法可能能夠利用這些輸入來(lái)幫助梳理出一年中的時(shí)間或時(shí)間類(lèi)型的季節(jié)性信息。
我們提出的監(jiān)督學(xué)習(xí)問(wèn)題是,給定月份和日,預(yù)測(cè)每日最低溫度,如下:
Month, Day, Temperature
Month, Day, Temperature
Month, Day, Temperature
我們可以使用Pandas做這些。首先,時(shí)間序列加載為一個(gè)Pandas Series。然后,為轉(zhuǎn)換的數(shù)據(jù)集構(gòu)建一個(gè)新的Pandas DataFrame。
接下來(lái),每次添加一列,值為從series中的每個(gè)觀察的時(shí)間戳信息中提取月和日信息。
下面是執(zhí)行此操作的Python代碼。
from pandas import Series
from pandas import DataFrame
series = Series.from_csv('daily-minimum-temperatures-in-me.csv', header=0)
dataframe = DataFrame()
dataframe['month'] = [series.index[i].month for i in range(len(series))]
dataframe['day'] = [series.index[i].day for i in range(len(series))]
dataframe['temperature'] = [series[i] for i in range(len(series))]
print(dataframe.head(5))
運(yùn)行上面的代碼,輸出轉(zhuǎn)換過(guò)的數(shù)據(jù)的前5行:
month day temperature
0 1 1 20.7
1 1 2 17.9
2 1 3 18.8
3 1 4 14.6
4 1 5 15.8
僅使用月份和日的信息來(lái)預(yù)測(cè)溫度并不復(fù)雜,并且很可能導(dǎo)致模型不佳。然而,這些信息與其他工程特征相結(jié)合可能最終會(huì)產(chǎn)生更好的模型。
您可以枚舉時(shí)間戳的所有屬性,并考慮可能對(duì)您的問(wèn)題有用的內(nèi)容,例如:
- 一天的分鐘數(shù)。
- 一天中的一小時(shí)。
- 營(yíng)業(yè)時(shí)間與否。
- 周末與否。
- 一年中的季節(jié)。
- 一年中的業(yè)務(wù)季度。
- 夏令時(shí)與否。
- 公共假期與否。
- 閏年與否。
從這些示例中,可以看到特征并不限于原始整數(shù)值,也可以使用二進(jìn)制標(biāo)記功能,例如是否在公共假日。
在最低溫度數(shù)據(jù)集的情況下,季節(jié)可能更相關(guān)。則季節(jié)屬于與問(wèn)題背景相關(guān)的特征,這些特征更有可能為您的模型增加價(jià)值。
基于日期時(shí)間的特征是一個(gè)良好的開(kāi)端,但包含先前時(shí)間的觀察值通常更有用。這些被稱(chēng)為lag值,我們將在下一節(jié)中介紹添加這些特征。
Lag特征
Lag特征是將時(shí)間序列預(yù)測(cè)問(wèn)題轉(zhuǎn)化為監(jiān)督學(xué)習(xí)問(wèn)題的經(jīng)典方式。
最簡(jiǎn)單的方法是在給定前一時(shí)間(t-1)的值的情況下預(yù)測(cè)下一次(t + 1)的值。具有移位值的監(jiān)督學(xué)習(xí)問(wèn)題如下所示:
Value(t-1), Value(t+1)
Value(t-1), Value(t+1)
Value(t-1), Value(t+1)
Pandas庫(kù)提供shift()函數(shù),以幫助從時(shí)間序列數(shù)據(jù)集創(chuàng)建這些移位或滯后特征。將數(shù)據(jù)集移動(dòng)1會(huì)創(chuàng)建t-1列,為第一行添加NaN(未知)值。沒(méi)有移位的時(shí)間序列數(shù)據(jù)集表示t + 1。
讓我們以一個(gè)例子來(lái)具體化。溫度數(shù)據(jù)集的前3個(gè)值分別為20.7,17.9和18.8。因此,前3個(gè)觀測(cè)值的移位和未移位溫度列表如下:
Shifted, Original
NaN, 20.7
20.7, 17.9
17.9, 18.8
我們可以使用沿列軸(axis= 1)的concat()函數(shù)將移位列合并到一個(gè)新的DataFrame中。
綜合這些,下面是為我們的日常溫度數(shù)據(jù)集創(chuàng)建Lag特征的示例。從加載的序列中提取值,并創(chuàng)建這些值的移位和未移位列表。為清晰起見(jiàn),每個(gè)列也在DataFrame中命名。
from pandas import Series
from pandas import DataFrame
from pandas import concat
series = Series.from_csv('daily-minimum-temperatures-in-me.csv', header=0)
temps = DataFrame(series.values)
dataframe = concat([temps.shift(1), temps], axis=1)
dataframe.columns = ['t-1', 't+1']
print(dataframe.head(5))
運(yùn)行上述代碼,打印包含Lag特征的新數(shù)據(jù)集的前五行:
t-1 t+1
0 NaN 20.7
1 20.7 17.9
2 17.9 18.8
3 18.8 14.6
4 14.6 15.8
可以看到,我們必須丟棄第一行才能使用數(shù)據(jù)集來(lái)訓(xùn)練監(jiān)督學(xué)習(xí)模型,因?yàn)樗鼪](méi)有全部列的數(shù)據(jù)可供使用。
Lag特征的添加稱(chēng)為滑動(dòng)窗口方法,在這種情況下,窗口寬度為1。就好像我們?cè)诿總€(gè)觀察的時(shí)間序列中滑動(dòng)焦點(diǎn),只關(guān)注窗口寬度內(nèi)的內(nèi)容。
我們可以擴(kuò)展窗口寬度并包含更多Lag特征。例如,下面的上述情況被修改為包括最后的3個(gè)觀察值以預(yù)測(cè)下一個(gè)時(shí)間步的值。
from pandas import Series
from pandas import DataFrame
from pandas import concat
series = Series.from_csv('daily-minimum-temperatures-in-me.csv', header=0)
temps = DataFrame(series.values)
dataframe = concat([temps.shift(3), temps.shift(2), temps.shift(1), temps], axis=1)
dataframe.columns = ['t-3', 't-2', 't-1', 't+1']
print(dataframe.head(5))
運(yùn)行上述代碼,打印新的數(shù)據(jù)集的前五行:
t-3 t-2 t-1 t+1
0 NaN NaN NaN 20.7
1 NaN NaN 20.7 17.9
2 NaN 20.7 17.9 18.8
3 20.7 17.9 18.8 14.6
4 17.9 18.8 14.6 15.8
同樣地,可以看到我們必須丟棄沒(méi)有足夠數(shù)據(jù)訓(xùn)練監(jiān)督模型的前幾行。
滑動(dòng)窗口方法的一個(gè)難點(diǎn)是,基于實(shí)際的問(wèn)題,如何確定為窗口的大小。
或許,一個(gè)很好的起點(diǎn)是執(zhí)行敏感性分析,嘗試一組不同的窗口寬度,從而創(chuàng)建一組不同的數(shù)據(jù)集“視圖”,并查看哪些結(jié)果表現(xiàn)更好的模型。會(huì)有一個(gè)收益遞減點(diǎn)。
另外,為什么要停止使用線(xiàn)性窗口?也許您需要上周,上個(gè)月和去年的滯后值。同樣,這歸結(jié)于特定領(lǐng)域。
在溫度數(shù)據(jù)集的情況下,來(lái)自前一年或前幾年的同一天的Lag值可能是有用的。
我們可以使用窗口做更多事情而不是包含原始值。在下一節(jié)中,我們將介紹包含在窗口中匯總統(tǒng)計(jì)信息的特征。
滾動(dòng)窗口統(tǒng)計(jì)
除了原始Lag值作為特征,還可以使用先前時(shí)間觀察值的統(tǒng)計(jì)信息及匯總。
我們可以計(jì)算滑動(dòng)窗口中值的匯總統(tǒng)計(jì)數(shù)據(jù),并將它們作為我們數(shù)據(jù)集中的特征包含在內(nèi)。也許最有用的是前幾個(gè)值的平均值,也稱(chēng)為滾動(dòng)均值。
例如,我們可以計(jì)算前兩個(gè)觀察值的平均值,并使用它來(lái)預(yù)測(cè)下一個(gè)值。對(duì)于溫度數(shù)據(jù),我們必須等待3個(gè)時(shí)間步,然后才能使用前2個(gè)值來(lái)取平均值,并使用該值來(lái)預(yù)測(cè)第3個(gè)值。
舉例:
mean(t-2, t-1), t+1
mean(20.7, 17.9), 18.8
19.3, 18.8
Pandas提供了一個(gè)rolling()函數(shù),它在每個(gè)時(shí)間步都創(chuàng)建一個(gè)帶有值窗口的新數(shù)據(jù)結(jié)構(gòu)。然后,我們可以在為每個(gè)收集時(shí)間步的窗口上執(zhí)行統(tǒng)計(jì)函數(shù),例如計(jì)算平均值。
首先,series需要移動(dòng)。然后滾動(dòng)數(shù)據(jù)集被創(chuàng)建,并在每個(gè)窗口上計(jì)算兩個(gè)值的平均值。
以下是前三個(gè)滾動(dòng)窗口中的值:
#, Window Values
1, NaN
2, NaN, 20.7
3, 20.7, 17.9
這表明我們?cè)诘?行之前不會(huì)有可用的數(shù)據(jù)。
最后,與上一節(jié)一樣,我們可以使用concat()函數(shù)構(gòu)建一個(gè)只包含新列的新數(shù)據(jù)集。
下面的示例演示了如何使用窗口大小為2的Pandas執(zhí)行此操作。
from pandas import Series
from pandas import DataFrame
from pandas import concat
series = Series.from_csv('daily-minimum-temperatures-in-me.csv', header=0)
temps = DataFrame(series.values)
shifted = temps.shift(1)
window = shifted.rolling(window=2)
means = window.mean()
dataframe = concat([means, temps], axis=1)
dataframe.columns = ['mean(t-2,t-1)', 't+1']
print(dataframe.head(5))
運(yùn)行該示例將打印新數(shù)據(jù)集的前5行。我們可以看到前兩行沒(méi)用。
- 第一個(gè)NaN是由系列的轉(zhuǎn)變創(chuàng)造的。
- 第二個(gè)因?yàn)镹aN不能用于計(jì)算平均值。
- 最后,第三行顯示,使用期望的值19.30(20.7和17.9的均值)去預(yù)測(cè)series中的第三個(gè)值18.8。
mean(t-2,t-1) t+1
0 NaN 20.7
1 NaN 17.9
2 19.30 18.8
3 18.35 14.6
4 16.70 15.8
我們可以計(jì)算出更多的統(tǒng)計(jì)數(shù)據(jù),甚至可以用不同的數(shù)學(xué)方法來(lái)計(jì)算“窗口”的定義。
下面是另一個(gè)示例,顯示窗口寬度為3,數(shù)據(jù)集包含更多摘要統(tǒng)計(jì)信息,特別是窗口中的最小值,平均值和最大值。
您可以在代碼中看到我們明確指定滑動(dòng)窗口寬度為命名變量。這使我們可以在計(jì)算系列的正確移位和將窗口寬度指定為rolling()函數(shù)時(shí)使用它。
在這種情況下,窗口寬度為3意味著我們必須將系列向前移動(dòng)2個(gè)時(shí)間步長(zhǎng)。這使得前兩行NaN。接下來(lái),我們需要計(jì)算窗口統(tǒng)計(jì)信息,每個(gè)窗口有3個(gè)值。在我們甚至從窗口中的系列中獲得足夠的數(shù)據(jù)以開(kāi)始計(jì)算統(tǒng)計(jì)數(shù)據(jù)之前,它需要3行。前5個(gè)窗口中的值如下:
#, Window Values
1, NaN
2, NaN, NaN
3, NaN, NaN, 20.7
4, NaN, 20.7, 17.9
5, 20.7, 17.9, 18.8
這表明我們不會(huì)期望至少在第5行(數(shù)組索引4)之前可用的數(shù)據(jù)。
from pandas import Series
from pandas import DataFrame
from pandas import concat
series = Series.from_csv('daily-minimum-temperatures-in-me.csv', header=0)
temps = DataFrame(series.values)
width = 3
shifted = temps.shift(width - 1)
window = shifted.rolling(window=width)
dataframe = concat([window.min(), window.mean(), window.max(), temps], axis=1)
dataframe.columns = ['min', 'mean', 'max', 't+1']
print(dataframe.head(5))
運(yùn)行代碼將打印新數(shù)據(jù)集的前5行。
我們可以檢查第5行(數(shù)組索引4)上值的正確性。我們可以看到確實(shí)17.9是最小值,20.7是[20.7,17.9,18.8]窗口中值的最大值。
min mean max t+1
0 NaN NaN NaN 20.7
1 NaN NaN NaN 17.9
2 NaN NaN NaN 18.8
3 NaN NaN NaN 14.6
4 17.9 19.133333 20.7 15.8
擴(kuò)展窗口統(tǒng)計(jì)
另一種可能有用的窗,包括該series中的所有先前數(shù)據(jù)。
這稱(chēng)為擴(kuò)展窗口,可以幫助跟蹤可觀察數(shù)據(jù)的邊界。與DataFrame上的rolling()函數(shù)一樣,Pandas提供了一個(gè)expanded()函數(shù),該函數(shù)收集每個(gè)時(shí)間步的所有先前值的集合。
匯總每個(gè)列表先前的觀察值并將其作為新特征包括在內(nèi)。例如,下面是該系列的前5個(gè)步驟的展開(kāi)窗口中的數(shù)字列表:
#, Window Values
1, 20.7
2, 20.7, 17.9,
3, 20.7, 17.9, 18.8
4, 20.7, 17.9, 18.8, 14.6
5, 20.7, 17.9, 18.8, 14.6, 15.8
同樣,您可以看到,我們對(duì)series移動(dòng)一步,以確保我們希望預(yù)測(cè)的輸出值從這些窗口值中排除。因此輸入窗口如下所示:
#, Window Values
1, NaN
2, NaN, 20.7
3, NaN, 20.7, 17.9,
4, NaN, 20.7, 17.9, 18.8
5, NaN, 20.7, 17.9, 18.8, 14.6
值得慶幸的是,統(tǒng)計(jì)計(jì)算不包括擴(kuò)展窗口中的NaN值,這意味著不需要進(jìn)一步修改。
下面是計(jì)算每日溫度數(shù)據(jù)集上展開(kāi)窗口的最小值,平均值和最大值的示例。
# create expanding window features
from pandas import Series
from pandas import DataFrame
from pandas import concat
series = Series.from_csv('daily-minimum-temperatures.csv', header=0)
temps = DataFrame(series.values)
window = temps.expanding()
dataframe = concat([window.min(), window.mean(), window.max(), temps.shift(-1)], axis=1)
dataframe.columns = ['min', 'mean', 'max', 't+1']
print(dataframe.head(5))
運(yùn)行該示例將打印數(shù)據(jù)集的前5行。
檢查擴(kuò)展的最小值,平均值和最大值的點(diǎn)顯示具有預(yù)期效果的示例
min mean max t+1
0 20.7 20.700000 20.7 17.9
1 17.9 19.300000 20.7 18.8
2 17.9 19.133333 20.7 14.6
3 14.6 18.000000 20.7 15.8
總結(jié)
在本教程中,您了解了如何使用特征工程將時(shí)間序列數(shù)據(jù)集轉(zhuǎn)換為用于機(jī)器學(xué)習(xí)的監(jiān)督學(xué)習(xí)數(shù)據(jù)集。
具體來(lái)說(shuō),你學(xué)到了:
- 對(duì)時(shí)間序列數(shù)據(jù)做特征工程的重要性和目標(biāo)。
- 如何開(kāi)發(fā)基于日期時(shí)間和Lag的特征。
- 如何開(kāi)發(fā)滑動(dòng)和擴(kuò)展窗口匯總統(tǒng)計(jì)特征。