当前位置: 首页 > news >正文

第 3 篇:揭秘时间模式 - 时间序列分解

第 3 篇:揭秘时间模式 - 时间序列分解

在上一篇中,我们学会了如何加载、可视化时间序列数据,并了解了时间序列可能包含趋势 (Trend)季节性 (Seasonality)周期性 (Cyclicality)随机性 (Residual) 这些基本成分。但仅仅是“知道”还不够,我们希望能更深入地“看透”数据,把这些成分明确地分离出来

这就是时间序列分解 (Time Series Decomposition) 的目标:将原始时间序列拆解成其内在的组成部分。这就像给时间序列做 CT 扫描,帮助我们:

  • 更深入地理解数据驱动力: 长期增长是主要因素,还是季节性波动更显著?
  • 为建模做准备: 某些模型(如 ARIMA 的一些变种)可能需要先去除趋势或季节性成分。分解后的成分本身也可以作为模型的特征。
  • 改进预测: 分别预测趋势和季节性,然后组合起来,有时能得到更好的结果。

分解模型:加法 vs. 乘法

如何将这些成分组合起来得到原始序列?主要有两种基本模型:

  1. 加法模型 (Additive Model):

    • 公式: Y(t) = Trend(t) + Seasonal(t) + Residual(t)
    • 含义: 假设季节性成分的幅度是相对恒定的,不随时间趋势的变化而变化。它只是在趋势的基础上进行加减。
    • 适用场景: 季节性波动的大小不依赖于时间序列的水平。例如,某商店每天固定在午餐时间多卖出约 50 份快餐,无论当天总销量是 200 份还是 500 份。
    • 图形特点: 在时间序列图上,季节性波动的“摆动幅度”看起来大致相同。
  2. 乘法模型 (Multiplicative Model):

    • 公式: Y(t) = Trend(t) * Seasonal(t) * Residual(t)
    • 含义: 假设季节性成分的幅度与趋势成比例变化。季节性可以看作是趋势的一个百分比。
    • 适用场景: 季节性波动的大小随着时间序列水平的升高而增大(或随降低而减小)。例如,航空乘客数量,在整体乘客量较低的年份,旺季和淡季的差距可能只有几千人;但在乘客量高的年份,这个差距可能达到数万人。
    • 图形特点: 在时间序列图上,季节性波动的“摆动幅度”随着趋势的上升而变宽,随着趋势的下降而变窄。
    • 小技巧: 对乘法模型取对数 (log),可以将其转换为加法模型:log(Y(t)) ≈ log(Trend(t)) + log(Seasonal(t)) + log(Residual(t))。这在某些分析中很有用。

如何选择?

  • 观察图形: 这是最直观的方法。看季节性波动的幅度是恒定的(倾向于加法)还是随趋势变化的(倾向于乘法)。
  • 经验判断: 某些类型的数据(如大多数经济指标、销售额)通常更符合乘法模型。
  • 尝试与比较: 如果不确定,可以两种模型都尝试,看哪个分解结果的残差项更接近随机噪声。

如何进行分解? statsmodels 来帮忙

Python 的 statsmodels 库提供了一个非常方便的函数来进行经典的时间序列分解:seasonal_decompose

from statsmodels.tsa.seasonal import seasonal_decompose
import statsmodels.api as sm # 用于加载示例数据
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns# 设置全局绘图参数 (可选)
plt.rcParams.update({'figure.figsize': (10, 8)})
sns.set_style("whitegrid")# 1. 加载示例数据 (经典的 CO2 浓度数据)
# 这个数据集有明显的趋势和季节性,适合演示
data = sm.datasets.co2.load_pandas().data# 2. 数据预处理
# CO2 数据是周度数据,可能有缺失值,且频率不完全固定
# 我们将其重采样为月度数据 (取均值),并填充缺失值
data['co2'].interpolate(inplace=True) # 线性插值填充 NaN
monthly_data = data.resample('M').mean() # 按月重采样print("月度 CO2 数据概览:")
print(monthly_data.head())# 3. 进行分解
# 观察 CO2 浓度图 (你可以先画一下 monthly_data['co2'].plot())
# 会发现季节性波动随着整体趋势上升而略有增大,因此选择乘法模型更合适
# 季节性周期为 12 (月度数据,年度季节性)
decomposition_result = seasonal_decompose(monthly_data['co2'], model='multiplicative', period=12)# 4. 可视化分解结果
print("\n正在绘制分解图...")
fig = decomposition_result.plot()
plt.suptitle('Time Series Decomposition of Monthly CO2 Data (Multiplicative Model)', y=1.02) # 添加总标题
plt.tight_layout()
plt.show()# 我们可以单独访问每个成分
trend_component = decomposition_result.trend
seasonal_component = decomposition_result.seasonal
residual_component = decomposition_result.residprint("\n分解得到的趋势成分 (前几行):")
print(trend_component.head())
print("\n分解得到的季节性成分 (前几行):")
print(seasonal_component.head())
print("\n分解得到的残差成分 (前几行):")
print(residual_component.head())

在这里插入图片描述

代码解释:

  1. 加载数据: 我们使用了 statsmodels 自带的 datasets.co2 数据集。
  2. 预处理:
    • interpolate(): 填充数据中的缺失值(NaN),这里使用简单的线性插值。
    • resample('M').mean(): 将数据从原始频率(可能是周度)转换为月度频率,计算每个月的平均 CO2 浓度。这使得我们有一个固定的频率,便于设定 period
  3. seasonal_decompose:
    • 第一个参数是你要分解的时间序列(monthly_data['co2'])。
    • model='multiplicative': 指定使用乘法模型。如果你的数据更像加法,就用 model='additive'
    • period=12: 这是非常重要的参数!它指定了季节性的周期长度。因为我们是月度数据,并且 CO2 浓度有明显的年度季节性,所以周期是 12 个月。如果是日数据且有周季节性,period 就应设为 7。
  4. decomposition_result.plot(): 这个方法直接绘制出分解的四个部分。

