初看题目大家或许会感到奇怪,这时间序列预测问题需要单独列一节讲数据预处理吗?具体问题具体分析嘛!话是这么讲没错,但是我们为什么不做一个model,后面遇到了类似问题,稍事修改一下岂不美哉?
本文参考了下文,有兴趣的朋友可以看一看!
How to Convert a Time Series to a Supervised Learning Problem in Python
本文主要探讨三个问题:
1、时间序列数据预处理基本思路
2、单变量时间序列预测数据预处理
3、多变量时间序列预测数据预处理
一、时间序列数据预处理基本思路
我们看一个数据,就假设这是一个教室里感冒人数随时间(每天)变化的规律吧!
0
1
2
3
4
5
6
7
8
9
对,我们看到这个教室里每天病患增加一名。那么我们怎么来用机器学习的方法来学习这个规律呢?
我先以小白的角度来考虑一下这个问题,有这么几种解决思路:
1、增加一组id列;
2、增加日期date列
……
先说增加id列吧!有时候增加这么一列还真能提高准确率!哈哈!比如kaggle上的某个比赛(Talkingdata 1)。但这么搞根本解释不清楚,为啥要把id作为输入呢?
第二种方式似乎可行,但略微繁琐。这种思路是想构造时间和事件结果间的关系。
但换个思路,我们需要的只是输入输出对(X-Y)吧?我们似乎可以采用更加简单的方式来解决这个问题。
能否构造t-1时刻与t时刻间的关系?
(t-1时刻作为inputs,t时刻作为outputs)
这种思路中移位是关键。
帮助将时间序列数据转化为监督学习问题的关键方法是Pandas shift()函数。
二、单变量时间序列预测数据预处理
在具体探讨这个问题之前,我们先来看看shift函数的效果。
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)
运行该示例,显示了一个最后一行值为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
我们可以看到正向和负向的移动可以用来创建一个新的数据帧,从而转变成监督学习问题的时间序列的输入和输出模式。
这不仅允许经典的X - > y预测,而且允许X - > Y,其中输入和输出都可以是序列。
model函数
基本的思路有了,我们需要进一步探讨如何实现的问题了。首先,考虑多变量能否转化为单变量预测问题?应该是可以的。那我们先考虑单变量的情况。
针对单变量时间序列预测数据预处理,我们先简单设计一下输入和输出。由上文可知,输入是1列,输出可以是多列。我们再具体一点,输入的1列可以是什么?序列,列表或二维的NumPy数组应该都可以,移位操作都能生效。输出最好是pandas里的dataframe,这样我们就能直接按列操作啦!我们再再具体一点,移位究竟是移动几位?这应当作为输入由我们控制吧?
好,我们来看看完整的输入输出定义。
我们将定义一个名为series_to_supervised()的函数,它采用单变量或多变量时间序列,并将其作为监督学习数据集。
该函数有四个参数:
数据:序列,列表或二维的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)的一个例子。
在时间序列预测中的标准做法是使用过去的观察值(例如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)
运行上面的代码,输出结果如下:
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
很简单,但是配合上例子就会很有趣。之后的博客里我们会给出一个例子。
三、多变量时间序列预测数据预处理
我们接着来考虑多变量的情况。其实仔细想想就知道,这个问题无非就是把X-Y的形式改成了(X1,X2……)-Y的形式。
具体怎么操作呢?实际上也就是把输入从1×n的向量改成m×n的矩阵。
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被预测。
下篇博文我们讲讨论一个具体的例子。