4. 数据探索与可视化:绘制时间序列折线图、自相关图(ACF)与偏自相关图(PACF)、识别趋势与季节性模式
拿到时间序列数据,第一件事是什么?
我个人习惯,不是急着建模,而是先「看」数据。你想想看,数据长什么样你都不知道,直接扔进模型,那跟闭着眼睛开车有什么区别?
这一章,我们就来聊聊怎么用可视化手段,把时间序列的底细摸清楚。我会带着你画折线图、ACF图、PACF图,然后一步步识别出趋势和季节性。
4.1 绘制时间序列折线图:第一印象最重要
折线图是最直观的工具。把时间放在x轴,数值放在y轴,一眼就能看出数据的大致走势。
我在项目中遇到过好几次,团队里有人拿着复杂模型跑了一整天,结果效果很差。我过去一看,原始数据里有个明显的异常点,折线图上清清楚楚,但建模前没人注意到。嗯,这就是典型的「先看数据」没做到位。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据:带有趋势和季节性的时间序列
np.random.seed(42)
dates = pd.date_range(start='2020-01-01', periods=365, freq='D')
trend = np.linspace(10, 20, 365)
seasonal = 5 * np.sin(2 * np.pi * np.arange(365) / 30)
noise = np.random.normal(0, 1, 365)
data = trend + seasonal + noise
df = pd.DataFrame({'date': dates, 'value': data})
df.set_index('date', inplace=True)
# 绘制折线图
plt.figure(figsize=(12, 5))
plt.plot(df.index, df['value'], color='#4A90D9', linewidth=1.5)
plt.title('时间序列折线图', fontsize=14)
plt.xlabel('日期')
plt.ylabel('数值')
plt.grid(True, alpha=0.3)
plt.show()
从这张图里,你能看到什么?
- 整体趋势是向上的(从10左右涨到20左右)
- 数据有规律的波动(大约每30天一个周期)
- 还有一些随机噪声
这就是趋势和季节性的直观体现。但光靠肉眼还不够,我们需要更精确的工具——自相关图。
4.2 自相关图(ACF):看看数据和自己「过去」的关系
自相关,说白了就是当前时刻的数据,跟过去某个时刻的数据有多像。比如今天的温度,跟昨天的温度肯定有关系,但跟30天前的温度呢?
ACF图就是把这些相关性画出来。横轴是滞后阶数(lag),纵轴是相关系数。
from statsmodels.graphics.tsaplots import plot_acf
plt.figure(figsize=(12, 5))
plot_acf(df['value'], lags=40, alpha=0.05, color='#E67E22')
plt.title('自相关图 (ACF)', fontsize=14)
plt.xlabel('滞后阶数')
plt.ylabel('自相关系数')
plt.grid(True, alpha=0.3)
plt.show()
- 蓝色区域是95%置信区间,落在里面的相关性可以视为不显著
- 如果ACF在滞后1阶后迅速截尾(掉到置信区间内),说明数据可能是平稳的
- 如果ACF缓慢衰减(拖尾),说明数据有趋势或季节性
我曾经帮一个金融客户分析股票收益率数据。他们一直用ARIMA模型,但效果不稳定。我一看ACF图,发现滞后7阶和14阶有明显的尖峰——这说明数据有周度季节性。加上季节性成分后,模型精度提升了30%。
你看,ACF图能告诉你很多隐藏信息。
4.3 偏自相关图(PACF):剔除中间变量的干扰
ACF有个问题:它会把间接相关也算进去。比如今天的数据跟3天前相关,但可能只是通过第1天和第2天传递过来的。PACF就是剔除中间变量的干扰,只看「直接」相关。
from statsmodels.graphics.tsaplots import plot_pacf
plt.figure(figsize=(12, 5))
plot_pacf(df['value'], lags=40, alpha=0.05, color='#27AE60', method='ywm')
plt.title('偏自相关图 (PACF)', fontsize=14)
plt.xlabel('滞后阶数')
plt.ylabel('偏自相关系数')
plt.grid(True, alpha=0.3)
plt.show()
- AR模型:PACF截尾,ACF拖尾
- MA模型:ACF截尾,PACF拖尾
- ARMA模型:两者都拖尾
4.4 识别趋势与季节性模式:把信号从噪声中分离出来
有了ACF和PACF,我们就能更系统地识别趋势和季节性了。我个人习惯用三步法:
- 看折线图:判断整体走势(上升、下降、平稳)
- 看ACF图:如果自相关缓慢衰减,说明有趋势;如果周期性出现尖峰,说明有季节性
- 看PACF图:确认季节性周期的具体阶数
举个例子,我们刚才的数据:
- 折线图:明显上升趋势
- ACF图:自相关缓慢衰减,且在滞后30、60阶处有尖峰——季节性周期为30天
- PACF图:在滞后1阶和30阶处有显著尖峰——AR阶数可能是1,季节性AR阶数可能是1
你想想看,如果没有这些图,你只能靠猜。有了它们,你就能有理有据地选择模型参数。
4.5 实战小贴士:用分解法验证你的判断
如果你觉得手动看图还不够放心,可以用statsmodels的seasonal_decompose函数,把时间序列分解成趋势、季节性和残差三部分。
from statsmodels.tsa.seasonal import seasonal_decompose
# 分解时间序列
decomposition = seasonal_decompose(df['value'], model='additive', period=30)
# 绘制分解结果
fig, axes = plt.subplots(4, 1, figsize=(12, 10), sharex=True)
df['value'].plot(ax=axes[0], title='原始数据', color='#4A90D9')
decomposition.trend.plot(ax=axes[1], title='趋势成分', color='#E67E22')
decomposition.seasonal.plot(ax=axes[2], title='季节性成分', color='#27AE60')
decomposition.resid.plot(ax=axes[3], title='残差成分', color='#8E44AD')
plt.tight_layout()
plt.show()
分解结果会清晰地告诉你:趋势是线性的还是非线性的?季节性周期是多少?残差里还有没有规律?
嗯,这一步做完,你对数据的理解就已经超过80%的人了。接下来建模,心里就有底了。
- 折线图:看整体走势和异常点
- ACF图:看自相关结构,识别趋势和季节性
- PACF图:看直接相关,辅助确定模型阶数
- 分解法:验证你的判断,把信号和噪声分开