首页/新闻资讯/正文详情

趋势与季节性时间序列预测:从STL分解到SARIMA建模实战

发布时间:2026/9/23 2:36:11 来源:云帆数科 栏目:资讯中心
趋势与季节性时间序列预测:从STL分解到SARIMA建模实战
简介面向有一定Python基础、希望掌握气候数据预测的时间序列分析初学者这套实战内容围绕趋势与季节性两个核心维度结合Pandas、statsmodels、Matplotlib等常用库系统演示了移动平均提取趋势、STL季节分解、ARIMA/SARIMA建模、预测效果评估等多个关键环节并强调了数据切分与误差指标的理解。包体约2.95MB共9个文件以3个Jupyter Notebook案例为主配有一个每日最低气温csv数据集和少量IDE配置打开后可按案例顺序运行逐步复现时间序列分析流程。目前已有528人学习下载资源同时覆盖温度数据集与合成数据集便于对照不同趋势和季节强度下模型的表现。通过动手操作读者不仅能掌握时间序列预测的通用流程还能学习SARIMA参数调优、MSE/RMSE/MAE等误差计算以及网格搜索等优化方法为气温、降雨等气象预测场景提供可复用的分析模板。1. 基于趋势和季节性的时间序列预测这份 rar 里的完整建模路线拿到一份时间序列预测的实战资源最怕的是它上来就让你调 LSTM调完也不知道结果为什么长这样。这份基于趋势和季节性的时间序列预测实战 rar 走的完全是另一条路线先用合成数据把趋势和季节性两个核心成分讲透再用一份真实的逐日气温数据跑通建模全流程。它解决的是气候预测里最经典的那类问题——数据有明显年度周期想预测未来走势又不希望结果是个黑匣子。适合刚做完入门课程、想动手复现完整流程的人也适合用统计模型做环境、能源、零售销量预测的从业者。整份资源不依赖深度学习框架核心工具就是 pandas、statsmodels 和 matplotlib。2. 拆开资源看门道两个 Notebook、一份气象数据与趋势/季节性分解2.1 解压后先分清主次哪些文件值得看哪些是 IDE 留下的噪音一个 rar 解压之后别急着打开 ipynb 就跑。这份资源里同时混着两类东西真正要研究的建模代码和 PyCharm 打开项目时自动生成的工程文件。如果不先分清主次很容易把注意力浪费在无关文件上。路径类型用途与优先级modeling_synthetic_dataset.ipynbJupyter Notebook合成数据上演示趋势与季节性建模核心modeling_temperature_dataset.ipynbJupyter Notebook真实气温数据全流程建模核心data/daily-minimum-temperatures.csvCSV墨尔本 1981-1990 逐日最低气温核心数据.idea/ 、*.iml 、workspace.xml 等IDE 配置PyCharm 解析项目用的缓存可忽略.ipynb_checkpoints/Jupyter 缓存Notebook 自动保存的副本可忽略这份资源在结构设计上有个很实用的安排它刻意用两个 Notebook 把「原理」和「实战」分开。合成数据 Notebook 里数据是人为构造的趋势项和季节项在生成时就已知你在结果里能精确看到分解是否还原了真相真实数据 Notebook 里再套用同一套流程从读 CSV 到出预测图全部走一遍。这种「先验已知 → 未知实测」的学习顺序比直接拿真实数据硬调参稳得多——当结果不对劲时你能判断题出在模型还是出在数据。把气象数据读进来最顺手的方式是这样import pandas as pd df pd.read_csv( data/daily-minimum-temperatures.csv, parse_dates[Date], index_colDate, ) df.columns [temp] print(df.head()) print(df.index.min(), df.index.max(), len(df))这里parse_dates[Date]把日期列解析成DatetimeIndexindex_col让时间直接成为索引这是 pandas 时间序列管线的第一步。打印出的首末日期和行数能快速确认数据范围与量级避免后面建模时才发现索引类型不对。如果你的本地 CSV 编码和资源里的不一致读取时可能报UnicodeDecodeError这是 Python 项目常见的开局问题加encodingutf-8多半能解决。2.2 趋势提取滚动均值并不是万能的趋势线趋势识别最常见的手段是移动平均pandas 里一句话就能算window 365 # 窗口对齐到一年把季节波动平均掉 df[trend_365] df[temp].rolling(windowwindow, min_periods30).mean() df[trend_30] df[temp].rolling(window30).mean()window365的滚动均值在气温数据里意味着把一整年的日温度取平均冬季低温和夏季高温在窗口内互相抵消留下来的就是年际之间的长期变动。min_periods30允许窗口开头部分不完整时也输出有效值减少头部 NaN 的损失。我一般不用窗口太短的均值代表趋势比如 30 天窗口对日温度来说更多是短期噪声平滑谈不上趋势。趋势的定义和季节周期是绑定的趋势窗口至少要覆盖一个完整季节周期这是新手最容易踩到的概念误区之一。另外滚动均值自带边界问题序列头尾各会损失约 window/2 长度的数据而预测任务恰恰关心尾部所以滚动均值只适合观察趋势形态不适合直接外推。更推荐的做法是把它当诊断工具真正建模时用 STL 分解输出的趋势分量或者把趋势直接交给 ARIMA/SARIMA 去拟合。2.3 STL 分解把序列拆成趋势、季节、残差三层要拆开季节性statsmodels 的 STL 是当前最常用的选择from statsmodels.tsa.seasonal import STL stl STL( df[temp], period365, # 季节周期逐日气象数据取 365 robustTrue, # 对极端气温鲁棒 seasonal13, # 季节分量平滑窗长奇数越大季节曲线越平滑 ) res stl.fit() res.plot()STLSeasonal-Trend decomposition using LOESS用局部加权回归把序列拆成res.trend、res.seasonal、res.resid三个分量。period365告诉算法季节循环的长度robustTrue让寒潮、热浪这类极端温度值不至于把局部分量拉偏这在气象数据里很实用。这里有个细节经常让人绕晕STL接收的是period而不是seasonal周期数。seasonal参数反而是季节分量的平滑窗长必须是奇数。窗口太小季节分量会追着噪声跑窗口太大季节分量又会把趋势的渐变吞掉。对逐日气温数据一般从seasonal13试到seasonal31。分解完成后res.resid的长度会因 LOESS 拟合的边界效应而缩短拿去跑检验统计量时自由度对不上是正常的不必惊慌。STL 对缺失值很敏感分解前务必确认序列没有 NaN否则fit()会直接报错或给出异常分量。3. 从平稳性到 SARIMA把日最低气温预测跑通的核心三步3.1 平稳性检查ADF 检验通过不代表万事大吉ARIMA 类模型的基本前提是把序列处理成平稳的至少是趋势平稳的所以第一步是检验平稳性。from statsmodels.tsa.stattools import adfuller res_adf adfuller(df[temp].dropna()) print(fADF statistic {res_adf[0]:.3f}, p-value {res_adf[1]:.4f}) if res_adf[1] 0.05: print(拒绝单位根原假设可视为平稳序列) else: print(无法拒绝单位根需要差分)adfuller的 p 值小于 0.05说明在 5% 显著性水平下可以拒绝「存在单位根」的零假设序列可视为平稳。但这条结论在气温数据上特别容易误导人日最低气温有很强的年度季节性ADF 检验只检测普通单位根并不会因为季节循环就必然判非平稳。你完全可能得到 p0.00 的结果然后直接去拟合一个不带季节项的 ARIMA最后预测出一条缓慢漂移的直线。所以我的习惯是对气象数据这类有明显周期的序列ADF 检验只用来决定差分阶数 d季节性阶数 D 要单独判断。判断方法有两个——一是看 STL 分解出来的 seasonal 分量振幅是否稳定二是对序列做季节性差分后再跑一次 ADF。若季节性差分后显著平稳说明D1是有必要的。3.2 时间序列分割不要碰随机 shuffle训练模型之前先把数据切分好。时间序列切分和普通机器学习最大的差别是必须保证时间连续性不能用随机抽样否则未来信息会泄漏进训练集让指标虚高。def split_time_series_sets(df, train_ratio0.8, valid_ratio0.1): n len(df) train_end int(n * train_ratio) valid_end int(n * (train_ratio valid_ratio)) train df.iloc[:train_end] valid df.iloc[train_end:valid_end] test df.iloc[valid_end:] return train, valid, test这个函数按行占比切出训练、验证、测试三段默认 8:1:1。注意参数含义train_ratio0.8不是随机抽 80% 的数据而是取前 80% 的时间范围valid_ratio0.1是紧邻其后的 10%。我一般会把验证集和测试集都设成最后一段连续时间因为预测业务天然关心的是「最近的未来」而不是「中间的某一段」。如果你在别人的代码里看到对时间序列用train_test_split(..., shuffleTrue)要警惕指标虚高。时间序列的自相关结构决定了相邻样本高度相似随机切分会让模型在训练时提前见到验证集附近的点验证 RMSE 会明显比真实滚动预测乐观。3.3 拟合 SARIMA日粒度数据别硬跑现在到了核心部分——用 SARIMA 建模。这里有个几乎每个人都会遇到的效率问题日粒度数据配上seasonal_order里的s365状态空间模型的规模会膨胀到难以拟合。温度序列的年度周期是 365但 SARIMA 的季节周期参数不只是「一个周期长度」它直接决定模型内部状态的维度。我一般先把数据降到月粒度再建模这样 s12训练时间和模型规模都在合理范围。# 重采样到月粒度用月均最低温 monthly df[temp].resample(M).mean().dropna() from statsmodels.tsa.statespace.sarimax import SARIMAX model SARIMAX( monthly, order(1, 1, 0), # 非季节 (p, d, q) seasonal_order(1, 1, 0, 12), # 季节 (P, D, Q, s) enforce_stationarityFalse, enforce_invertibilityFalse, ) res model.fit(dispFalse) print(res.summary())order(1,1,0)表示非季节部分用 1 阶自回归和 1 阶差分不含滑动平均项seasonal_order(1,1,0,12)表示季节部分同样用 1 阶自回归、1 阶季节差分季节周期为 12 个月。enforce_stationarityFalse和enforce_invertibilityFalse是给参数估计留余地避免在搜索边界上直接报错但正式报告模型时我会改回 True 再跑一遍确认。dispFalse关闭拟合过程的迭代日志做网格搜索时几十个模型的日志会把 Notebook 输出区淹没。提示pandas 2.2 之后resample(M)会给出弃用警告新写法是resample(ME)语义相同。拟合之后预测分两步先用get_forecast拿到未来 12 个月的预测和置信区间再作图。import matplotlib.pyplot as plt fc res.get_forecast(steps12) pred fc.predicted_mean ci fc.conf_int(alpha0.05) plt.figure(figsize(10, 4)) plt.plot(monthly.index, monthly.values, labelobserved, colortab:blue) plt.plot(pred.index, pred.values, labelforecast, colortab:red) plt.fill_between( pred.index, ci.iloc[:, 0], ci.iloc[:, 1], colortab:red, alpha0.15, ) plt.legend() plt.show()get_forecast(steps12)产生的是多步滚动预测注意它不是重新估计模型而是用已拟合的参数迭代推出 12 个点。conf_int(alpha0.05)给出 95% 置信区间温度数据预测 12 个月后区间会很宽这是正常的——它提醒你点预测只是条件均值不是确定答案。有一点要特别说明如果把steps拉长到 24 或 36预测曲线会逐渐收敛到序列的条件均值附近这是 ARIMA 类模型的固有行为不是代码写错了。想验证模型的真实预测能力要去看验证集上的滚动预测而不是盯着一张外推图感叹曲线太平。4. SARIMA 调参与避坑网格搜索范围与四条高频踩坑记录4.1 网格搜索先固定 D再把搜索范围压缩到 81 个组合以内SARIMA 参数表面上看只有 6 个p,d,q,P,D,Q但 d 和 D 一旦定了剩下的 4 个参数每个在 0~2 之间取就是 81 个组合。如果取 0~3就是 256 个组合配合月粒度数据每个模型拟合十几秒一个网格搜下来一小时就没了。我的习惯是先固定差分阶数再搜小范围。import itertools import warnings warnings.filterwarnings(ignore) best_aic, best_cfg float(inf), None for p, q, P, Q in itertools.product(range(3), repeat4): try: m SARIMAX( monthly, order(p, 1, q), seasonal_order(P, 1, Q, 12), enforce_stationarityFalse, enforce_invertibilityFalse, ).fit(dispFalse) if m.aic best_aic: best_aic, best_cfg m.aic, (p, q, P, Q) except Exception: continue print(fbest AIC {best_aic:.2f}, best (p,q,P,Q) {best_cfg})这里用itertools.product(range(3), repeat4)遍历 p、q、P、Q 在 0、1、2 之间的所有组合d 和 D 固定为 1。try/except是为了跳过参数估计时数值发散导致LinAlgError的组合——这不是掩盖问题而是网格搜索里常见的防御性写法。真正出报告时我会把所有失败的组合单独列出来看是不是集中在某个参数区间。选模型的标准用 AIC 而不是 R²。AIC 惩罚参数数量倾向更简洁的模型对预测任务更合适。如果你关心季节周期是否被充分建模可以额外加一条检查如果最优模型的季节滑动平均阶数 Q 恒为 0说明季节自相关已经被季节差分处理掉了这是正常的。参数含义搜索范围建议d非季节差分阶数0~1ADF 不平稳再取 1D季节差分阶数0~1季节分量不稳取 1p / q非季节 AR / MA 阶数0~2P / Q季节 AR / MA 阶数0~2s季节周期月数据 12周数据 7日数据酌情降采样这个表值得贴在 Notebook 旁边时间和精力永远优先花在 d 和 D 上p、q、P、Q 差不多就行因为它们对预测结果的边际影响远小于差分和季节设定的影响。4.2 四条高频踩坑记录现象、原因、对策坑一日粒度数据直接拟合 s365程序卡死或内存暴涨现象SARIMAX(... seasonal_order(1,1,0,365)).fit()跑了几十分钟没有输出CPU 持续拉满部分机器直接报 MemoryError。 原因季节周期 s 在状态空间模型中直接决定系统状态的维度。s365 意味着状态空间里要维护 365 个季节相关变量状态矩阵规模是月粒度s12的数百倍拟合和滤波的计算量完全不在一个量级。 对策把数据resample(M).mean()降采样到月度后再建模s12只有非季节部分保留日粒度或者换用 STL 分解后对季节调整序列做简单 ARIMA。不要跟 s365 硬刚这不是调参能解决的问题。坑二随机切分训练集导致验证指标虚高真实预测翻车现象用train_test_split(shuffleTrue)切数据验证集 RMSE 很低模型看起来很好一上线做真实滚动预测误差直接翻一倍。 原因时间序列的相邻样本强相关随机切分让训练集混入了验证集相邻时段的真实观测模型无形中提前看到了「未来」的信息。 对策统一用按时间顺序分割的函数训练集永远在最前验证集和测试集在时间上严格靠后评估时用 walk-forward每次只用截至当前时刻的数据训练。怕记不住的话给自己立个规矩凡是在时间序列上写了shuffleTrue的一律打回重审。坑三ADF 检验通过、普通 ARIMA 拟合成功但残差里还有明显年度波动现象ADF p 值小于 0.05order(1,1,0)的 ARIMA 也能拟合但残差序列的时序图在每年同期出现波浪形起伏预测图则是一条直线。 原因ADF 检验只识别普通单位根强季节性序列在包含截距加趋势项的回归设定下也可能被判定为平稳但它并不告诉你有周期成分。忽略季节性的 ARIMA 无法捕捉年度循环信息全留在残差里。 对策建模前先跑 STL 分解如果 seasonal 分量振幅稳定且明显就不要把 D 设为 0。用 ADF 决定 d用分解后的季节分量决定 D两者分开判断。这比「看 ADF p 值定一切」稳得多。坑四多步预测收敛成直线被当成模型坏了现象用predict(dynamicTrue)外推 24 个月预测值慢慢变成一条接近常数的线和「温度应该有季节波动」的直觉严重不符。 原因多步预测是无反馈递归预测点会替代滞后项进入下一次计算误差逐步累积当预测步数超过有效记忆长度模型开始输出条件均值规律上就表现为直线化。这是 ARIMA 类模型的统计性质不是 bug。 对策先检查seasonal_order的 s 是否和数据周期匹配月度数据用 12别用 365 这种残留值再看置信区间区间快速变宽恰恰是模型在诚实表达不确定性最后用验证集上的滚动预测对比真实观测而不是盯着一张外推图判断好差。5. 建模前的两个固定动作残差白噪声检查与回头预测5.1 残差白噪声Ljung-Box 五分钟体检SARIMA 拟合完别急着下结论先看一眼残差是不是白噪声。如果残差里还有结构说明模型没把信息提取干净。from statsmodels.stats.diagnostic import acorr_ljungbox lb acorr_ljungbox(res.resid.dropna(), lags[12, 24], return_dfTrue) print(lb)两行的lb_pvalue都大于 0.05说明残差在 12 阶、24 阶滞后上没有显著自相关季节性和趋势信息已经被模型吸收。这两行就是模型可信度的快速体检单。5.2 回头预测把「猜未来」变成「回测过去」预测未来没法立即验证但可以回头验证把最后 12 个月藏起来用前面的数据拟合逐月滚动预测再和真实值对比。history monthly.iloc[:-12].copy() preds [] for i in range(12): m SARIMAX(history, order(1,1,0), seasonal_order(1,1,0,12)).fit(dispFalse) preds.append(m.forecast(1).iloc[0]) history pd.concat([ history, pd.Series([monthly.iloc[-12 i]], index[monthly.index[-12 i]]) ]) rmse ((pd.Series(preds, indexmonthly.index[-12:]) - monthly.iloc[-12:]) ** 2).mean() ** 0.5这就是 walk-forward 的最简形态每次只预测 1 步、把真实值补进历史、重拟合循环 12 次。RMSE 落在 1~2 摄氏度以内说明月粒度模型在实战尺度上能打比训练集指标高出一截才是常态不高反而要怀疑有没有信息泄漏。从那以后我每次做时间序列预测都把残差白噪声检查和回头预测当成两个固定动作哪怕只是临时评估一个模型也要走完这两步才敢说结果可信。血泪经验是跳过这两步省下的十分钟会在上线之后用一个不眠之夜还回来。希望这两个动作能帮到你也帮你少踩几次我当年踩过的坑。本文还有配套的精品资源点击获取

