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()
小技巧:画折线图时,我建议把线条调细一点(linewidth=1.5左右),太粗会掩盖细节。另外,网格线透明度设低一些,别抢了数据本身的风头。

从这张图里,你能看到什么?

  • 整体趋势是向上的(从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()
注意:PACF的解读和ACF是配合使用的。一般来说:
  • AR模型:PACF截尾,ACF拖尾
  • MA模型:ACF截尾,PACF拖尾
  • ARMA模型:两者都拖尾
这个规律在确定模型阶数时非常有用。

4.4 识别趋势与季节性模式:把信号从噪声中分离出来

有了ACF和PACF,我们就能更系统地识别趋势和季节性了。我个人习惯用三步法:

  1. 看折线图:判断整体走势(上升、下降、平稳)
  2. 看ACF图:如果自相关缓慢衰减,说明有趋势;如果周期性出现尖峰,说明有季节性
  3. 看PACF图:确认季节性周期的具体阶数

举个例子,我们刚才的数据:

  • 折线图:明显上升趋势
  • ACF图:自相关缓慢衰减,且在滞后30、60阶处有尖峰——季节性周期为30天
  • PACF图:在滞后1阶和30阶处有显著尖峰——AR阶数可能是1,季节性AR阶数可能是1

你想想看,如果没有这些图,你只能靠猜。有了它们,你就能有理有据地选择模型参数。

避坑指南:我曾经犯过一个错误——看到ACF图里有很多尖峰,就以为季节性周期很短。后来才发现,那是数据预处理没做好,有缺失值导致的伪相关。所以,画图之前,一定要先处理缺失值和异常值。

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图:看直接相关,辅助确定模型阶数
  • 分解法:验证你的判断,把信号和噪声分开
记住,数据探索不是走过场,它是整个建模流程的基石。花时间把这一步做好,后面会省很多事。