开始使用Python中的时间序列分解
时间序列指的是在一段时间内连续收集的数据。在任何特定的时间点上,各种成分通常对任何观察到的时间序列值都有贡献。
因此,一个时间序列可以被分解,使每个组成部分独立存在。通常情况下,如果一个模型的大部分变化原因是未知的,那么它的工作是具有挑战性的。在时间序列中,模型的一些变化原因是确定性的。
分析一个有许多变异原因的模型在计算上是很昂贵的。因此,我们对那些变化是确定的成分进行分解。不确定的部分,即残差,然后被用来分析自相关。
前提条件
为了从本材料中获得最大的收益,学习者必须具备以下条件。
- Python编程语言的基本知识。
- 熟悉[谷歌Colab]或[Jupyter笔记本]。
时间序列的组成部分
在任何时候,一个时间序列通常由以下组成部分组成。
- 趋势
- 季节性
- 残差
让我们了解一下这些成分是什么。
- 趋势 - 这是该时间序列的长期方向。这个组成部分通常是增加、减少或不变的。下图说明了一个时间序列的增长趋势。

- 季节性--这是在一年内发生的时间序列的周期性行为。下图是时间序列的季节性成分的一个例子。

- 残差--这是去除趋势和季节性后的时间序列的剩余部分。

正如我们所说,时间序列值通常是上述成分在任何时间点的组合。这些值可以将所有成分相加,将它们相乘,或与这两种操作相互作用。
因此,我们在分解时间序列时使用以下三种模型。
- 加法时间序列模型。
- 乘法时间序列模型。
- 伪加法模型。
现在,通常的情况是,在我们分解时间序列之前,我们首先发现要使用的模型。为了发现这一点,我们通常会绘制时间序列图,然后看该图是否满足以下假设,以选择一个合适的模型来使用。
如果季节性和残差与绘制的时间序列中的趋势无关,我们就使用加性模型来分解数据。下面的图显示了使用加性时间序列模型来分解数据的情况。正如你所看到的,季节性成分并没有随着趋势的变化而变化。

我们所说的加性时间序列模型的形式是。$O_t = T_t+S_t+R+t$
其中。
$O_t$是观察值。$T_t$是趋势值。$S_t$是季节性值。$R_t$是残差值。$_t$是某一时间指数的变量。
如果绘制的序列显示季节性和残差随着趋势的变化而变化,我们就使用一个乘法模型来分解我们的序列。下图是一个使用乘法时间序列模型的优秀例子。

从上面的时间序列图中,我们注意到,季节性随着趋势的增加而增加。一个乘法时间序列模型的形式是。
$O_t = T_t*S_t*R_t$
其中,$O_t$,$T_t$,$S_t$,$R_t$ ,正如我们之前解释的那样。
在有些情况下,我们既不能使用加法时间序列模型,也不能使用乘法模型来分解我们的数据。在这种情况下,我们使用一个特殊的模型,称为伪加性模型。这个模型的形式是。
$O_t = T_t+T_t(S_t-1)+T_t(R+t-1)$
然而,在本教程中,我们将不考虑这种模型。
既然我们知道我们可以使用各种模型来分解我们的时间序列模型,现在让我们看看我们如何在python中分解时间序列。
为了使这一环节精彩纷呈,我们将刺激每个时间序列组成部分的数据,然后分别使用加法和乘法模型对它们进行汇总,得到每种模型的两个时间序列数据。
在这之后,我们将把这些模型分解回我们创建的那些初始成分。通过这样做,我们将节省自己花在现实世界数据预处理上的时间。
现在,让我们开始吧。
Python时间序列分解
像往常一样,让我们首先为这个环节导入所需的库。
# get libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose as sm
第一步:模拟时间序列成分
开始,让我们设置我们的时间序列的范围。
# stting the range of time series
T_Series = np.arange(1, 51)
让我们创建我们的时间序列的趋势成分。
Trend = T_Series * 2.75
我们可以用下面的代码绘制这个趋势。如果我们这样做,我们应该得到一个类似于下面提供的输出。
plt.plot(T_Series, Trend, 'c.')
plt.title("Trend against Time")
plt.xlabel("Minutes")
plt.ylabel("product demand");
输出。

现在我们有了趋势部分,让我们模拟我们的季节性部分。为了做到这一点,我们将采用sin函数,因为它是能够以最佳方式产生时间序列的季节性波动的函数。
# creating the seasonality component
seasonality = 10 + np.sin(T_Series) * 10
输出。