相关推荐

OpenClaw(AI龙虾)自部署教程:10分钟用Docker+WSL2搭建个人AI助手
OpenClaw(AI龙虾)自部署教程:10分钟用Docker+WSL2搭建个人AI助手

说实话,第一次听到“AI龙虾”这个名字的时候我也愣了一下,后来才知道社区里都在这么叫OpenClaw(它早先叫Clawdbot,被戏称为Claude的“爪子机器人”,再加上Claw这个词天生就让人联想到龙虾钳子,叫着叫着就成… · 2026/9/23 2:36:11

4核cpu环境部署踩坑指南:保姆级教程解决卡顿难题
4核cpu环境部署踩坑指南:保姆级教程解决卡顿难题

4核cpu环境部署踩坑指南:保姆级教程解决卡顿难题 配置环境就卡半天?别急着骂硬件。很多新手在4核cpu上跑现代开发工具,CPU占用率直接飙到100%,风扇狂转,代码保存都要等三秒。这往往不是你的电脑不行,而是你默认的配置在“杀鸡用牛刀”或… · 2026/9/23 2:36:05

点点滴滴的近义词速查手册
点点滴滴的近义词速查手册

点点滴滴近义词:版本升级API全变后的最佳实践与源码拆解 版本升级后 API 全变了,这种痛苦谁懂?昨天还能跑通的代码,今天一升级依赖包,满屏的红色报错让人头皮发麻。这不是个别现象,而是许多开发者在维护老旧项目或跟进新技术栈时面临的常态。面… · 2026/9/23 2:36:05

飞书画板(lark-whiteboard)里程碑时间线图 DSL 绘制指南:从布局规则到骨架模板实战
飞书画板(lark-whiteboard)里程碑时间线图 DSL 绘制指南:从布局规则到骨架模板实战

飞书画板(lark-whiteboard)里程碑时间线图 DSL 绘制指南:从布局规则到骨架模板实战 【免费下载链接】cli The official Lark/飞书 CLI tool, maintained by the larksuite team — built for humans and AI Agents. Covers core business dom… · 2026/9/23 6:01:10

Ceph cephadm 命令行工具完全指南:本地主机的容器化编排管理
Ceph cephadm 命令行工具完全指南:本地主机的容器化编排管理

Ceph cephadm 命令行工具完全指南:本地主机的容器化编排管理 【免费下载链接】ceph Ceph is a distributed object, block, and file storage platform 项目地址: https://gitcode.com/gh_mirrors/ce/ceph cephadm 是 Ceph 分布式存储系统中用于管理本地主机… · 2026/9/23 6:01:10

3步搞定微信公众号收费源码解析,新手避坑指南
3步搞定微信公众号收费源码解析,新手避坑指南

3步搞定微信公众号收费源码解析,新手避坑指南 官方文档里那堆XML标签和异步回调机制,看得人脑子嗡嗡响,根本抓不住重点。其实只要把 微信公众号收费 背后的源码逻辑拆解开,你会发现核心就那几个函数在跑。今天这篇 源码解析… · 2026/9/23 6:01:10

Swift 任务优先级提升 API 实战解读:SE-0462 与 withTaskPriorityEscalationHandler 完全指南
Swift 任务优先级提升 API 实战解读:SE-0462 与 withTaskPriorityEscalationHandler 完全指南

Swift 任务优先级提升 API 实战解读:SE-0462 与 withTaskPriorityEscalationHandler 完全指南 【免费下载链接】swift-evolution This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. 项目地址: https://git… · 2026/9/23 6:01:04

C++图形编程入门:用EasyX从零实现贪吃蛇游戏
C++图形编程入门:用EasyX从零实现贪吃蛇游戏

写这个项目的起因很简单:我见过太多人学C学到指针、类就放弃了,理由是“看不见摸不着”,不知道学这些东西到底能干嘛。图形编程库EasyX恰好能解决这个痛点——它能让你用熟悉的C语法,很快画出一个窗口、一个圆、一个方块&#xff… · 2026/9/23 6:01:04

8款提升程序员效率的AI工具实战指南
8款提升程序员效率的AI工具实战指南

1. 项目概述作为一名在技术行业摸爬滚打多年的老手,我深知效率工具对程序员日常工作的重要性。今天要分享的这8款AI工具,是我在过去两年里从上百个同类产品中筛选出来的真正"生产力神器"。它们覆盖了从文档创作到代码编写的全流程,… · 2026/9/23 6:01:04

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码