將時間序列預測問題轉換為python中的監督學習問題
像深度學習這樣的機器學習方法可以用於時間序列預測。
在機器學習方法可以被使用前,時間序列預測問題必須重新構建成監督學習問題,從一個單純的序列變成一對序列輸入和輸出。
在這個教程中,你將瞭解如何將單變數和多變數時間序列預測問題轉換為與機器學習演算法一起使用的監督學習問題。
在你完成本教程後,你將知道:
- 如何開發一個功能,將時間序列資料集轉換為監督學習資料集。
- 如何變換單變數時間序列資料進行機器學習。
- 如何變換多元時間序列資料進行機器學習。
讓我們開始吧。
時間序列與監督學習
在我們開始之前,讓我們花點時間來更好地理解時間序列和監督學習資料的形式。
時間序列是由時間索引排序的一系列數字, 這可以被認為是有序值列表或列。
例如:
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()函式及其如何用於從時間序列資料中自動定義監督學習資料集。
- 如何將單變數時間序列重構為一步多步監督學習問題。
- 如何將多元時間序列重構為一步多步監督學習問題。