
简介基于时间序列分析的降雨量预测Python项目面向需要完成课程设计或入门时序建模的学习者以历史气象数据为基础提供未来降雨量的预测方案。项目整合自回归积分滑动平均ARIMA与长短期记忆网络LSTM两类主流模型完整涵盖数据清洗、标准化、缺失值处理等预处理环节并支持结合温度、湿度、气压等多变量特征提升最终精度。资源包以zip压缩包形式提供大小约11.12MB内含Python项目源码及核心模块可直接查阅模型构建、训练评估与预测输出的关键代码。技术栈涉及Pandas、NumPy、Statsmodels、Keras/TensorFlow等兼顾传统统计建模与深度学习应用同时预留实时更新、可视化界面以及极端天气报警提醒等可扩展功能并提供小时、日、月等多时间尺度的动态预测便于对比不同粒度下的模型表现。已有161人学习能帮助读者快速上手时间序列预测完整流程理解多变量特征融合与模型评估思路适合用于计算机、大数据或气象相关专业课程设计与实践。1. 时间序列分析降雨量预测这个 Python 项目里到底有什么做气象、农业或者水文方向课程设计的同学大概率都搜到过这个「基于时间序列分析的降雨量预测」的 Python 项目压缩包。它不是什么花哨的 Web 应用而是一套完整的「数据清洗 → 模型构建 → 预测评估」流水线核心模型是 ARIMA 和 LSTM 两条路线都给你铺好了拿到手改改数据就能跑出预测曲线。这个项目对两类人最实用一是要做课程设计、毕业设计但不想从零造轮子的学生二是刚接触时序预测、想看看真实气象数据该怎么处理的从业者。说白了它帮你把从下载气象数据到画出预测对比图这段最磨人的路走完你只需要关注模型参数和结果解读。2. 从数据到预测ARIMA 和 LSTM 两条路线怎么选2.1 降雨量数据的特殊性决定了模型选型降雨量数据和股票价格、网站流量这类时序数据有个本质区别它非常不平稳而且大量数值是零。你要用 ARIMA 这类统计模型第一步就得做差分让数据平稳而 LSTM 这类深度学习方法对原始分布没那么敏感但它需要的数据量远比 ARIMA 大。这个项目把两条路都实现了就是考虑到用户手里的数据规模不一样——如果你只有一两年的日降雨量记录ARIMA 更靠谱如果你能拿到十年以上的小时级数据LSTM 的优势才能发挥出来。我在拿到这个项目时一般先用 Pandas 快速做一轮探索性分析看数据的趋势项、季节项和残差项分别长什么样。这个步骤很多人会跳过直接喂给模型结果预测出来是一条水平线然后开始怀疑模型写错了。实际上不是模型错了是你没处理季节性。2.2 环境搭建少踩 Python 库版本冲突的坑这个项目的依赖主要是pandas、numpy、statsmodels、keras、tensorflow、sklearn、matplotlib。如果你是照着 README 用pip install -r requirements.txt一把梭大概率会遇到tensorflow和numpy版本不兼容的问题。我试过 numpy 2.x 配 tensorflow 2.10直接报错说找不到某个 C 扩展。建议你装的时候分开装先装statsmodels和sklearn再单独装tensorflow-cpu这样能省下不少排查时间。# 建议用 conda 建一个干净环境Python 版本选 3.9 或 3.10 conda create -n rainfall python3.10 -y conda activate rainfall # 先装数据处理和统计模型相关库 pip install pandas numpy matplotlib statsmodels scikit-learn # 再单独装深度学习库避免一次装太多冲突 pip install tensorflow-cpu2.10.0 pip install keras2.10.0这段命令的逻辑是先隔离环境再把统计模型和深度学习模型的依赖分开装。tensorflow-cpu版本不用追求最新2.10 左右对 Keras 的支持最稳定。如果你的机器有 NVIDIA 显卡想用 GPU 加速就把tensorflow-cpu换成tensorflow-gpu但一定要先确认 CUDA 版本匹配否则训练时会出现莫名奇妙的Could not create cudnn handle报错。2.3 两条路线对比先想清楚你要交付什么这个项目里 ARIMA 和 LSTM 不是上下位替代关系而是互补的。ARIMA 的优势在于可解释性强你告诉老师「这个模型的 d1是因为原始数据一阶差分后通过了 ADF 检验」这就是答辩时的加分项。LSTM 的优势在于能捕捉长期依赖但代价是需要更多的数据、更长的训练时间以及更难调参。我的建议是课程设计的时间通常只有两三个月你完全可以把两条路线都跑通然后对比结果挑效果好的作为主线另一条作为对照实验放在论文里。答辩时老师问你「为什么不用 LSTM」你可以回答「我试了但在当前数据规模下 ARIMA 的 RMSE 更低」这个回答比只说「我选择了 ARIMA」要扎实得多。3. 数据清洗与特征工程预测准不准七成看这里3.1 缺失值和异常值的处理套路气象站数据缺测是常态尤其是小时级降雨数据常出现连续几个小时的 NaN。这个项目里的数据处理模块提供了一个比较完整的处理流程先统计缺失率再决定是删除、插值还是用前向填充。我拿到的数据里如果某个月的缺失率超过 20%这一整段我一般直接剔除因为插值填出来的数据对模型是误导如果只是零星缺失用前后两小时的平均值填充就够用了。import pandas as pd import numpy as np df pd.read_csv(rainfall_data.csv, parse_dates[date]) df.set_index(date, inplaceTrue) # 统计缺失率 missing_rate df[rainfall].isna().mean() print(f缺失率: {missing_rate:.2%}) if missing_rate 0.2: # 缺失率过高直接删除对应时间段 df_clean df.dropna() else: # 缺失率低线性插值填充 df_clean df[rainfall].interpolate(methodlinear) # 处理异常值降雨量不可能为负超过 300mm/天的记录几乎可以断定是仪器故障 df_clean df_clean[(df_clean 0) (df_clean 300)] # 重采样为日降雨量保持数据口径一致 df_daily df_clean.resample(D).sum()这段代码里有两个关键决策点interpolate(methodlinear)适合缺失值稀疏的场景如果连续缺失超过 6 小时线性插值的误差会很大这时候改成methodtime会更合理。resample(D).sum()是把小时数据聚合成日数据注意聚合函数的选择——降雨量用求和是符合气象学口径的但如果你后续要做温度预测聚合就得用平均值。3.2 平稳性检验AIC 和 ADF 的配合使用ARIMA 模型的前提是数据平稳但降雨量数据几乎天生不平稳——旱季和雨季的方差差好几倍。这个项目用的是 ADF 检验p 值大于 0.05 就认为不平稳需要差分。这里有个容易翻车的点差分次数不是越多越好d1 通常就能让降雨量序列平稳如果继续做二阶差分虽然 ADF 的 p 值更漂亮了但会损失掉原始数据里的长期趋势信息预测结果反而更差。from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # 一阶差分 df_diff df_daily[rainfall].diff().dropna() # ADF 检验 adf_result adfuller(df_diff) print(fADF p-value: {adf_result[1]:.4f}) if adf_result[1] 0.05: print(差分后序列平稳可以进入 ARIMA 建模) else: print(序列仍不平稳考虑二阶差分或取对数)判断平稳性不要只看 p 值还要配合可视化的 ACF 和 PACF 图一起看。我见过有人把 p 值压到 0.001 以下但 ACF 图拖尾仍然很明显这时候模型预测出来依然是一条直线因为方差的不平稳 ADF 检验不一定抓得到。稳妥的做法是再做一个 KPSS 检验两个检验结果交叉验证。3.3 特征工程把湿度、温度变成模型能用的输入这个项目支持多变量分析也就是说除了降雨量本身你还可以把温度、湿度、气压这些气象指标一起喂给模型。但这里有个陷阱如果直接用原始数值拼在一起作为 LSTM 的输入温度可能到三十几和降雨量通常只有个位数的数值量级不一样模型会偏向学习量级大的特征。我在特征工程这一环必做标准化处理用的是sklearn的StandardScaler把每个特征都归一到均值 0、方差 1 的分布上。from sklearn.preprocessing import StandardScaler feature_cols [rainfall, temperature, humidity, pressure] scaler StandardScaler() scaled_data scaler.fit_transform(df_daily[feature_cols]) # 注意fit 只用训练集测试集要用同一个 scaler 做 transform train_size int(len(scaled_data) * 0.8) train_data scaled_data[:train_size] test_data scaled_data[train_size:] # 保存 scaler 参数预测时反标准化要用 np.save(scaler_mean.npy, scaler.mean_) np.save(scaler_scale.npy, scaler.scale_)这里最关键的一步是scaler.fit_transform必须只用在训练集上测试集用transform否则会把未来的信息泄露给模型导致测试集上的评估指标虚高。你拿去答辩的时候老师很可能问这个问题答上来了就能证明你真的理解数据泄露这个概念。4. ARIMA 建模实战从定阶到预测的完整步骤4.1 定阶的三个工具怎么配合ARIMA 模型由三个参数组成p 是自回归阶数d 是差分次数q 是移动平均阶数。这个项目代码里通常用了两种定阶方式一种是看 ACF 和 PACF 图的截尾和拖尾特征另一种是用statsmodels的auto_arima做网格搜索。两种方法我都用过网格搜索确实省事但它是个黑匣子一旦结果不好你不知道从哪里排查手动看图定阶虽然费时间但你能清楚地知道每个参数是怎么来的。from statsmodels.tsa.arima.model import ARIMA import statsmodels.api as sm # 假设已通过 ACF/PACF 图确定了 p2, d1, q2 model ARIMA(df_daily[rainfall], order(2, 1, 2)) model_fit model.fit() # 看一眼模型摘要 print(model_fit.summary()) # 残差白噪声检验 residuals model_fit.resid lb_test sm.stats.acorr_ljungbox(residuals, lags10) print(fLjung-Box p-value: {lb_test[lb_pvalue].iloc[-1]:.4f})如果 Ljung-Box 检验的 p 值小于 0.05说明残差里还有信号没被模型捕捉你就要回头调 p 和 q 的阶数。正常情况 p 值应该大于 0.05表示残差是白噪声信息已经提取干净了。4.2 预测与反标准化别把差分后的结果直接当预测值很多人第一次跑这个项目会犯一个错直接用model_fit.forecast(steps30)输出预测值然后和原始数据画在一起对比发现预测值比实际值小一大截以为模型有问题。其实是没做反差分。你用 d1 建模预测出来的是差分序列的未来值要还原成原始量纲需要把差分前的最后一个真实值加上预测的差分值。import matplotlib.pyplot as plt # 预测未来 30 天 forecast_diff model_fit.forecast(steps30) # 反差分从训练集最后一个真实值开始累加 last_value df_daily[rainfall].iloc[-1] forecast [] current last_value for diff_val in forecast_diff: current current diff_val forecast.append(current) # 如果是差分后建模记得同步对齐时间索引 forecast_index pd.date_range(startdf_daily.index[-1] pd.Timedelta(days1), periods30) forecast_series pd.Series(forecast, indexforecast_index) # 画对比图 plt.figure(figsize(12, 6)) plt.plot(df_daily[rainfall].iloc[-90:], labelHistorical) plt.plot(forecast_series, labelForecast, colorred) plt.legend() plt.show()反差分是 ARIMA 实战里的一个经典易错点。这个项目如果代码里已经封装了反差分函数你要确认它对的是训练集最后一个真实值而不是测试集的最后一个值。两者看起来很像但差一个时间步累积误差会越来越大。4.3 模型评估RMSE、MAE 和 U 值怎么解读评估这一步我习惯同时算 RMSE、MAE 和 Theils U。RMSE 对异常值敏感如果预测结果里有几个偏差特别大的点RMSE 会被拉得很高这时候 MAE 能告诉你「大多数点到底偏了多远」。Theils U 是个相对指标小于 1 说明模型比朴素预测即用上一个值预测下一个值好这个指标在答辩时非常有用因为它给了你一个明确的基准对比。from sklearn.metrics import mean_squared_error, mean_absolute_error # 假设 y_true 是真实值y_pred 反差分后的预测值 rmse np.sqrt(mean_squared_error(y_true, y_pred)) mae mean_absolute_error(y_true, y_pred) # Theils U 计算 n len(y_true) denominator np.sqrt(np.sum((y_true[1:] - y_true[:-1])**2) / n) u_stat rmse / denominator if denominator ! 0 else float(inf) print(fRMSE: {rmse:.4f}) print(fMAE: {mae:.4f}) print(fTheils U: {u_stat:.4f})RMSE 和 MAE 的单位和降雨量一致比如 RMSE 是 6.5说明平均每个预测日和真实值差 6.5 毫米。这个数值绝对值的高低要结合你所在地区的年均降雨量来看如果年均降雨量才 300 毫米6.5 毫米的 RMSE 已经算不错了。5. LSTM 建模实战数据窗口和训练参数是成败关键5.1 把时序数据切成监督学习样本LSTM 不像 ARIMA 那样直接吃时间序列它需要你构造「过去 N 天预测未来 M 天」的样本对。这个滑动窗口的长度选择有讲究窗口太短模型看不到周期性规律窗口太长训练数据量会锐减而且模型容易过拟合。我一般对日降雨量数据用 30 天的窗口这样模型能看到一个完整月的模式预测未来 7 天。def create_sequences(data, seq_length30, pred_length7): X, y [], [] for i in range(len(data) - seq_length - pred_length 1): X.append(data[i:i seq_length]) y.append(data[i seq_length:i seq_length pred_length]) return np.array(X), np.array(y) # 用标准化后的数据构造样本 seq_length, pred_length 30, 7 X, y create_sequences(scaled_data, seq_length, pred_length) # 按时间顺序切分训练集和测试集 split_idx int(len(X) * 0.8) X_train, X_test X[:split_idx], X[split_idx:] y_train, y_test y[:split_idx], y[split_idx:] print(f训练集样本数: {X_train.shape[0]}, 每个样本维度: {X_train.shape[1:]})注意这里create_sequences函数里没有打乱数据顺序这一点在 LSTM 训练中非常关键。如果用了train_test_split的默认shuffleTrue时间序列的顺序信息会被打乱训练集里混入测试时间段的数据模型评估就完全失真了。这是我见过最隐蔽的错误很多网上的教程都这么写但你用真实数据一跑就露馅。5.2 训练配置Batch Size 和 Epoch 的取舍LSTM 的训练过程在 CPU 上会非常慢尤其是数据量大的时候。这个项目如果默认用的是全量数据训练 100 个 epoch你在自己电脑上可能需要跑几个小时。我的经验是先把epochs设成 20batch_size设成 32快速跑通全流程确认代码没 bug再加大训练量。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout model Sequential() model.add(LSTM(units64, return_sequencesTrue, input_shape(seq_length, X.shape[2]))) model.add(Dropout(0.2)) model.add(LSTM(units32, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(unitspred_length)) model.compile(optimizeradam, lossmse, metrics[mae]) history model.fit( X_train, y_train, validation_data(X_test, y_test), epochs20, batch_size32, verbose1 )这里input_shape(seq_length, X.shape[2])中的第二个维度是特征数量也就是你用了几列气象数据。如果只预测降雨量单变量这里就是 1。两个 LSTM 层中间加Dropout是为了防止过拟合但比例不要超过 0.3否则模型会欠拟合。adam优化器默认学习率是 0.001如果 loss 震荡不收敛可以试试把手动把学习率调到 0.0005。5.3 预测结果反标准化与可视化对比LSTM 预测输出的是标准化后的数值你需要用之前保存的scaler做逆变换才能和真实降雨量对比。这里有个细节如果y_pred是 7 天的预测序列你要对每个时间步都做反标准化而不是对整个数组一次性变换。# 用之前保存的 scaler 做逆变换 y_pred_scaled model.predict(X_test) y_pred scaler.inverse_transform(y_pred_scaled) y_true scaler.inverse_transform(y_test) # 取最后一个测试样本的真实值和预测值画对比 plt.figure(figsize(12, 5)) plt.plot(y_true[-1], labelActual, markero) plt.plot(y_pred[-1], labelPredicted, markers) plt.xlabel(Days Ahead) plt.ylabel(Rainfall (mm)) plt.legend() plt.title(LSTM 7-Day Rainfall Forecast vs Actual) plt.show()反标准化的正确性直接决定预测曲线是「看着差不多」还是「完全对不上」。如果你跳过这步LSTM 预测出的数值会全部落在 -2 到 2 之间画的图虽然形态对但纵轴刻度完全不可读交作业时一眼就被看穿。6. 避坑指南时间序列预测项目最常见的 5 个翻车点6.1 数据顺序被打乱模型评估结果虚高现象用 LSTM 训练完测试集上的 RMSE 特别漂亮但换一段新数据预测就完全不行。原因在构造训练集和测试集时使用了随机切分或者用了train_test_split默认的shuffleTrue导致训练集里混入了未来时间段的数据模型相当于「开卷考试」。解决严格按时间顺序切分训练集只用前 80% 的时间段测试集用后 20%。切分后检查一下两个集合的日期索引有没有交叉确认没有再做后续操作。6.2 ARIMA 的 d 值过度差分把趋势信息差掉了现象d2 时 ADF 检验 p 值确实很小但预测结果是一条水平线。原因二阶差分把序列中原本存在的趋势项也抹掉了模型只能学到随机波动预测值自然趋于均值。解决差分次数通过 d0、d1、d2 分别建模后比较 AIC 值来确定。AIC 最小的模型对应最优 d。不要盲目追求 p 值最小AIC 才是综合衡量拟合度和复杂度减法的指标。6.3 缺失值用全局均值填充旱季雨季的规律被抹掉现象预测曲线整体偏平完全看不到季节波动。原因填充缺失值时用了整个序列的均值比如全年日均降雨量是 5 毫米那么所有缺失日都被填成 5 毫米旱季该是 0 的地方变成 5雨季该是 20 的地方也变成 5季节信号被彻底破坏。解决缺失值填充要分季节处理。旱季缺失用旱季相邻日期的均值雨季缺失用雨季的均值。或者用前向填充和后向填充结合至少保持局部趋势的一致性。6.4 LSTM 训练 loss 不降反升现象loss在训练过程中剧烈震荡甚至出现nan。原因特征没有标准化某个特征的数值范围太大梯度更新时发生爆炸。另一个常见原因是学习率设得太高。解决检查是否用了StandardScaler确认所有特征都被缩放到差不多的量级。如果标准化没问题把learning_rate从默认的 0.001 降到 0.0001batch_size从 32 降到 16再跑一次看 loss 曲线。6.5 预测多条曲线时时间轴错位现象预测曲线和真实曲线形态完全对不上但数值范围看起来是合理的。原因预测值和真实值的时间索引没对齐。ARIMA 的forecast返回的是从训练集结束后的下一个时间点开始而如果你把测试集的索引原封不动地用来对齐就会整体偏移 1 个或 N 个时间步。解决预测前先打印出训练集最后一个时间戳和测试集第一个时间戳确认预测序列的起点。然后手动构造预测序列的日期索引不要直接沿用测试集索引。7. 让预测结果更可信的进阶用法滚动预测与模型对比滚动预测是验证时间序列模型真实水平的常用方法也是答辩时老师最可能追问的点。它模拟的是「预测明天等明天到了再更新模型」的真实业务场景先用前 90% 的数据训练模型预测接下来 7 天然后把这个 7 天段真实数据并入历史数据重新训练模型再预测下一个 7 天以此类推。这样做能直观看出模型的误差是稳定还是随时间累积变大。def rolling_forecast(model_func, data, train_ratio0.8, window7): 滚动预测每次扩展训练集预测下 window 天 model_func: 接收训练数据返回预测结果的函数 split_idx int(len(data) * train_ratio) forecasts [] actuals [] for start in range(split_idx, len(data) - window 1): train_data data[:start] test_data data[start:start window] # 训练并预测 pred model_func(train_data, window) forecasts.append(pred) actuals.append(test_data) return np.array(forecasts), np.array(actuals)滚动预测的意义在于它能暴露模型的时间稳定性问题。如果你的模型在第一个窗口预测误差小但后面几个窗口误差越来越大很可能是因为数据分布发生了变化比如旱季转雨季的过渡阶段模型没跟上这个变化。这时候你要考虑的不是盲目调参而是给模型加上季节因子或者在特征里显式加入「月份」这种时间编码。我一般会做一张模型对比表把 ARIMA 和 LSTM 的 RMSE、MAE、训练耗时、预测耗时都列出来。这张表的意义不只是选一个「最好的」而是让你理解两种模型的适用边界——ARIMA 在小数据量、强季节性的场景下可能打平甚至超过 LSTM而 LSTM 的优势在长序列和高维特征输入时才能体现出来。模型RMSEMAE训练耗时适用场景ARIMA5.23.82 秒数据量小、特征单一LSTM4.73.515 分钟数据量大、多变量输入这个项目本身已经把 ARIMA 和 LSTM 两条路都封装好了但你要真正把它变成自己的东西建议把两个模型的评估结果跑出来哪怕 ARIMA 效果更好你也能在答辩时说清楚 LSTM 为什么在这个数据规模下没有优势。时间序列预测不是一个「调出最优参数」就结束的活参数背后的理解才决定你能不能把这份资源转化为自己的工程能力。我自己的习惯是每次拿到一份新的降雨量数据先花 20 分钟做数据探索把季节性、缺失模式、异常值范围摸清楚再决定走哪条建模路线。跳过这一步后面所有工作都是在沙滩上建楼。希望这篇拆解能帮你把项目跑通更希望你跑通之后能说清楚每一步为什么这么做——那时候这份资源才算真正属于你了。本文还有配套的精品资源点击获取