4、时序数据分析基础:时间序列分解(趋势、季节、残差)、自相关分析、平稳性检验
各位同学,咱们今天聊点实在的。做削峰填谷,说白了就是跟时间序列打交道。你连数据的基本规律都摸不透,怎么去削?怎么去填?
我个人习惯,拿到任何时序数据,第一件事不是建模,而是先做三件事:分解、看相关性、测平稳性。这三板斧下去,数据的大致脾气你就摸透了。
4.1 时间序列分解:把数据拆开看
时间序列分解,就是把一个复杂的时间序列,拆成几个简单的部分。我一般拆成三块:趋势、季节、残差。
- 趋势(Trend):长期来看,数据是往上走还是往下走?比如用电量,随着经济发展,整体趋势是上升的。
- 季节(Seasonal):固定周期内的波动。比如一天24小时,用电高峰在白天;一年四季,夏天用电多。
- 残差(Residual):去掉趋势和季节后剩下的随机波动。说白了,就是“运气成分”或者“异常事件”。
核心公式:
加法模型:Y(t) = Trend(t) + Seasonal(t) + Residual(t)
乘法模型:Y(t) = Trend(t) × Seasonal(t) × Residual(t)
我建议,如果季节波动幅度随趋势变化,用乘法模型;如果波动幅度稳定,用加法模型。
我在项目中遇到过,有个同学直接用加法模型去拟合用电量数据,结果残差越来越大。为什么?因为夏天用电量基数大,波动自然大,乘法模型才合适。
4.1.1 动手分解一下
Python里用 statsmodels 库,几行代码就搞定。我给你们写个例子:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose
# 生成模拟数据:趋势上升 + 季节波动 + 随机噪声
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=365, freq='D')
trend = np.linspace(100, 200, 365) # 线性上升趋势
seasonal = 10 * np.sin(2 * np.pi * np.arange(365) / 7) # 周周期
residual = np.random.normal(0, 2, 365)
data = trend + seasonal + residual
# 分解
result = seasonal_decompose(data, model='additive', period=7)
# 画图
fig, axes = plt.subplots(4, 1, figsize=(12, 8))
axes[0].plot(data, label='原始数据')
axes[0].legend()
axes[1].plot(result.trend, label='趋势', color='orange')
axes[1].legend()
axes[2].plot(result.seasonal, label='季节', color='green')
axes[2].legend()
axes[3].plot(result.resid, label='残差', color='red')
axes[3].legend()
plt.tight_layout()
plt.show()
小提示:分解时 period 参数很关键。如果是日数据,周周期设7;如果是小时数据,日周期设24。设错了,分解出来的季节成分就是错的。
4.2 自相关分析:数据跟自己“攀亲戚”
自相关,说白了就是看当前时刻的数据,跟过去某个时刻的数据有没有关系。比如今天下午3点的用电量,跟昨天下午3点的用电量,是不是很像?
我常用两个工具:ACF(自相关函数)和PACF(偏自相关函数)。
- ACF:衡量当前值跟所有滞后值之间的相关性。比如今天跟昨天、前天、大前天的关系。
- PACF:剔除中间变量后,当前值跟特定滞后值之间的“纯”相关性。比如今天跟大前天的关系,但要去掉昨天和前天的影响。
你想想看,如果ACF图衰减很慢,说明数据有很强的趋势性,大概率不平稳。如果ACF在某个滞后阶数后突然截尾,那可能就是季节周期的长度。
4.2.1 画个ACF图看看
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
# 画ACF和PACF
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
plot_acf(data, lags=30, ax=axes[0])
plot_pacf(data, lags=30, ax=axes[1])
plt.show()
避坑指南:我曾经在分析一个流量数据时,ACF图显示所有滞后阶数都显著,我以为周期很长。后来才发现,是因为数据有缺失值,插值后引入了虚假的自相关。所以,先处理缺失值,再做自相关分析。
4.3 平稳性检验:数据“稳不稳”
平稳性,是时间序列建模的前提。什么叫平稳?简单说,就是数据的统计性质(均值、方差)不随时间变化。
为什么要检验平稳性?因为很多模型(比如ARIMA)要求数据平稳。不平稳的数据,模型会学出虚假的规律。
我常用的检验方法是ADF检验(Augmented Dickey-Fuller Test)。
- 原假设H0:序列存在单位根(不平稳)。
- 备择假设H1:序列平稳。
- 判断标准:p值 < 0.05,拒绝原假设,认为序列平稳。
4.3.1 代码实现
from statsmodels.tsa.stattools import adfuller
# ADF检验
result = adfuller(data)
print(f'ADF统计量: {result[0]:.4f}')
print(f'p值: {result[1]:.4f}')
print(f'临界值:')
for key, value in result[4].items():
print(f' {key}: {value:.4f}')
if result[1] < 0.05:
print('结论:序列平稳')
else:
print('结论:序列不平稳,需要差分')
输出示例:
| 指标 | 值 |
|---|---|
| ADF统计量 | -3.4567 |
| p值 | 0.0098 |
| 1%临界值 | -3.4325 |
| 5%临界值 | -2.8624 |
| 10%临界值 | -2.5672 |
p值0.0098 < 0.05,拒绝原假设,序列平稳。
嗯,这里要注意。如果数据不平稳,怎么办?差分。一阶差分不行就二阶差分。但别差太多,否则会丢失信息。我一般最多差到二阶。
4.4 知识体系总览
为了让你更直观地理解这三块内容的关系,我画了张图:
这张图把今天讲的内容串起来了。你从时序数据出发,先做分解,再看自相关,最后测平稳性。每一步都有对应的工具和方法。
我的习惯:在实际项目中,我会先画一张这样的知识地图,然后对着地图一步步执行。这样不容易漏掉关键步骤。
好了,今天的内容就到这里。记住,时序数据分析不是一蹴而就的,多动手、多画图、多思考。你把这些基础打牢了,后面做削峰填谷模型,自然水到渠成。
公众号:蓝海资料掘金营,微信deep3321