恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
时间序列预测实战:趋势与季节性分解及ARIMA/SARIMA建模
首页
资讯中心
/
时间序列预测实战:趋势与季节性分解及ARIMA/SARIMA建模
时间序列预测实战:趋势与季节性分解及ARIMA/SARIMA建模
发布时间:2026/9/11 21:33:35
简介面向气候预测场景的时间序列分析实战资源针对既含趋势又含季节波动的数据帮助Python学习者掌握从数据探索、移动平均去噪、STL季节性分解到ARIMA/SARIMA建模、参数调优与误差评估的完整链路。压缩包共九个文件以三个Jupyter Notebook为核心另配日最低气温csv数据集以及若干工程配置文件整体不足3MB轻量易用适合教学和快速验证。核心Notebook分别基于合成数据与真实温度数据逐步演示用Pandas处理时间索引和重采样、用滚动窗口平滑提取趋势、用seasonal_decompose提取季节成分再借助ACF/PACF辅助确定阶数基于statsmodels构建SARIMA模型并计算MSE、RMSE、MAE等指标评估效果最终用Matplotlib输出训练拟合与未来预测曲线直观对比模型表现。已有525人学习浏览内容组织紧凑、示例可运行既适合初学者跟着敲一遍也能作为后续气候预测项目的基础模板复用。1. 从两份 notebook 看趋势与季节性的时间序列预测实战很多人拿到时间序列数据第一反应就是找模型直接套结果预测曲线像一条直线还以为是数据太乱。实际上绝大多数气候类序列比如每日最低气温本质上是趋势、季节周期和随机噪声叠加的结果没把成分拆开就建模相当于把信号和噪声一起交给了回归器。这个压缩包里两个 notebook 正好覆盖两类典型场景modeling_synthetic_dataset.ipynb在合成数据上验证分解和建模逻辑modeling_temperature_dataset.ipynb再用真实气温数据跑完整预测流程。两种数据适合不同学习阶段的人也适合做气候分析或运维监控的工程师对照着查漏补缺。下面我会按「先分解、再定阶、后诊断」的顺序把可以复现的参数和踩坑点拆开讲。2. 为什么趋势和季节性必须先于建模分解与平稳性检验2.1 STL 与 seasonal_decompose 的拆解逻辑时间序列的四成分中趋势和季节性决定模型形态随机成分则决定误差边界。直接用原始序列拟合 ARIMA模型会把趋势当成均值变化把季节周期当成自回归项去逼近导致阶数虚高、预测失效。所以我一般会先对序列做分解确认趋势和季节周期是否显著再决定用 ARIMA 还是 SARIMA。statsmodels 提供了两种分解入口seasonal_decompose适合周期性固定、周期长度明确的场景STLSeasonal-Trend decomposition using LOESS对异常值更鲁棒且能处理平滑变化的趋势。对每日最低温度这类数据周期是 365 天用STL更合适因为它不会像简单移动平均那样在序列两端损失太多信息。import pandas as pd import matplotlib.pyplot as plt from statsmodels.tsa.seasonal import STL # 读取压缩包内数据集日期作为索引 df pd.read_csv(data/daily-minimum-temperatures.csv, parse_dates[Date], index_colDate) df.columns [temp] series df[temp].astype(float) # 按年周期做 STL 分解周期长度 365 stl STL(series, period365, robustTrue) res stl.fit() # 绘图原始、趋势、季节、残差四联图 fig res.plot() plt.show() print(res.trend.head(), res.seasonal.head())这里period365是气候序列最常见的选择如果换成语料或流量数据周期通常是 7 或 24需要先通过自相关图确认。robustTrue表示用 LOESS 的鲁棒版本能削弱极端气温日对趋势线的拉扯。2.2 ADF 检验与差分先让序列“可被模型理解”分解只能帮我们看清结构不能直接决定是否差。ARIMA 的 I 部分要求差分后的序列平稳所以建模前必须做 ADFAugmented Dickey-Fuller检验。判断标准很直接p值小于 0.05 就拒绝“存在单位根”的原假设认为序列平稳否则需要差分。from statsmodels.tsa.stattools import adfuller result adfuller(series) print(raw p-value:, result[1]) # 一阶差分后再检验 diff_series series.diff().dropna() result_diff adfuller(diff_series) print(diff p-value:, result_diff[1])常见做法是同时打印 ADF 统计量和临界值但我习惯记录p值变化趋势。原始温度数据通常p值较大一阶差分后会显著变小这说明序列存在随机游走倾向。注意series.diff().dropna()会丢掉第一天之后预测时要保留对应的时间索引对齐。2.3 合成数据 vs 温度数据两类场景的分解差异modeling_synthetic_dataset.ipynb里造的合成数据一般包含线性或分段趋势加上固定正弦季节项所以seasonal_decompose能拆得很干净。真实温度数据则有两个特点一是存在年际波动比如厄尔尼诺年份整体偏暖二是残差不满足纯高斯分布冬季方差更大。这两点直接影响后续模型选择。我的建议是在合成数据上跑通流程重点看分解是否能把三个成分完全分离在真实数据上则要观察残差里是否还残留周期。如果 STL 分解后残差仍然有明显的年周期自相关说明period参数或模型阶数没选对而不是序列本身有问题。序列类型周期长度趋势特征推荐分解方式合成数据自定义如 12/365线性/阶跃seasonal_decompose气象温度365 或 365.25缓变漂移STLrobust销售数据7 或 12突变节假日效应STL 节假日回归变量分解完之后才是建模拟合的环节但很多项目在这里就止步了因为直接拿原始序列拟合也能得到不错的训练集 R²。下一步我会结合两个 notebook 里的建模代码讲清楚 ARIMA 和 SARIMA 的参数到底怎么定。3. 用 statsmodels 搭建 ARIMA/SARIMA 基线模型3.1 数据切分与评估指标split_time_series_sets 的正确姿势时间序列切分不能用随机打乱必须按时间顺序保留最后一段作为测试集。split_time_series_sets本质上是把序列切出训练、验证、测试三块常见比例是 70/15/15。温度数据如果有 3650 天那测试集就是最后 547 天正好覆盖一年半能测出跨季节预测能力。def split_time_series_sets(data, train_ratio0.7, val_ratio0.15): n len(data) train_end int(n * train_ratio) val_end int(n * (train_ratio val_ratio)) train data[:train_end] val data[train_end:val_end] test data[val_end:] return train, val, test train, val, test split_time_series_sets(series) print(ftrain: {len(train)}, val: {len(val)}, test: {len(test)})验证集用于模型选择和调参测试集只允许跑一次否则会高估模型泛化能力。评估指标上我一般看 RMSE 和 MAE 两个值RMSE 对异常温度更敏感MAE 更贴近业务可感知误差。不要只看 R²温度序列强季节性会让 R² 天然很高。3.2 ARIMA 的 p/d/q 选择ACF/PACF 与信息准则ARIMA 的三个参数里d是差分阶数靠 ADF 检验确认p和q可以靠看差分后序列的 ACF/PACF 图初选再结合 AIC/BIC 精调。简单经验是ACF 拖尾、PACF 截尾走 AR 模型ACF 截尾、PACF 拖尾走 MA 模型都拖尾就增加阶数或换 SARIMA。from statsmodels.graphics.tsaplots import plot_acf, plot_pacf import matplotlib.pyplot as plt diff train.diff().dropna() plot_acf(diff, lags40) plot_pacf(diff, lags40) plt.show()手动看图容易有主观性所以我会用pmdarima的auto_arima做网格搜索它会遍历多个p,d,q, P,D,Q,s组合并返回 AIC 最低的模型。注意自动搜索只适合基线版本最终参数要结合残差诊断确认。from pmdarima import auto_arima auto_model auto_arima( train, seasonalTrue, m365, traceFalse, stepwiseTrue, max_order8, D1, suppress_warningsTrue ) print(auto_model.order, auto_model.seasonal_order)m365是季节周期stepwiseTrue用逐步搜索而不是全量网格大幅压缩时间。这样跑出来的基线参数再去手工微调P和Q会快很多。3.3 SARIMA 如何吸收季节性周期最小温度数据建模ARIMA 只建模短期自相关温度序列一年一个周期必须用 SARIMA 的(P,D,Q,m)季节部分吸收。m365会让模型参数矩阵变大所以真实气象项目里常先把数据聚合成月均值或者用m7建模周周期。压缩包里的daily-minimum-temperatures.csv是逐日数据我建议至少在验证阶段先试m365再试m30或m7对比哪个在验证集上 RMSE 更低。import statsmodels.api as sm # 按 auto_arima 给定的阶数拟合 SARIMA model sm.tsa.statespace.SARIMAX( train, order(2, 1, 1), seasonal_order(1, 1, 1, 365), enforce_stationarityFalse, enforce_invertibilityFalse ) res model.fit(dispFalse) print(res.summary())enforce_stationarityFalse和enforce_invertibilityFalse是应对长周期数据拟合报错的关键设置。气候数据经常因为数值刚性导致滤波不收敛关掉这两个约束后能算出结果但要记得回到res.summary()看系数是否显著显著性差就降阶。3.4 网格搜索参数与交叉验证陷阱普通机器学习常用的 K 折交叉验证不能直接用打乱样本会破坏时间顺序造成未来数据泄漏。做时间序列调参时我一般会按时间顺序做滚动验证把训练集起点固定终点逐步后移每次预测同一段验证窗口最后汇总误差。from sklearn.metrics import mean_squared_error import numpy as np def rolling_validate(train_data, order, seasonal_order, horizon30): errors [] start len(train_data) - 3 * horizon for i in range(start, len(train_data) - horizon, horizon): train_part train_data[:i] test_part train_data[i:ihorizon] model sm.tsa.statespace.SARIMAX( train_part, orderorder, seasonal_orderseasonal_order, enforce_stationarityFalse, enforce_invertibilityFalse ).fit(dispFalse) pred model.forecast(stepslen(test_part)) errors.append(np.sqrt(mean_squared_error(test_part, pred))) return np.mean(errors)订阅参数表格参数含义常见选择策略p / q非季节 AR/MA 阶数ACF/PACF AICP / Q季节 AR/MA 阶数不超过 2越大越易过拟合d / D差分阶数ADF 检验倾向不超过 1m季节周期长度7 / 12 / 365需从业务确认调参不是一劳永逸的下一种情况里一个看起来很美的低 AIC 模型可能在残差诊断时直接翻车。4. 预测结果诊断与调参残差、RMSE 与过拟合边界4.1 残差白噪声检验Ljung-Box 与残差图模型拟合并不能只看 AIC。AIC 低的模型如果残差里还有自相关说明信息没提取完预测区间会失真。最直接的诊断是画残差 Q-Q 图和自相关图再用 Ljung-Box 检验看残差是否白噪声。from statsmodels.stats.diagnostic import acorr_ljungbox resid res.resid lb_test acorr_ljungbox(resid, lags10, return_dfTrue) print(lb_test)代码里lags10表示检查残差滞后 1 到 10 阶的联合自相关。lb_pvalue全部大于 0.05 就说明残差没有显著自相关。如果某个滞后阶数的p小于 0.05优先检查是否漏掉了更长的季节周期而不是盲目加P或Q。modeling_temperature_dataset.ipynb里通常会有一次残差图展示注意观察残差是否在某几个月份显著变大比如冬季温度波动大残差方差就偏高。这种异方差不会明显影响点预测但会让置信区间不准确。4.2 滚动预测与多步预测的误差区别单步预测误差低不代表多步预测可靠。ARIMA/SARIMA 的多步预测会把上一步的预测值作为输入继续往后推误差会累积。滚动预测则是每一步都用真实观测值更新模型状态这更适合评估模型参数本身的好坏而不适合评估实际部署场景。def multi_step_forecast(model, steps): pred model.forecast(stepssteps) return pred对比两种方式时你会看到滚动误差远低于多步误差。真实气候预测都是多步所以报告 RMSE 时要标明预测步长。我习惯分别报告 1 步、7 步、30 步的 RMSE这样才知道模型在哪个时间尺度上开始不可信。预测方式数据使用适用场景误差特征单步滚动每一步都用真实值参数评估低多步递归预测值喂给自己实际业务预测随步长放大4.3 常见误用与坑直接对非平稳序列建模、忽略季节性周期最常见的错误发生在两个地方一是跳过 ADF 直接对原始序列拟合 ARIMA得到很低的训练误差但差分阶数不对预测值会漂移二是把季节周期设错比如温度数据用了m12却不知道这个值是月周期还是年周期导致季节项每次拟合都在漂移。另外还有一类坑是过度调参。auto_arima在全量搜索下会选出高阶模型比如p7, q5虽然 AIC 低但参数越多样本外预测越不稳定。我在处理温度数据时会把高阶模型和低阶模型放在验证集上对比如果两者 RMSE 相差不到 5%选低阶模型。5. 把模型落到实际温度预测的验证技巧与可视化输出5.1 用 Matplotlib 绘制预测对比带有了测试集预测结果后不只是画一条折线还要给预测区间加上置信带。SARIMAX 的get_forecast能返回均值和置信区间用来画带透明度的区间图很直观。import matplotlib.pyplot as plt forecast_result res.get_forecast(stepslen(test)) mean_pred forecast_result.predicted_mean conf_int forecast_result.conf_int() plt.figure(figsize(12, 5)) plt.plot(test.index, test, labelactual, colorblack) plt.plot(test.index, mean_pred, labelforecast, colorblue) plt.fill_between( test.index, conf_int.iloc[:, 0], conf_int.iloc[:, 1], colorblue, alpha0.2, label95% CI ) plt.legend() plt.show()conf_int默认给出 95% 置信区间alpha 参数控制带的透明度。实际温度预测里区间宽度在冬季明显更宽这能提醒业务方夏季预测更可信冬季预测要留更多冗余。5.2 业务化验证MAE 阈值与样本外测试做气候预测时光看 RMSE 不够还要定一个业务可接受的 MAE 阈值。比如日均最低气温预测如果 MAE 控制在 1 摄氏度以内农民安排防霜冻措施就有意义超过 2 摄氏度则需要人工校对。我把测试集预测结果和真实值相减按月份聚合 MAE通常能发现夏秋两季误差小、冬春两季误差大。import pandas as pd err (test - mean_pred).abs() qualitative test.to_frame(actual) qualitative[err] err monthly_mae qualitative.resample(M).mean()[err] print(monthly_mae.describe())最后可以再和更复杂的方法做一次对比例如用 LSTM 或 Transformer 在同样的训练集上训练通常传统统计模型在序列长度只有几千、且强周期的情况下SARIMA 已经足够深度学习模型反而容易在温度数据上过拟合。如果你把 SARIMA 的预测结果作为基线再引入x_reg外生变量如气压、湿度模型的实用价值会比单纯追求 RMSE 更低更有意义。本文还有配套的精品资源点击获取