3. 时间序列基础:平稳性检验、自相关与偏自相关、差分操作
各位同学,咱们今天聊点硬核的。时间序列分析,说白了就是跟历史数据对话,让它告诉我们未来可能发生什么。但这里有个大坑——如果数据本身不老实,你建的模型就是空中楼阁。
我个人习惯,拿到任何时间序列数据,第一件事不是跑模型,而是先做三件事:平稳性检验、看自相关结构、必要时做差分。这三板斧砍下去,数据的基本脾气你就摸透了。
3.1 平稳性:时间序列的「定海神针」
什么叫平稳?简单说,就是数据的统计性质不随时间变化。均值是常数,方差是常数,自相关结构也是稳定的。
你想想看,如果数据本身均值一直在漂移,你拿什么去预测未来?就像在移动的火车上测距离,永远测不准。
严平稳: 联合分布不随时间平移而变化。这个条件太强,现实中几乎不存在。
弱平稳(宽平稳): 均值恒定、方差恒定、自协方差只与时间间隔有关。我们平时说的「平稳」默认指这个。
我在项目中遇到过最典型的非平稳序列——GDP数据。你看它长期趋势是向上的,均值明显在变化。这种数据直接建模,结果就是伪回归,看着R²很高,其实全是假象。
3.2 平稳性检验:别靠肉眼,上统计量
很多人喜欢画个图,凭感觉说「嗯,这个看起来平稳」。千万别!我吃过这个亏。有一次我对着一个看起来挺平的序列建了ARIMA模型,回测效果奇好,结果实盘一跑就崩。后来一查,那序列是随机游走,压根不平稳。
所以,老老实实用检验方法:
3.2.1 ADF 检验(增广迪基-富勒检验)
这是最常用的单位根检验。原假设是「序列存在单位根(非平稳)」。p值小于0.05,就拒绝原假设,认为序列平稳。
from statsmodels.tsa.stattools import adfuller
import pandas as pd
# 假设 data 是 Series
result = adfuller(data, autolag='AIC')
print(f'ADF统计量: {result[0]:.4f}')
print(f'p值: {result[1]:.4f}')
print(f'临界值: {result[4]}')
if result[1] < 0.05:
print('✅ 拒绝原假设,序列平稳')
else:
print('❌ 无法拒绝原假设,序列非平稳')
3.2.2 KPSS 检验
这个跟ADF正好反过来——原假设是「序列平稳」。所以两个检验配合使用,就像双保险。
from statsmodels.tsa.stattools import kpss
result_kpss = kpss(data, regression='c')
print(f'KPSS统计量: {result_kpss[0]:.4f}')
print(f'p值: {result_kpss[1]:.4f}')
if result_kpss[1] < 0.05:
print('❌ 拒绝原假设,序列非平稳')
else:
print('✅ 无法拒绝原假设,序列平稳')
3.3 自相关与偏自相关:读懂数据的「记忆」
时间序列有个特点——过去会影响现在。今天的股价跟昨天有关,今天的温度跟昨天有关。这种「记忆」就是自相关。
3.3.1 自相关函数(ACF)
ACF 衡量的是当前值与其滞后值之间的相关性。比如 lag=1 的ACF,就是今天和昨天的相关系数。
嗯,这里要注意:ACF 会受中间滞后项的影响。比如今天和前天相关,可能是因为今天和昨天相关、昨天和前天相关导致的「传递效应」。所以我们需要 PACF。
3.3.2 偏自相关函数(PACF)
PACF 剔除了中间变量的影响,直接看「纯」的相关性。比如 lag=2 的PACF,就是剔除了 lag=1 的影响后,今天和前天的直接关系。
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))
plot_acf(data, lags=20, ax=ax1)
ax1.set_title('自相关函数 (ACF)')
plot_pacf(data, lags=20, ax=ax2, method='ywm')
ax2.set_title('偏自相关函数 (PACF)')
plt.tight_layout()
plt.show()
- ACF 拖尾(缓慢衰减)+ PACF 截尾(p阶后突降)→ AR(p) 模型
- ACF 截尾(q阶后突降)+ PACF 拖尾 → MA(q) 模型
- 两者都拖尾 → ARMA 模型
- 两者都截尾 → 可能数据是白噪声,没啥可建模的
我记得有一次做CPI预测,ACF在lag=12处有个明显的尖峰,我当时就意识到——这是季节性。后来加了12阶差分,模型效果直接提升了一个档次。所以看ACF图的时候,别光看前几阶,远一点的滞后也可能藏着重要信息。
3.4 差分操作:让不平稳变平稳的「手术刀」
如果数据不平稳,最直接的解决办法就是差分。说白了,就是把「数值」变成「变化量」。
3.4.1 一阶差分
今天的值减去昨天的值。如果原始数据是随机游走,一阶差分后就是白噪声。
# 一阶差分
diff_1 = data.diff().dropna()
# 再检验一下平稳性
result_diff = adfuller(diff_1)
print(f'差分后p值: {result_diff[1]:.4f}')
3.4.2 季节性差分
对于月度数据,用今年1月减去年1月。对于季度数据,用今年Q1减去年Q1。
# 季节性差分(以12期为例)
seasonal_diff = data.diff(12).dropna()
3.4.3 如何确定差分阶数?
我个人习惯用两步法:
- 先看ACF图: 如果ACF衰减很慢(比如10阶后还有显著相关),大概率需要差分
- 再用单位根检验: ADF和KPSS交叉验证,直到p值通过
其实还有一个更自动化的方法——用 pmdarima 库的 ndiffs 函数,它会帮你算最优差分阶数。
from pmdarima.arima import ndiffs
# 自动确定差分阶数
d = ndiffs(data, test='adf')
print(f'建议差分阶数: {d}')
3.5 实战流程总结
好了,把今天的内容串起来。我拿到一个时间序列,一般走这个流程:
| 步骤 | 操作 | 目的 |
|---|---|---|
| 1 | 画时序图 + ADF/KPSS检验 | 判断是否平稳 |
| 2 | 如果不平稳,做差分(先1阶,再季节性) | 转化为平稳序列 |
| 3 | 画ACF和PACF图 | 识别AR/MA阶数 |
| 4 | 根据ACF/PACF特征选择模型 | 确定p、q参数 |
你想想看,这套流程走下来,数据的基本特征就全在你手里了。后面建什么模型,心里都有底。
嗯,今天就到这儿。记住一句话:平稳性是时间序列建模的基石,差分是处理非平稳的利器,ACF/PACF是识别模型结构的眼睛。这三样东西玩熟了,时间序列分析你就入门了。