剩下要创建的部分是残差部分。让我们用NumPY随机函数对其进行模拟。
np.random.seed(10) # for result reproducibility
residual = np.random.normal(loc=0.0, scale=1, size=len(T_Series))
然后我们绘制这个残差分量,如下所示。
plt.plot(T_Series, residual, 'r-.')
plt.title("Residuals against Time")
plt.xlabel("Minutes")
plt.ylabel("Product demand");
输出。

我们现在有了所有的时间序列成分,因此,我们可以在任何特定的时间点上创建时间序列值。
我们用这些成分做的第一件事是为我们的时间序列创建一个加法模型。因此,让我们运行下面的代码并完成这一工作。
# additive tine series model
additive_Tmodel = Trend + seasonality + residual
现在,我们的加法时间序列模型已经创建,我们可以提前一步绘制它,以实现可视化,如下所示。
plt.plot(T_Series, additive_Tmodel, 'k.')
plt.title("Additive Time Series")
plt.xlabel("Minutes")
plt.ylabel("product demand");
输出。

我们可以直接注意到,上面的加性时间序列模型的季节性成分并不随着趋势的变化而变化,这是一个时间序列被认为是加性时间序列的基本特征。
同样地,让我们建立一个乘法时间序列。为了使模式更加明显,在这种情况下,我们将忽略模型中的残差。然后,我们按以下方式创建我们的模型。
# we ignore residual to make the pattern more apparent
ignored_residual = np.ones_like(residual)
# we multiply other components to create a multiplicative time series
multiplicative_Tmodel = Trend * seasonality * ignored_residual
绘制模型。
plt.plot(T_Series, multiplicative_Tmodel, 'k-.')
plt.title("Multiplicative Time Series")
plt.xlabel("Minutes")
plt.ylabel("product demand");
这段代码返回。

从输出中,我们注意到这个时间序列的季节性成分随着趋势的变化而变化。我们寻找这个特征来声明一个时间序列模型是一个乘法时间序列模型。另外,我们应该记住,在这种情况下我们忽略了残差成分。所以我们需要理解,对于一个乘法时间序列来说,残差分量也会随着趋势的变化而变化。
到此为止,我们已经创建了我们的两类时间序列。我们手中的任务是将这些模型独立分解回它们的初始成分。
第二步:时间序列的分解
在这一节中,我们将从分解我们刚刚创建的加性时间序列模型开始。
-
加法时间序列的分解
让我们导入所需的库。
from statsmodels.tsa.seasonal import seasonal_decompose
下面的代码将把我们的模型分解成其最初的组成部分。
# frequency is the time a time serie is taking to complete on cycal
ts_dicomposition = seasonal_decompose(x=additive_Tmodel, model='additive', freq=6)
trend_estimate = ts_dicomposition.trend
seasonal_estimate = ts_dicomposition.seasonal
residual_estimate = ts_dicomposition.resid
我们可以通过运行以下代码来绘制分解后的分量。
# Plotting the time series and it's components together
fig, axes = plt.subplots(4, 1, sharex=True, sharey=False)
fig.set_figheight(10)
fig.set_figwidth(20)
# First plot to the Original time series
axes[0].plot(additive_Tmodel, label='Original')
axes[0].legend(loc='upper left');
# second plot to be for trend
axes[1].plot(trend_estimate, label='Trend')
axes[1].legend(loc='upper left');
# third plot to be Seasonality component
axes[2].plot(seasonal_estimate, label='Seasonality')
axes[2].legend(loc='upper left');
# last last plot to be Residual component
axes[3].plot(residual_estimate, label='Residuals')
axes[3].legend(loc='upper left');
输出。

第一个图代表我们的原始时间序列。第二张图表示我们从原始时间序列中提取的时间序列模型的趋势。最后,第三和第四张图表示同一时间序列中的季节性和残差成分。
-
分解乘法时间序列模型
我们将遵循与分解加法时间序列类似的步骤,只是我们将模型指定为乘法时间序列。
因此,下面的代码进行了这项活动。
ts_decomposition = seasonal_decompose(x=multiplicative_Tmodel, model='multiplicative', freq=6)
trend_estimate = ts_decomposition.trend
seasonal_estimate = ts_decomposition.seasonal
residual_estimate = ts_decomposition.resid
然后,我们通过运行下面的代码块来绘制分解后的成分。
fig, axes = plt.subplots(4, 1, sharex=True, sharey=False)
fig.set_figheight(10)
fig.set_figwidth(15)
axes[0].plot(multiplicative_Tmodel, label='Original')
axes[0].legend(loc='upper left');
axes[1].plot(trend_estimate, label='Trend')
axes[1].legend(loc='upper left');
axes[2].plot(seasonal_estimate, label='Seasonality')
axes[2].legend(loc='upper left');
axes[3].plot(residual_estimate, label='Residuals')
axes[3].legend(loc='upper left');
这段代码产生。

