原文:《How to Convert a Time Series to a Supervised Learning Problem in Python》---Jason Brownlee
一、前言
像深度學習這樣的機器學習方法可以用于時間序列預測。
在機器學習方法可以被使用前,時間序列預測問題必須重新構建成監督學習問題,從一個單純的序列變成一對序列輸入和輸出。
在這個教程中,你將了解如何將單變量和多變量時間序列預測問題轉換為與機器學習算法一起使用的監督學習問題。
在你完成本教程后,你將知道:
- 如何開發一個功能,將時間序列數據集轉換為監督學習數據集。
- 如何變換單變量時間序列數據進行機器學習。
- 如何變換多元時間序列數據進行機器學習。
讓我們開始吧。
二、時間序列與監督學習
在我們開始之前,讓我們花點時間來更好地理解時間序列和監督學習數據的形式。
時間序列是由時間索引排序的一系列數字, 這可以被認為是有序值列表或列。
例如:
0
1
2
3
4
5
6
7
8
9
監督學習問題由輸入模式(X)和輸出模式(y)組成,使得算法可以學習如何從輸入模式預測輸出模式。
例如:
X, y
1 2
2, 3
3, 4
4, 5
5, 6
6, 7
7, 8
8, 9
有關這個主題更多的信息,請參考如下文章:
三、Pandas shift()方法介紹
幫助將時間序列數據轉化為監督學習問題的關鍵方法是Pandas shift()函數。
給定一個DataFrame,可以使用shift()函數來創建向前推送的列的副本(NaN值的行添加到前面)或拉回(添加到最后的NaN值的行)。
這是創建滯后觀察列以及監督學習格式的時間序列數據集的預測觀測列所需的行為。
我們來看一些shift()函數的實際使用案例。
我們可以定義一個由10個數字組成的序列來模擬時間序列數據集,在這種情況下,DataFrame中的單個列如下所示:
from pandas import DataFrame
df = DataFrame()
df['t'] = [x for x in range(10)]
print(df)
運行上面的例子,按行打印時間序列數據,輸出如下:
t
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
我們可以通過在頂部插入一個新的行來將所有的觀察結果向下移動一步。 由于新行沒有數據,我們可以使用NaN來表示“無數據”。
shift()函數可以為我們做到這一點,我們可以插入這個移位列在我們原始列的旁邊。
from pandas import DataFrame
df = DataFrame()
df['t'] = [x for x in range(10)]
df['t-1'] = df['t'].shift(1)
print(df)
運行示例,我們發現數據集中有了兩列的值,第一個是原來的列和一個新的shift()函數產生的列。
我們可以看到,將序列向前移動一步,我們構造出了一個原始的監督學習問題,盡管X和y的順序是錯誤的。 忽略行標簽的那一列,由于NaN值,第一行需要被丟棄。 第二行顯示第二列(輸入或X)中的輸入值0.0和第一列(輸出或y)中的值1。
t t-1
0 0 NaN
1 1 0.0
2 2 1.0
3 3 2.0
4 4 3.0
5 5 4.0
6 6 5.0
7 7 6.0
8 8 7.0
9 9 8.0
我們可以看到,如果我們可以重復上述過程,通過移動2步,3步和更多的移位,我們如何創建長的輸入序列(X),用來預測輸出值(y)。
移位運算符也可以接受一個負整數值。 這樣做的結果是通過在最后插入新行來提取結果。 下面是一個例子:
from pandas import DataFrame
df = DataFrame()
df['t'] = [x for x in range(10)]
df['t+1'] = df['t'].shift(-1)
print(df)
運行該示例,顯示了一個最后一行值為NaN的新列。
我們可以看到,原始列可以作為輸入(X),第二個新列作為輸出值(y)。 那就是輸入值0可以用來預測1的輸出值。
t t+1
0 0 1.0
1 1 2.0
2 2 3.0
3 3 4.0
4 4 5.0
5 5 6.0
6 6 7.0
7 7 8.0
8 8 9.0
9 9 NaN
在技術上,在時間序列預測術語中,當前時間(t)和未來時間(t + 1,t + n)是預測時間,過去的觀測值(t-1,t-n)被用于預測。
我們可以看到正向和負向的移動可以用來創建一個新的數據幀,從而轉變成監督學習問題的時間序列的輸入和輸出模式。
這不僅允許經典的X - > y預測,而且允許X - > Y,其中輸入和輸出都可以是序列。
此外,移位函數也適用于所謂的多元時間序列問題。 我們有多個(例如溫度和壓力),而不是有一組時間序列的觀測值。 時間序列中的所有變量可以向前或向后移動以創建多元輸入和輸出序列。 我們將在本教程稍后討論這個問題。
四、series_to_supervised()函數介紹
我們可以通過給定的輸入和輸出序列的長度,使用Pandas中的shift()函數自動創建新的時間序列問題的框架。
這將是一個有用的工具,因為它可以讓我們使用機器學習算法探索不同框架的時間序列問題,來找到更好的模型。
在本節中,我們將定義一個名為series_to_supervised()的新Python函數,它采用單變量或多變量時間序列,并將其作為監督學習數據集。
該函數有四個參數:
- 數據:序列,列表或二維的NumPy數組。 必需的參數。
- n_in:作為輸入的滯后步數(X)。 值可能介于[1..len(data)],可選參數。 默認為1。
- n_out:作為輸出的移動步數(y)。 值可以在[0..len(data)-1]之間, 可選參數。 默認為1。
- dropnan:Boolean是否刪除具有NaN值的行。 可選參數。 默認為True。
該函數返回一個單一的值:
- 返回:作為監督學習序列的Pandas DataFrame類型值。
新的數據集被構造為一個DataFrame,每一列都適當地以可變數量和時間步長命名。 這允許您從給定的單變量或多變量時間序列中設計各種不同的時間步長序列類型預測問題。
一旦DataFrame返回,您可以決定如何將返回的DataFrame的行分割為X和Y兩部分,以便以任何您希望的方式監督學習。
這個函數是用默認參數定義的,所以如果你只用你的數據調用它,它將構造一個DataFrame,其中t-1為X,t為y。
該函數可以在Python 2和Python 3中運行,下面列出了完整的功能,包括功能注釋:
from pandas import DataFrame
from pandas import concat
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
"""
Frame a time series as a supervised learning dataset.
Arguments:
data: Sequence of observations as a list or NumPy array.
n_in: Number of lag observations as input (X).
n_out: Number of observations as output (y).
dropnan: Boolean whether or not to drop rows with NaN values.
Returns:
Pandas DataFrame of series framed for supervised learning.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
if i == 0:
names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg
如果你發現什么好的方法,可以使上面的函數更強大或更可讀,請在下面的評論中告訴我。
現在我們有了全部的函數,我們可以探索如何使用它。
五、移動一步的單變量預測
在時間序列預測中的標準做法是使用過去的觀察值(例如t-1)作為輸入變量來預測當前的時間步長(t),這被稱為一步預測。
下面的例子演示了使用過去的時間步(t-1)來預測當前時間步長(t)的一個例子。
from pandas import DataFrame
from pandas import concat
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
"""
Frame a time series as a supervised learning dataset.
Arguments:
data: Sequence of observations as a list or NumPy array.
n_in: Number of lag observations as input (X).
n_out: Number of observations as output (y).
dropnan: Boolean whether or not to drop rows with NaN values.
Returns:
Pandas DataFrame of series framed for supervised learning.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
if i == 0:
names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg
values = [x for x in range(10)]
data = series_to_supervised(values)
print(data)
運行上面的代碼,輸出結果如下:
var1(t-1) var1(t)
1 0.0 1
2 1.0 2
3 2.0 3
4 3.0 4
5 4.0 5
6 5.0 6
7 6.0 7
8 7.0 8
9 8.0 9
我們可以看到,列值被命名為“var1”,輸入列值被命名為(t-1),輸出時間步長命名為(t)。
我們還可以看到,具有NaN值的行已經從DataFrame中自動刪除。
我們可以用任意數量的長度輸入序列(如3)來重復這個例子,這可以通過指定輸入序列的長度作為參數來完成; 例如:
data = series_to_supervised(values, 3)
完整的例子如下所示:
from pandas import DataFrame
from pandas import concat
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
"""
Frame a time series as a supervised learning dataset.
Arguments:
data: Sequence of observations as a list or NumPy array.
n_in: Number of lag observations as input (X).
n_out: Number of observations as output (y).
dropnan: Boolean whether or not to drop rows with NaN values.
Returns:
Pandas DataFrame of series framed for supervised learning.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
if i == 0:
names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg
values = [x for x in range(10)]
data = series_to_supervised(values, 3)
print(data)
再次運行該示例,并打印重新構建的序列。 我們可以看到,輸入序列是按照正確的從左到右的順序,輸出變量是在最右邊預測的。
var1(t-3) var1(t-2) var1(t-1) var1(t)
3 0.0 1.0 2.0 3
4 1.0 2.0 3.0 4
5 2.0 3.0 4.0 5
6 3.0 4.0 5.0 6
7 4.0 5.0 6.0 7
8 5.0 6.0 7.0 8
9 6.0 7.0 8.0 9
六、多步或者序列預測
另一種類型的預測問題是使用過去的值來預測未來的序列值,這可以被稱為序列預測或多步預測。
我們可以通過指定另一個參數來構建序列預測的時間序列。 例如,我們可以用2個過去的觀測值的輸入序列來構造一個預測問題,以便預測2個未來的觀測值如下:
data = series_to_supervised(values, 2, 2)
完整的代碼如下:
from pandas import DataFrame
from pandas import concat
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
"""
Frame a time series as a supervised learning dataset.
Arguments:
data: Sequence of observations as a list or NumPy array.
n_in: Number of lag observations as input (X).
n_out: Number of observations as output (y).
dropnan: Boolean whether or not to drop rows with NaN values.
Returns:
Pandas DataFrame of series framed for supervised learning.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
if i == 0:
names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg
values = [x for x in range(10)]
data = series_to_supervised(values, 2, 2)
print(data)
運行上面的代碼,輸出結果如下,t-2和t-1作為輸入序列,t和t+1作為輸出序列:
var1(t-2) var1(t-1) var1(t) var1(t+1)
2 0.0 1.0 2 3.0
3 1.0 2.0 3 4.0
4 2.0 3.0 4 5.0
5 3.0 4.0 5 6.0
6 4.0 5.0 6 7.0
7 5.0 6.0 7 8.0
8 6.0 7.0 8 9.0
七、多變量預測
另一個重要的時間序列稱為多元時間序列。
這是我們可以觀察到多種不同的方式,并有興趣預測其中的一個或多個。
例如,我們可能有兩組時間序列觀測obs1和obs2,我們希望預測其中的一個或兩個。
我們可以以完全相同的方式調用series_to_supervised(),如下:
from pandas import DataFrame
from pandas import concat
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
"""
Frame a time series as a supervised learning dataset.
Arguments:
data: Sequence of observations as a list or NumPy array.
n_in: Number of lag observations as input (X).
n_out: Number of observations as output (y).
dropnan: Boolean whether or not to drop rows with NaN values.
Returns:
Pandas DataFrame of series framed for supervised learning.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
if i == 0:
names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg
raw = DataFrame()
raw['ob1'] = [x for x in range(10)]
raw['ob2'] = [x for x in range(50, 60)]
values = raw.values
data = series_to_supervised(values)
print(data)
運行示例將打印數據,為顯示一個時間步長但是包含兩個變量的輸入模式,以及一個時間步長兩個變量的輸出模式。
同樣,根據問題的具體情況,可以任意選擇將列分成X和Y,例如,如果當前觀察到的var1也作為輸入提供,并且只有var2被預測。
var1(t-1) var2(t-1) var1(t) var2(t)
1 0.0 50.0 1 51
2 1.0 51.0 2 52
3 2.0 52.0 3 53
4 3.0 53.0 4 54
5 4.0 54.0 5 55
6 5.0 55.0 6 56
7 6.0 56.0 7 57
8 7.0 57.0 8 58
9 8.0 58.0 9 59
通過指定輸入和輸出序列的長度,您可以看到如何使用多元時間序列輕松地進行序列預測。
例如,下面是以1個時間步驟作為輸入和2個時間步驟作為預測序列的重新構造的示例。
from pandas import DataFrame
from pandas import concat
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
"""
Frame a time series as a supervised learning dataset.
Arguments:
data: Sequence of observations as a list or NumPy array.
n_in: Number of lag observations as input (X).
n_out: Number of observations as output (y).
dropnan: Boolean whether or not to drop rows with NaN values.
Returns:
Pandas DataFrame of series framed for supervised learning.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
if i == 0:
names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg
raw = DataFrame()
raw['ob1'] = [x for x in range(10)]
raw['ob2'] = [x for x in range(50, 60)]
values = raw.values
data = series_to_supervised(values, 1, 2)
print(data)
運行上面的代碼,輸出如下:
var1(t-1) var2(t-1) var1(t) var2(t) var1(t+1) var2(t+1)
1 0.0 50.0 1 51 2.0 52.0
2 1.0 51.0 2 52 3.0 53.0
3 2.0 52.0 3 53 4.0 54.0
4 3.0 53.0 4 54 5.0 55.0
5 4.0 54.0 5 55 6.0 56.0
6 5.0 55.0 6 56 7.0 57.0
7 6.0 56.0 7 57 8.0 58.0
8 7.0 57.0 8 58 9.0 59.0
嘗試使用自己的數據集,并嘗試使用多個不同的框架,以查看最佳效果。
八、總結
在本教程中,您發現了如何將時間序列數據集重新組織為有監督的Python學習問題。
具體來說,你了解到:
- 關于Pandas shift()函數及其如何用于從時間序列數據中自動定義監督學習數據集。
- 如何將單變量時間序列重構為一步多步監督學習問題。
- 如何將多元時間序列重構為一步多步監督學習問題。