如何用Python将时间序列转换为监督学习问题

像深度学习这样的机器学习方法可以用于时间序列预测。

在机器学习方法出现之前,时间序列预测问题必须重构为监督学习问题来处理,将时间序列转化为输入和输出的时间序列对。

在本教程中,你将了解到如何将单变量和多变量时间序列预测问题转换为机器学习算法处理的监督学习问题。

完成本教程后,您将知道:

  • 如何编写一个函数来将时间序列数据集转换为监督学习数据集。
  • 如何变换单变量时间序列数据进行机器学习。
  • 如何变换多变量时间序列数据进行机器学习。

让我们开始吧。

照片来源:Quim Gil. https://www.flickr.com/photos/quimgil/8490510169/, 保留部分权利

时间序列 vs 监督学习

在开始之前,让我们花点时间来更好地理解时间序列和监督学习数据的形式。

时间序列是按照时间索引排列的一串数字,可以理解为有序值构成的一列数据或有序列表。

例如:

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个数字的序列,此时得到的单列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)

运行代码,我们在原有数据集的基础上得到了两列数据,第一列为原始的观测值,第二列为下移后得到的新列。

可以看到,通过前移序列,我们得到了一个原始的监督学习问题( Xy 的左右顺序是反的)。忽略行标签,第一列的数据由于存在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 + 1t + n)是预测时间,过去的观测值(t-1t-n)用于预测。

从这一节我们可以看到我们可以通过设定shift函数左移或右移来从原始时间序列上创建用于监督学习的输入和输出模式组成的序列。

这种方法不仅可以用于传统的 X => y 预测,也可以实现 X => Y,即输入和输出都可以是序列。

此外,移位函数也适用于所谓的多变量时间序列问题。在这种问题中,我们在一个时间序列中不是仅有一组观测值而是有多组观测值(如温度和大气压)。此时时间序列中的变量需要整体前移或者后移来创建多元的输入序列和输出序列。我们稍后将讨论这个问题。

series_to_supervised()函数

我们可以利用Pandas中的 shift() 函数实现在给定输入和输出序列长度的情况下自动重组时间序列问题的数据集。

这是一个很有用的工具,因为它允许我们在用机器学习算法解决时间序列问题时可以尝试不同的输入输出序列组合,以便观察哪一个可能得到更优的模型。

在本节中,我们将用Python实现 series_to_supervised() 函数来接受单变量/多变量时间序列输入并转化为监督学习所需的数据集。

这个函数共有4个参数:

  • data:观测值序列,类型为列表或者二维的Numpy数组,必选参数。
  • n_in:作为输入的滞后观测值数量(X)。值介于1..len(data)之间,可选参数,默认为1。
  • n_out:作为输出的观测值数量(y)。值介于0..len(data)-1之间,可选参数,默认为1。
  • dropnan:是否删除具有NaN值的行,类型为布尔值。可选参数,默认为True。

该函数返回一个值:

  • return:为监督学习重组得到的Pandas DataFrame序列。

新的数据集将被构造为DataFrame,每一列根据变量的编号以及该列左移或右移的步长来命名。这允许你从给定的单变量或多变量序列上设定不同的时移步长来尝试解决当前的时间序列问题。

DataFrame返回之后,你就可以根据需要将其分割为 Xy 两部分以供监督学习使用。

上面的函数定义了每列的默认名,所以你可以在返回数据上直接调用,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)

运行代码,控制台将打印重构后的数据,包含两个变量的单步输入观测值和单步输出观测值。

同样,根据问题的实际情况可以将这些列任意拆分为 XY 部分,比方说 var1var2 均为观测值但是只有 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将时间序列数据集重新组织来供监督学习使用。

具体来说,你了解到:

  • Pandas的 shift() 函数及其如何用它自动从时间序列数据中产生监督学习数据集。
  • 如何将单变量时间序列重构为单步和多步监督学习问题。
  • 如何将多变量时间序列重构为单步和多步监督学习问题。

本文的版权归 ArrayZoneYour 所有,如需转载请联系作者。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习算法与Python学习

机器学习(34)之BIRCH层次聚类详解

关键字全网搜索最新排名 【机器学习算法】:排名第一 【机器学习】:排名第一 【Python】:排名第三 【算法】:排名第四 前言 在K-Means算法(机器学习...

4285
来自专栏余林丰

13.高斯消去法(2)——三角矩阵

  对于矩阵有一类特殊的矩阵,叫做三角矩阵。 ?   这种矩阵如果还是按照定义一个二维数组来对数值进行存储的话,无疑将消耗掉不必要的空间,所以我们采用压缩存储的...

2299
来自专栏CDA数据分析师

R语言预处理之异常值问题

>>>> 一、问题 什么是异常值?如何检测异常值?请伙伴们思考或者留言讨论。 >>>> 二、解决方法 1. 单变量异常值检测 2. 使用局部异常因子进行异常值...

30010
来自专栏视觉求索无尽也

【Python】Numpy使用指南

Numpy是用来存储和处理大型矩阵,比Python自身的嵌套列表结构要高效的多,本身是由C语言开发。这个是很基础的扩展,其余的扩展都是以此为基础。

1512
来自专栏上善若水

002计算机图形学之直线画线算法

主要思想是,由于我们在缓存区上画点,全部是整数。那么在画线的时候,当斜率k小于1的时候,下一个点是取(x+1,y+1)还是(x+1,y)取决于点(x+1,y+0...

2452
来自专栏林德熙的博客

C# 判断两条直线距离

d= \frac{ \left| C_1-C_2 \right|}{\sqrt{A^2+B^2}}

1192
来自专栏张俊红

Sklearn参数详解—SVM

2.3K4
来自专栏专知

【Keras教程】用Encoder-Decoder模型自动撰写文本摘要

【导读】这篇博文介绍了如何在深度学习框架Keras上实现文本摘要问题,探讨了如何使用编码器-解码器递归神经网络体系结构来解决文本摘要问题,如何实现文本摘要问题的...

8705
来自专栏人工智能头条

十分钟掌握Keras实现RNN的seq2seq学习

1414
来自专栏专知

机器学习的大局:用神经网络和TensorFlow分类文本

本译文自Déborah Mesquita在https://medium.freecodecamp.org发表的Big Picture Machine Learn...

42114

扫码关注云+社区

领取腾讯云代金券