现在,这只是一个演示,正如我们所知,我们不是数据科学家,只能处理我们自己创造的问题。相反,我们要训练自己利用数据中隐藏的知识来处理现实世界的挑战。
因此,为了学习现实世界数据中的相关性,我们应被要求首先分解数据,以消除数据中变化的决定性原因。因此,为了训练自己对这些数据进行分解,让我们首先学会在哪里快速获取时间序列数据进行练习。
要获取时间序列,可以访问Kaggle,谷歌趋势,以及更多。然而,在这里我们不会去这些网站。相反,我们将使用一个著名的python库,为我们提供大公司的股票市场表现数据集,如亚马逊、特斯拉、FB、阿里巴巴等。
这个python库被称为yfinance 。要访问这个库中的数据,我们需要确保它已经安装在python中。
在谷歌的合作项目中,我们使用下面的代码来安装这个库。
!pip install yfinance
现在我们已经安装了这个包,但这并不意味着我们将直接访问其中的数据。为了使用它所包含的数据,我们需要加载它。所以我们按如下方式加载它。
# import the library
import yfinance as yf
我们现在可以访问加载在yfinance 库中的任何时间序列数据。因此,例如,我们可以访问Facebook股票的数据,如下面的代码所示。
例如,我们可以获得Facebook的股票数据,如下所示。
data = yf.download(tickers="FB", start="2016-1-1",end="2020-12-31",progress=False)
data.head()
这段代码应返回与下面类似的输出。

以上是Facebook股票数据的多变量时间序列。你可以改变股票的价值,并下载其他公司的数据集,如亚马逊 "AMZN",特斯拉 "TSLA",等等。
由于我们上面的Facebook数据是一个多变量的时间序列,我们不会在这次会议上讨论,让我们通过索引一列High ,提取一个单变量的,并绘制出来。
data['High'].plot()
这段代码返回的输出与下面提供的类似。

我们注意到,在绘制High 时间序列时,该序列有一个上升趋势。另外,该时间序列没有季节性成分,相反,它有一个周期性成分。这就是为什么我们访问了一年多时间内的数据,超过了一年。
因此,我们将使用之前使用的相同程序对该数据进行分解,但在这种情况下,频率将超过12个月。让我们在下面的代码中看到这一点。
ts_decompose_add = seasonal_decompose(x=data['High'],
model='additive',
freq=36) # the frequency of fluctuation is more than one year thus cyclic component
estimated_trend_add = ts_decompose_add.trend
estimated_seasonal_add = ts_decompose_add.seasonal
estimated_residual_add = ts_decompose_add.resid
现在我们可以运行下面的块状代码,并得到这些组件的打印。
fig, axes = plt.subplots(4, 1, sharex=True, sharey=False)
fig.set_figheight(10)
fig.set_figwidth(15)
axes[0].plot(data['High'], label='Original')
axes[0].legend(loc='upper left');
axes[1].plot(estimated_trend_add, label='Trend')
axes[1].legend(loc='upper left');
axes[2].plot(estimated_seasonal_add, label='Cyclic')
axes[2].legend(loc='upper left');
axes[3].plot(estimated_residual_add, label='Residuals')
axes[3].legend(loc='upper left');
输出。

我们可以看到,将时间序列频率设置为36,趋势被很好地捕捉到。另外,如果我们看一下残差图,我们可以看到没有明确的模式。因此,我们可以说我们的时间序列被很好地分解成了其组成部分。
结论
我们的研究已经结束了。我们了解了什么是时间序列和它的组成部分。我们讨论了用于时间序列分解的三种类型的模型,也描述了在何种情况下适合使用某种特定的模型。
使用一个简单的方法,我们模拟了一个时间序列数据,然后将其分解为我们所创建的初始成分。
我们了解到一个图书馆,我们从那里获取时间序列数据。我们从该库中获取了Facebook股票的数据并分解了其中的一个时间序列。至此,我们的课程结束了,我希望你喜欢它。