# 0x01 时间序列概念

## 时间序列

时间序列是一系列的数据点, 点与点之间有着**常数时间的间隔**, 比如每秒, 每天, 每月, 每年一个数据点, 组成的一个序列. 作为时间序列, 有着与其他序列不同的, 独特的性质:

* 与时间强相关, 因此观测值之间就不再像其他的数据一样, 观测值之间是相关的, 而不是独立的
* 有**趋势trend**和**季节性seasonality**

因此, 时间序列的分析有着其独特的处理形式.

时间序列从性质上分为三种:

* **白噪声序列**(纯随机序列): 完全随机. 这种序列不需分析, 也无法分析
* **平稳非白噪声序列**: 对于这种序列, 有成熟的时间序列模型可以拟合, 是我们研究的对象, 其他形式的时间序列都需要转换成平稳序列, 才能继续分析和建模.

  另外白噪声序列也是平稳序列.
* **非平稳序列**: 需要将这种序列转换成平稳序列, 才可以继续进行分析和建模

## 平稳性

### 为什么序列要有平稳性

大多数时间序列模型建立在平稳序列上, 关于平稳序列的建模理论更成熟. 直观理解就是: 平稳序列, 序列更有**重复性**, 序列历史中的某种表现, 在未来也有很大的概率重现.

### 平稳性的表现

如果一个时间序列是平稳的, 那么序列的**统计属性**, 如**均值**, **方差**等, 都不随时间而改变

具体来说, 平稳的时间序列有以下的性质:

* 均值是常数
* 方差是常数
* 自协方差(autocovariance)与时间无关

### 判断平稳性

如何判断一个时间序列是否是平稳的, 通常通过以下的方法:

* 绘制统计量的滚动图: 选取一个时间窗口, 求窗口截取的子时间序列的均值, 方差等统计值的观测值, 然后观察它们是否随着时间变化. 这是一种主观的方法, 只可做大致的可视化体现和判断.
* **Dickey-Fuller检验**: 这是一种统计检验方法. 原假设是时间序列是非平稳序列. 具体的原理在时间序列使用到的所有统计检验方法中有详细描述. 在`Python`中的使用方法如下:

  ```python
  from statsmodels.tsa.stattools import adfuller

  dftest = adfuller(timeseries, autolag='AIC')
  dfoutput = pd.Series(dftest[0:4], index=['Test Statistic','p-value','#Lags Used','Number of Observations Used'])
  ```

### 非平稳的原因

现实世界中的时间序列基本都是非平稳的, 时间序列不平稳的主要原因主要有以下两个:

* **趋势trend**: 均值随着时间变化
* **季节性seasonality**: 在特定的时间范围内产生的变化

### 非平稳序列转化为平稳序列

由于非平稳序列的来源于以上两点相关, 因此要将非平稳序列转换成平稳序列就需要消除时间序列中的**趋势**和**季节性**.

### 消除趋势

* 使用**非线性转换函数**. 所选的函数应当是一个递增函数, 原来较大的值转换后还是更大, 但**缩小了原始较大值与较小值**之间的差别. 常用的这类函数有:
  * **log**
  * **square root 平方根**
  * **cube root 立方根**
* **聚合方法Aggregation**: 选择一个时间窗口, 然后求每个时间窗口的**均值**, 作为新的时间序列
* **平滑Smoothing**: 对时间序列取**移动平均值**
* **多项式回归拟合Ploynomial Fitting**: 拟合一个回归模型

### 平滑方法

平滑方法在时间序列处理中占有着总要的地位, 是消除趋势的重要的方法.

**做法为**: 对时间序列通过各种**移动平均**的方法进行平滑, 得到均值曲线, 并把这个曲线作为趋势, 将原来的时间序列与这个曲线相减, 就去除了时间序列中的**趋势部分**.

这个方法只能去除序列中的趋势部分, 而且如果使用窗口, 会损失掉平滑时使用的窗口大小的样本.

#### Moving Average(MA)

根据时间序列的频率, 选取一个大小固定的窗口, 计算窗口截取的子序列的均值, 然后逐个移动窗口计算. 对应于`Pyhton`中的方法为:

```python
pd.Series.rolling(window=k).mean()
pd.DataFrame.rolling(window=k).mean()
```

这样做的缺点为:

* 将会损失前$$k-1$$个样本, $$k$$是选取的窗口的长度
* 对于**频率**不是定值的时间序列(如股票价格), 选择的时间窗口的大小无法确定

这种方法因为窗口中的每个样本求平均时都是相同的权值, 因此也称为Simple Moving Average(SMA).

#### Weighted Moving Average

但通常, 我们认为时间更近的样本, 重要性越高, 因此在求平均时占的比重应该越大, 越远的权值应该越小. 因此Weighted Moving Average就是一种**权值不等求均值**的思想.

#### Exponential Weighted Moving Average(EWMA)

每个样本权重的评估有多重方法, 这里的EWMA是最常用的方法, 它使用如下的公式计算得到新的均值:

![](https://wikimedia.org/api/rest_v1/media/math/render/svg/54ece5e7437b18e4955d3e8894426eb819f9eda6)

因此可以看出, 如果衰减比例是$$\alpha$$的话($$\alpha<1$$), 最近的时间点对应的权重是$$\alpha$$, 第二近的权重为$$\alpha(1-\alpha)$$依次类推下去, 行程一种指数的形式, 这就是**指数移动平均**名称的由来.

![](https://wikimedia.org/api/rest_v1/media/math/render/svg/6bc1b4ba47fbc9c5a2421c2b332b806398ade8b3)

使用指数移动平均有一个优点为: **不损失样本**. 而使用窗口平均的方法都会损失窗口大小-1个的样本.

在`Python`中的使用方法为:

```python
# 具体使用alpha值的设定参考pd.Series.ewm函数的说明
pd.Series.ewm(...).mean()
```

### 移除趋势和季节性的方法

上面说的几种方法都是**单独去除趋势**的方法. 这里要说的是**同时去除趋势和季节性**, 得到平稳序列的方法.

#### 差分 Differencing

一阶差分即使用当前序列减去右移**time lag**时间片之后, 两个序列的差组成的序列(因此会有序列长度的损失). 通常的差分对应的**time lag = 1**, 即:

![](https://wikimedia.org/api/rest_v1/media/math/render/svg/d0d29193070515cbacc44e9f2e2d4c4ec69ec685)

二阶差分是对一阶差分得到的序列再进行一次差分, 而不是time lag = 2之后再重新计算:

![](https://wikimedia.org/api/rest_v1/media/math/render/svg/6e83f9ed5de7d6f2d61f3b346363cf2c4282dc0f)

单独提一句, **季节差分**指的就是time lag不为1, 而等于季节频率(一个季节包含的节点的数量). 一阶季节差分为:

![](https://wikimedia.org/api/rest_v1/media/math/render/svg/7571fe17f9f52be595b7b41c82072fe2a2630801)

[Autoregressive integrated moving average](https://en.wikipedia.org/wiki/Autoregressive_integrated_moving_average#Differencing)中有关于差分的介绍.

#### 分解 Decomposing

根据上面内容, 我们认为时间序列的非线性部分来自于**趋势**和**季节性**, 如果我们从时间序列中去除了这两者, 剩余的部分应当是平稳的部分.

因此我们认为时间序列由:

* 趋势
* 季节性
* 残差

三部分组成. 将时间序列构建成由这三部分组成的模型, 通过分解的技术将各成分进行还原. 通常我们认为有**加法模型**和**乘法模型**两种模型, 分别为:

$$X=T+S+e$$

$$X=T \cdot S \cdot e$$

其中$$T$$是趋势, $$S$$是季节性, $$e$$是残差部分. 在`Python`中, 分解的方法为:

```python
from statsmodels.tsa.seasonal import seasonal_decompose

decomposition = seasonal_decompose(x, model='additive', filt=None, freq=None, two_sided=True, extrapolate_trend=0)
trend = decomposition.trend
seasonal = decomposition.seasonal
residual = decomposition.resid
```

通过`model`参数选择`additive`加法模型或`multiplicative`乘法模型. 然后将获得的**残差**`residual`当作新的时间序列, 进行进一步的分析和建模.

如何判断一个时间序列是适用于加法模型还是乘法模型呢, 在后面的例子中会有涉及.

需要**注意**的是, 如果一个序列是**乘法模型**, 可以直接对这个时间序列作用`log`函数, 将其转换成**加法模型**, 然后再通过**差分**的方法将其转换成平稳序列, 而再不需要使用分解的方法了.

### 时间序列的预测

经过以上处理后, 得到的序列可能是以下两种情况:

* 白噪声序列, 即原时间序列只有趋势和季节性成分, 因此只需还原趋势和季节性的成分, 就可以对未来的序列做出预测. 这种情况在现实情况中非常罕见
* 时间序列的当前值显著依赖于若干个其他值(如过去值). 通常我们需要使用**ARIMA**模型对其进行建模

### 预测数据还原

假设我们建立了适当的模型, 对时间序列的未来进行了预测, 由于我们对原序列进行了多种方法的处理, 因此这里的预测是对处理后的**平稳序列**做出的预测, 要对应得到原时间序列的预测, 需要进行数据的**还原**工作.

* 差分还原

  首先是差分还原. 差分对应的反函数是**累加**, 即`Python`中的`np.cumsum`操作. 对于一阶差分:

  * 通过`np.cumsum`函数进行累加还原
  * 每个时间点再统一加上第一个时间点的值, 再把差分抛弃的第一个时间点的值拼接上

  这样我们就得到和还原后的序列.

  对于高阶差分, 我们只需要重复上面的两步对应的次数即可.
* 转换还原

  如果在一开始使用了`log`等非线性转换函数(如原序列为乘法模型, 使用`log`函数转换成加法模型), 还需要使用这个函数的反函数进行还原, `log`函数对应的就是指数函数.

## 参考资料

* [Complete guide to create a Time Series Forecast (with Codes in Python)](https://www.analyticsvidhya.com/blog/2016/02/time-series-forecasting-codes-python/)
* [时间序列分析](https://blog.csdn.net/pipisorry/article/details/62053938)
* [时间序列相关算法与分析步骤](https://blog.csdn.net/qq_33414271/article/details/79588126?utm_medium=referral)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://kerasnoone.gitbook.io/garnet/xi-fen-ling-yu/shi-jian-xu-lie/0x01-shi-jian-xu-lie-gai-nian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
