像深度学习这样的机器学习方法可以用于时间序列预测。
在机器学习方法出现之前,时间序列预测问题必须重构为监督学习问题来处理,将时间序列转化为输入和输出的时间序列对。
在本教程中,你将了解到如何将单变量和多变量时间序列预测问题转换为机器学习算法处理的监督学习问题。
完成本教程后,您将知道:
让我们开始吧。
在开始之前,让我们花点时间来更好地理解时间序列和监督学习数据的形式。
时间序列是按照时间索引排列的一串数字,可以理解为有序值构成的一列数据或有序列表。
例如:
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()函数。
对于一个给定的DataFrame,可以使用 shift() 函数前移(前面的缺失值用NaN补全)或后移(后面的缺失值用NaN补全)来采集定长切片保存至列中。
在对监督学习的时间序列数据集进行处理时,创建滞后观察列和预测列是必需的。
我们来看一下shift函数应用的实例。
我们可以假定时间序列数据集为10个数字的序列,此时得到的单列Dtaframe如下:
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)
运行代码,我们在原有数据集的基础上得到了两列数据,第一列为原始的观测值,第二列为下移后得到的新列。
可以看到,通过前移序列,我们得到了一个原始的监督学习问题( X 和 y 的左右顺序是反的)。忽略行标签,第一列的数据由于存在NaN值应当被丢弃。从第二行来看,输入数据0.0位于第二列(X),输出数据1位于第一列(y)。
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)。
shift操作也可以接受负整数作为输入,这样的效果是在末尾插入新行来提取新的观测结果。代码示例如下:
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.0。
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)用于预测。
从这一节我们可以看到我们可以通过设定shift函数左移或右移来从原始时间序列上创建用于监督学习的输入和输出模式组成的序列。
这种方法不仅可以用于传统的 X => y 预测,也可以实现 X => Y,即输入和输出都可以是序列。
此外,移位函数也适用于所谓的多变量时间序列问题。在这种问题中,我们在一个时间序列中不是仅有一组观测值而是有多组观测值(如温度和大气压)。此时时间序列中的变量需要整体前移或者后移来创建多元的输入序列和输出序列。我们稍后将讨论这个问题。
我们可以利用Pandas中的 shift() 函数实现在给定输入和输出序列长度的情况下自动重组时间序列问题的数据集。
这是一个很有用的工具,因为它允许我们在用机器学习算法解决时间序列问题时可以尝试不同的输入输出序列组合,以便观察哪一个可能得到更优的模型。
在本节中,我们将用Python实现 series_to_supervised() 函数来接受单变量/多变量时间序列输入并转化为监督学习所需的数据集。
这个函数共有4个参数:
该函数返回一个值:
新的数据集将被构造为DataFrame,每一列根据变量的编号以及该列左移或右移的步长来命名。这允许你从给定的单变量或多变量序列上设定不同的时移步长来尝试解决当前的时间序列问题。
DataFrame返回之后,你就可以根据需要将其分割为 X 和 y 两部分以供监督学习使用。
上面的函数定义了每列的默认名,所以你可以在返回数据上直接调用,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):
"""
将时间序列重构为监督学习数据集.
参数:
data: 观测值序列,类型为列表或Numpy数组。
n_in: 输入的滞后观测值(X)长度。
n_out: 输出观测值(y)的长度。
dropnan: 是否丢弃含有NaN值的行,类型为布尔值。
返回值:
经过重组后的Pandas DataFrame序列.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# 输入序列 (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)]
# 预测序列 (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)]
# 将列名和数据拼接在一起
agg = concat(cols, axis=1)
agg.columns = names
# 丢弃含有NaN值的行
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):
"""
将时间序列重构为监督学习数据集.
参数:
data: 观测值序列,类型为列表或Numpy数组。
n_in: 输入的滞后观测值(X)长度。
n_out: 输出观测值(y)的长度。
dropnan: 是否丢弃含有NaN值的行,类型为布尔值。
返回值:
经过重组后的Pandas DataFrame序列.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# 输入序列 (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)]
# 预测序列 (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)]
# 将列名和数据拼接在一起
agg = concat(cols, axis=1)
agg.columns = names
# 丢弃含有NaN值的行
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):
"""
将时间序列重构为监督学习数据集.
参数:
data: 观测值序列,类型为列表或Numpy数组。
n_in: 输入的滞后观测值(X)长度。
n_out: 输出观测值(y)的长度。
dropnan: 是否丢弃含有NaN值的行,类型为布尔值。
返回值:
经过重组后的Pandas DataFrame序列.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# 输入序列 (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)]
# 预测序列 (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)]
# 将列名和数据拼接在一起
agg = concat(cols, axis=1)
agg.columns = names
# 丢弃含有NaN值的行
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
另一种预测问题类型是使用过去的观测序列来预测未来的观测序列。
这就是多步预测或序列预测。
我们可以指定另一个参数来重构序列预测问题中的时间序列。举例来说,如果我们的预测问题需要用过去的两个观测值输入来预测两个未来的观测值,我们可以通过下面的调用形式来重构数据:
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):
"""
将时间序列重构为监督学习数据集.
参数:
data: 观测值序列,类型为列表或Numpy数组。
n_in: 输入的滞后观测值(X)长度。
n_out: 输出观测值(y)的长度。
dropnan: 是否丢弃含有NaN值的行,类型为布尔值。
返回值:
经过重组后的Pandas DataFrame序列.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# 输入序列 (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)]
# 预测序列 (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)]
# 将列名和数据拼接在一起
agg = concat(cols, axis=1)
agg.columns = names
# 丢弃含有NaN值的行
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-n)和输出变量(t+n)情况下重构数据的区别。
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):
"""
将时间序列重构为监督学习数据集.
参数:
data: 观测值序列,类型为列表或Numpy数组。
n_in: 输入的滞后观测值(X)长度。
n_out: 输出观测值(y)的长度。
dropnan: 是否丢弃含有NaN值的行,类型为布尔值。
返回值:
经过重组后的Pandas DataFrame序列.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# 输入序列 (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)]
# 预测序列 (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)]
# 将列名和数据拼接在一起
agg = concat(cols, axis=1)
agg.columns = names
# 丢弃含有NaN值的行
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 均为观测值但是只有 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
可以看到,我们只需要指定输入输出序列的长度就可以轻松地在多变量时间序列上进行序列预测。
例如,下面是单步输入,两步输出的序列预测问题实例:
from pandas import DataFrame
from pandas import concat
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
"""
将时间序列重构为监督学习数据集.
参数:
data: 观测值序列,类型为列表或Numpy数组。
n_in: 输入的滞后观测值(X)长度。
n_out: 输出观测值(y)的长度。
dropnan: 是否丢弃含有NaN值的行,类型为布尔值。
返回值:
经过重组后的Pandas DataFrame序列.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# 输入序列 (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)]
# 预测序列 (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)]
# 将列名和数据拼接在一起
agg = concat(cols, axis=1)
agg.columns = names
# 丢弃含有NaN值的行
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)
运行实例,可以看到重构得到的DataFrame。
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将时间序列数据集重新组织来供监督学习使用。
具体来说,你了解到: