如何将时间序列转换为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值行)。
这是创建滞后观察列以及监督学习格式的时间序列数据集的预测观察列所需的行为。
让我们看一下移位函数的一些例子。
我们可以将模拟时间序列数据集定义为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)
运行该示例为我们提供了数据集中的两列。第一个是原始观察和一个新的移动列。
我们可以看到,将系列向前移动一个步骤给了我们一个原始的监督学习问题,尽管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和更多的移位重复此过程,我们如何创建可用于预测输出值(y)的长输入序列(X)。
移位运算符也可以接受负整数值。这具有通过在末尾插入新行来提升观察结果的效果。以下是一个例子:
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,tn)用于进行预测。
我们可以看到正向和负向移位如何用于从具有监督学习问题的输入和输出模式序列的时间序列创建新的DataFrame。
这不仅允许经典的X - > y预测,而且还允许X[] - > Y[],其中输入和输出都可以是序列。
此外,移位功能也适用于所谓的多变量时间序列问题。在这里,我们有多个(例如温度和压力),而不是对时间序列进行一组观察。时间序列中的所有变量都可以向前或向后移动以创建多变量输入和输出序列。我们将在本教程的后面部分对此进行探讨。
series_to_supervised()函数
我们可以使用Pandas中的shift()函数在给定所需的输入和输出序列长度的情况下自动创建新的时间序列问题框架。
这将是一个有用的工具,因为它将允许我们使用机器学习算法探索时间序列问题的不同框架,以查看哪些可能导致更好的模型。
在本节中,我们将定义一个名为series_to_supervised()的新Python函数,该函数采用单变量或多变量时间序列并将其构建为监督学习数据集。
该函数有四个参数:
- data:作为列表或2D NumPy数组的观察序列。需要。
- n_in:作为输入的滞后观察数(X)。值可以在[1..len(数据)]之间可选。默认为1。
- n_out:作为输出的观测数量(y)。值可以在[0..len(数据)-1]之间。可选的。默认为1。
- dropnan:Boolean是否删除具有NaN值的行。可选的。默认为True。
该函数返回一个值:
- 返回:系列的Pandas DataFrame用于监督学习。
新数据集构造为DataFrame,每列适当地由变量数和时间步命名。这允许您根据给定的单变量或多变量时间序列设计各种不同的时间步长序列类型预测问题。
返回DataFrame后,您可以决定如何将返回的DataFrame的行拆分为X和y组件,以便以任何方式进行监督学习。
该函数使用默认参数定义,因此如果仅使用数据调用它,它将构造一个数据框,其中t-1为X,t为y。
下面列出了完整的功能,包括功能注释。
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)的一个滞后时间步长(t-1)。
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)
运行该示例显示输入(tn)和输出(t + n)变量的区别,其中当前观察(t)被视为输出。
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)
运行该示例显示了大型重构的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