解读分解结果图

seasonal_decompose 生成的图通常包含四个子图:

  1. Observed (原始数据): 就是我们输入的 monthly_data['co2']
  2. Trend (趋势成分): 显示了数据的长期平滑走向。注意,在序列的开始和结束部分,趋势线可能无法计算出来(显示为灰色或缺失),这是经典分解方法(基于移动平均)的一个局限性。对于 CO2 数据,我们可以看到一个明显的长期上升趋势。
  3. Seasonal (季节性成分): 显示了固定的季节性波动模式。对于乘法模型,这个值通常在 1 附近波动;对于加法模型,在 0 附近波动。你应该能看到一个每年重复的模式(对应 period=12)。对于 CO2 数据,每年都有一个峰值和一个谷值。
  4. Residual (残差成分): 这是从原始数据中移除趋势和季节性成分后剩下的部分 Residual = Observed / (Trend * Seasonal) (乘法) 或 Residual = Observed - Trend - Seasonal (加法)。理想情况下,残差应该看起来像随机噪声,没有明显的模式。如果残差中还存在明显的模式,可能意味着分解模型选择不当,或者数据中还包含其他未被捕捉的结构(如周期性成分或异常事件)。

讨论与局限性

  • seasonal_decompose 是一种比较基础的分解方法,它主要基于移动平均来估计趋势和季节性。
  • 端点问题: 如上所述,由于移动平均的计算方式,序列两端的数据点可能无法很好地估计出趋势和季节性成分(结果可能是 NaN)。
  • 季节性假设: 它假设季节性模式是固定不变的。如果季节性模式随时间演变,这种方法可能效果不佳。
  • 对异常值敏感: 移动平均对数据中的极端值(异常点)比较敏感。

尽管有这些局限性,seasonal_decompose 对于快速理解数据结构、检查趋势和季节性模式仍然是一个非常有用的入门工具。更高级的分解方法包括 STL (Seasonal and Trend decomposition using Loess)、X-13-ARIMA-SEATS 等,但它们超出了我们入门系列的范围。

小结

今天,我们深入了解了时间序列分解:

  • 知道了分解的目标是将序列拆分为趋势季节性残差
  • 学习了加法乘法两种分解模型,以及如何根据数据特点选择。
  • 掌握了使用 Python statsmodels.tsa.seasonal_decompose 函数进行实际分解操作,并理解了 modelperiod 参数的重要性。
  • 学会了解读分解结果图,从趋势、季节性和残差图中获取信息。
  • 了解了经典分解方法的一些局限性。

下一篇预告

通过分解,我们能清晰地看到序列中的趋势和季节性。然而,很多经典的时间序列模型(比如后面可能会接触的 ARIMA)都要求数据是平稳 (Stationary) 的——简单来说,就是数据的统计特性(如均值、方差)不随时间改变。而带有明显趋势或季节性的序列通常是非平稳的。

那么,什么是平稳性?为什么它如此重要?如何判断一个序列是否平稳?如果它不平稳,我们又该如何处理呢?下一篇,我们将探讨时间序列分析的基石——平稳性

敬请期待!


(分解 CO2 数据时,你选择了乘法模型。如果尝试用加法模型,结果图会有什么不同?欢迎在评论区分享你的发现!)

相关文章:

  • 【显卡占用】kill程序后,显卡仍被占用
  • 高效Java面试题(附答案)
  • 【C++篇】string类的终章:深浅拷贝 + 模拟实现string类的深度解析(附源码)
  • uCOS3实时操作系统(系统初始化和任务启动)
  • 《Learning Langchain》阅读笔记5-RAG(1)
  • 7. 服务通信 ---- 使用自定义srv,服务方和客户方cpp,python文件编写
  • MATLAB 训练CNN模型 yolo v4
  • 强化学习框架verl源码学习-快速上手之如何跑通PPO算法
  • Linux学习笔记协议篇(六):SPI FLASH设备驱动
  • 嵌入式人工智能应用-第三章 opencv操作8 图像特征之HOG 特征
  • 网络原理 - 3(UDP 协议)
  • 读文献先读图:韦恩图怎么看?
  • 设备、管道绝热(保冷)设计计算
  • Flutter路由模块化管理方案
  • 文件包含漏洞,目录遍历漏洞,CSRF,SSRF
  • 深度解析云计算:概念、优势与分类全览
  • 爬虫获取sku信息需要哪些库
  • 用银河麒麟 LiveCD 快速查看原系统 IP 和打印机配置
  • 网页下载的m3u8格式文件使用FFmpeg转为MP4
  • three.js中的instancedMesh类优化渲染多个同网格材质的模型
  • 天地图新版上线对公众、企业有何用?自然资源部总规张兵详解
  • 解放日报:128岁的凤凰自行车“双轮驱动”逆风突围
  • 中印尼“2+2”:中国周边外交的范式突破与东南亚棋局重构
  • 张巍|另眼看古典学⑩:再创作让古希腊神话重获生机——重述厄勒克特拉
  • 硅基世界的“缘分”——系统与人工智能携手进化
  • 10亿美元拿下加纳金矿!“矿茅”紫金矿业黄金板块突围战再下一城