☰
时间序列分析降雨量预测Python项目复现:从ARIMA到平稳性检验
2026/10/1 23:10:53 网站建设 项目流程

简介:这是一份基于时间序列分析的降雨量预测 Python 项目源码,定位为课程设计与气象数据挖掘实战资源,适合计算机、数据分析或大气科学相关专业的初学者和进阶者学习。项目围绕历史降雨数据处理展开,重点包含数据清洗与标准化、ARIMA与LSTM模型构建及训练、温度湿度气压等多变量融合、日/月等多个时间尺度预测、模型评估对比等完整流程,还可扩展简易可视化界面与极端天气报警功能,能够为气象预报、农业规划和水资源管理提供数据支撑。压缩包为zip格式,约11.12MB,目前平台暂无文件数量和类型明细,下载后可直接解压获取项目源码、说明文档与运行所需材料。目前已有161人学习,可帮助读者快速理解时间序列建模从数据预处理到模型落地的全过程,也能在课程设计、毕业设计或科研实践中直接复用或二次开发。

1. 拿到的降雨量预测 cs.zip,先分清它是作业包还是能跑的项目

网上搜“python项目基于时间序列分析的降雨量预测cs.zip”,下载下来多半是一个课程设计或毕设打包工程,里面塞着几个 py 脚本、一份说明文档、一个 csv 数据文件,运气好还有 requirements.txt。时间序列分析的思路很直白:把降雨量当成一串按日、按月排列的数字,从历史里找规律去推未来,而不是像普通回归那样把日期当无关特征扔给模型。问题是,这类 zip 包十个里有六七个解压后跑不起来,不是缺库就是数据路径写死,要么模型文件没打进去。

这篇笔记按我复现这类项目的顺序来:先拆包看结构,再把环境补齐跑通最小命令,然后把平稳性检验、自相关分析和模型选型这套方法论落进代码,最后讲数据清洗和四个最容易翻车的坑。新手能照着把项目从黑匣子变成自己能改的程序,熟手可以跳过前两章直接看参数边界和验证方法。

2. 解包之后先看这四件事:跑通最小 python 程序的顺序

2.1 解压后第一件事:认清项目结构与入口文件

这类 zip 包没有统一规范,我拿到手习惯先建一个干净目录,再解压并打印文件树。因为常见做法是,作者把模型训练和预测脚本分开,入口文件不一定是名字最像的那个,得靠文件清单判断。

mkdir rain_project && cd rain_project unzip ../基于时间序列分析的降雨量预测cs.zip find . -maxdepth 2 -type f | sort

逻辑说明:mkdir建独立目录,避免解压出来的文件散落到其他地方;unzip解压后马上用find列出两层以内的文件,目的是在还没打开任何脚本前,先形成一份项目地图。参数说明:-maxdepth 2表示只向下找两层,防止把 python 虚拟环境里几千个文件也列出来;-type f只显示文件,不显示目录,配合sort让同类脚本排在一起。

拿到文件清单后,我一般先找三个东西:数据文件(csv/xlsx)、模型脚本(含 train 或 model 字样)、预测脚本(含 predict 或 forecast 字样)。数据文件的日期列格式、模型脚本里读数据的相对路径,是后面百分之八十报错的源头,先记下来比什么都强。

2.2 用 requirements.txt 还原环境:两个命令的连带效应

项目能解压不等于能运行。多数课程设计包是在作者本机跑的,依赖版本写没写全完全看良心。我的习惯是,先看有没有 requirements.txt;没有就直接看 import 语句,把 pandas、numpy、statsmodels、sklearn 这些常见的先装齐。注意 python 版本和库版本有连带关系,python 3.12 上装旧版 statsmodels 会遇到编译报错,装新版又可能遇到 API 变更。

python -m venv .venv source .venv/bin/activate pip install -r requirements.txt

逻辑说明:python -m venv .venv建独立虚拟环境,避免把项目依赖装进系统 python 里污染其他工程;source .venv/bin/activate激活环境,之后pip install都落在当前项目里。我一般会在这一步把pip install pandas numpy statsmodels matplotlib一起列进去,因为很多旧项目包的 requirements.txt 是手写的,漏了绘图库也不知道。

参数说明:如果解压后没有 requirements.txt,就把上面的安装命令换成pip install pandas numpy statsmodels matplotlib scikit-learn。装 statsmodels 时看到需要编译,优先换用pip install --only-binary :all: statsmodels,直接拉官方预编译包,省去本机缺编译器的麻烦。这个环节最容易踩的坑是版本冲突,后面第五章专门展开。

2.3 跑通最小预测脚本:从加载数据到输出一个数字

环境就绪后,别一上来就跑完整训练,先找一个入口文件执行一遍。我这几年复现项目的顺序是:先运行、看报错、修路径、再运行,直到出结果。很多 zip 包的问题不是逻辑错,而是数据路径写死成了C:/Users/xxx/Desktop/,在别人机器上必挂。

import pandas as pd from statsmodels.tsa.arima.model import ARIMA df = pd.read_csv("rainfall.csv", parse_dates=["date"], index_col="date") df = df.asfreq("D").ffill() # 转为日频并前向填充缺失 train = df["rain"][:-30] # 留最后 30 天做验证 model = ARIMA(train, order=(1, 0, 1)).fit() forecast = model.forecast(steps=30) print(forecast.head())

逻辑说明:parse_dates在读取时把日期列转成 datetime 类型,这是时间序列分析的起点,后面做索引、重采样、滞后特征全依赖它是时间类型;asfreq("D")把数据补齐为连续日序列,再用ffill填充空值,旧项目的数据经常缺几天,不补的话模型会因为索引断裂直接报错。order=(1, 0, 1)是最简单的 ARMA 结构,forecast输出未来 30 天的预测值。

参数说明:[:-30]是 python 数组切片的典型写法,表示取除最后 30 行以外的全部数据。跑通这一小段,说明数据读取、模型训练、预测输出这条链路没断,接下来才值得花时间调参数。如果这条链路断了,优先去读数据文件的真实列名,很多时候不是date和rain,而是日期和降水量,改列名比改逻辑省事得多。

3. 时间序列分析的核心假设与三件套:平稳性、自相关和模型选型

3.1 降雨量为什么不是普通回归问题:时间顺序是信息的载体

很多新手会问:都是特征预测标签,为什么降雨量预测不能用 sklearn 的线性回归?原因是降雨量数据里真正有用的信息不在特征的取值,而在特征出现的先后顺序。今天的降雨量往往和昨天、前天的降雨量相关,这种相关性叫自相关,它只能通过保留时间顺序来捕捉。普通回归要求样本独立,把日期随机打乱后模型照样能训练,但预测的是“在某种特征条件下平均降雨量”,不是“下一天降雨量”。

所以我复现这类项目时,第一件事不是调模型,而是确认数据的时间跨度。降雨量预测的建模粒度决定了模型复杂度:按天预测,用 ARIMA 或 SARIMA 就够;按小时预测,数据量大了可以上 LSTM;按年预测,样本量太小,什么模型都难做出亮点。zip 包里如果只给了几十条数据,超出了模型能力范围的期望,得先调低预期。

3.2 平稳性检验:adfuller 的三个返回值怎么读

时间序列模型大多数要求序列平稳,意思是均值和方差在时间上没有系统性变化。降雨量明显不满足这个条件,旱季雨季交替让均值周期波动,所以建模前必须做平稳性检验。statsmodels 里最常用的就是 ADF 检验,代码如下:

from statsmodels.tsa.stattools import adfuller import pandas as pd df = pd.read_csv("rainfall.csv", parse_dates=["date"], index_col="date")["rain"] result = adfuller(df.dropna()) print("ADF 统计量:", result[0]) print("p 值:", result[1]) print("临界值:", result[4])

逻辑说明:adfuller的原假设是序列存在单位根,即非平稳。返回的第二个值 p 值小于 0.05 时拒绝原假设,认为序列平稳。实际操作中只看 p 值就够,小于 0.05 认为过关,大于 0.05 就需要差分。降雨量序列绝大多数情况下 p 值远大于 0.05,所以要进入下一步处理。

参数说明:dropna()在检验前删掉空值,因为 ADF 不接受 NaN;result[4]是三个置信水平下的临界值,用来和result[0]对比,统计量小于临界值也表示平稳。如果 p 值不达标,常见做法是做一阶差分再检验一次,也就是把今天的值和昨天的值的差变成新序列。这里有个容易误用的点:差分可能会消除趋势,但降雨量的季节性差分往往需要按周期来,比如月度数据做 12 阶差分,而不是只做一阶。

3.3 从 ACF/PACF 到 ARIMA 选型:p 和 q 的经验取舍

平稳性解决后,选型靠自相关图。ARIMA 模型有三个参数:p是自回归阶数,q是移动平均阶数,d是差分阶数。求 p 和 q 的常见做法是画出 ACF 和 PACF 图观察截尾和拖尾,但实际项目里图形判断对新手并不友好,我一般是先用 ACF/PACF 粗定范围,再用 AIC 精调。

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf import matplotlib.pyplot as plt fig, axes = plt.subplots(1, 2, figsize=(12, 4)) plot_acf(df.diff().dropna(), ax=axes[0], lags=20) plot_pacf(df.diff().dropna(), ax=axes[1], lags=20) plt.tight_layout() plt.show()

逻辑说明:df.diff()对原序列做一阶差分,plot_acf和plot_pacf分别画出自相关函数和偏自相关函数图。ACF 图在滞后阶数之后突然截尾,说明 q 取那个阶数;PACF 图截尾的阶数对应 p。降雨量数据通常 ACF 衰减缓慢、呈现周期性,光靠看图很难下结论,所以这里只是给一个候选范围。

参数说明:lags=20表示展示前 20 个滞后的相关系数,对日数据来说 20 天足够;figsize=(12, 4)控制画布宽高,横轴太长会把周期信息压扁。真正的参数选择我会用循环遍历几个候选组合,比较 AIC 值,取最小的那个。注意 AIC 只能在同一数据集上比较,差分阶数改了之后数据集长度会变,不能拿来直接比。

4. 把降雨量数据喂给模型:清洗、重采样和训练集划分的落地写法

4.1 降雨量数据的清洗逻辑:缺测、异常极值和零值堆积

气象数据是所有数据里最不干净的之一,缺测是常态。常见的处理有删除、均值填充和前向填充,但降雨量不能无脑套用,因为它的分布是零值堆积加偶发极端值。一个地方一年里大半时间不下雨,用均值填充会把“无雨”填成“小雨”,直接把分布毁了。我处理这类数据的顺序是先看缺失比例,再决定策略。

df = pd.read_csv("rainfall.csv", parse_dates=["date"], index_col="date") rain = df["rain"].copy() missing_ratio = rain.isna().mean() print("缺失比例:", round(missing_ratio, 4)) rain = rain.mask(rain < 0) # 负值视为异常,置空 rain = rain.fillna(method="ffill").fillna(0) rain = rain.clip(upper=rain.quantile(0.999))

逻辑说明:isna().mean()算出缺失比例,10% 以内可以填充,30% 以上就要谨慎,填出来的序列可能掩盖真实状态。mask(rain < 0)把负值置空,传感器故障常见的表现就是出现负的降水量。fillna(method="ffill")前向填充,对降雨这种短时缺测最稳妥;fillna(0)处理序列开头没有历史值的位置。clip(upper=...)用分位数截断极端值,防止某次特大暴雨把模型参数带偏。

参数说明:quantile(0.999)表示把超过 99.9% 分位的降雨量压到该分位值,相当于手动去掉最极端的天灾级样本。这个参数可以按业务调,做防洪预报警报可以放宽,做常规预测建议收紧。实际项目里我倾向于不删异常样本,而是截断,因为降雨量本身的物理上限并不存在,删掉会导致模型永远没见过极端情况。

4.2 按日重采样和滚动窗口特征:把日期列变成可用的监督学习样本

时间序列模型可以直接吃原始序列,但要上机器学习模型,就得把时间序列转成监督学习样本。核心操作是用shift构造滞后特征:昨天、前天、上周同期的降雨量作为特征,今天的降雨量作为标签。这一步做不好,后面所有模型都是白搭。

feature_df = pd.DataFrame({"rain": rain}) feature_df["lag_1"] = feature_df["rain"].shift(1) feature_df["lag_2"] = feature_df["rain"].shift(2) feature_df["lag_7"] = feature_df["rain"].shift(7) feature_df["target"] = feature_df["rain"].shift(-1) feature_df = feature_df.dropna()

逻辑说明:shift(1)把序列整体下移一行,第 t 行的lag_1值就是第 t-1 天的降雨量;shift(-1)向上移一行,第 t 行的target值是第 t+1 天的降雨量,这正是模型要预测的对象。dropna()删除没有完整历史特征的头部行,因为前面几行没有足够的滞后数据。

参数说明:三个滞后阶数分别对应昨天、前天和一周前,周滞后是为了捕捉星期效应,虽然降雨不像电商销量有那么强的星期规律,但很多地区的天气系统有 7 天左右的周期,留着没坏处。这里也可以用resample("W").mean()做周重采样,当原始数据是小时级时先聚合到日,避免单日数据波动太大。

4.3 训练集与测试集的切分:时序数据不能用 train_test_split

这是时间序列分析和普通机器学习最本质的区别,也是我在代码审查里最先看的地方。train_test_split默认随机切分,会打乱时间顺序,让模型看到未来数据去预测过去,测试集指标虚高得离谱。时序切分必须保证训练集时间在前,测试集时间在后。

split_idx = int(len(feature_df) * 0.8) train = feature_df.iloc[:split_idx] test = feature_df.iloc[split_idx:] X_train, y_train = train[["lag_1", "lag_2", "lag_7"]], train["target"] X_test, y_test = test[["lag_1", "lag_2", "lag_7"]], test["target"]

逻辑说明:int(len(feature_df) * 0.8)算出 80% 位置的行号,iloc[:split_idx]取前 80% 行作为训练集,iloc[split_idx:]取后 20% 作为测试集。iloc是位置切片,不受索引值影响,如果数据索引不是整数,用loc容易踩坑。切完后单独取出特征列和标签列,特征列是滞后值,标签列是未来值。

参数说明:0.8 是常见比例,但时序项目里我建议根据数据长度定。样本量少于 500 时用 0.7 比 0.8 更稳,因为测试集太薄看不到完整季节周期。另一个要点是,切分前先把数据按时间排序,zip 包里的数据偶尔会乱序,用sort_index()排一下再做切分。

5. 时间序列降雨量预测的四个常见坑:现象、原因和止损办法

5.1 日期解析失败:object 类型不是 datetime,索引一塌糊涂

现象:代码跑起来没报错,但画的图横坐标挤成一团,或者模型报“ValueError: Given date string not a date”。我见过不少从 Excel 导出的数据,日期列看起来是2023/1/5,实际上 pandas 读进来是字符串。

原因:read_csv默认把所有列当字符串读,虽然2023/1/5能被 pandas 一眼认出,但2023-01-05和2023/1/5 0:00混在一起时解析结果不稳定,尤其是 Excel 里部分行被存成自定义格式。

解决:读入后做一次强制转换,确认 dtype 是 datetime64。

df["date"] = pd.to_datetime(df["date"], errors="coerce") df = df.dropna(subset=["date"]) print(df["date"].dtype)

pd.to_datetime的errors="coerce"把无法解析的值变成NaT,再通过dropna把它们连同行一起删掉,而不是让脚本在中间环节莫名报错。输出dtype检查是不是datetime64[ns],是才能继续做重采样和滞后特征。

5.2 把非平稳序列直接喂给 ARIMA,ACF 图拖着长尾巴

现象:seasonal_decompose画出的趋势图一路向下,ACF 图衰减极慢,模型训练完的残差仍然有明显模式,预测结果最后变成一条水平线。

原因:降雨量受季节驱动,序列本身非平稳,ARIMA 的d没设够。很多人以为d=1差分一次就万事大吉,实际上月降雨量可能有年度季节性,需要 12 阶差分才能消除周期性。

解决:先用adfuller检验,非平稳再差分,差分后重新检验。

from statsmodels.tsa.stattools import adfuller diff_1 = df["rain"].diff().dropna() print("一阶差分 p 值:", adfuller(diff_1)[1]) seasonal_diff = diff_1.diff(12).dropna() print("去季节差分 p 值:", adfuller(seasonal_diff)[1])

diff(12)对月度数据做 12 阶差分,相当于把今年 1 月与去年 1 月的差作为新序列的值,周期性被剥掉一层。两个 p 值都小于 0.05 才说明序列已经平稳。很多 zip 包里的脚本只写了一次差分,遇到降雨量这种季节性数据必翻车。

5.3 零值太多的降雨量序列,预测结果全是负值

现象:模型没有报错,但预测的降雨量出现大量负值,比如 -3.2mm。业务上完全不可用,气象观测里降水量不可能为负。

原因:ARIMA 这类模型假设误差服从正态分布,预测区间是围绕均值对称的。降雨量数据大量堆积在 0 附近,分布严重右偏,模型学到的均值附近区域就是负数区域。

解决:先看零值占比,超过一半就别硬用 ARIMA 直接拟合原始值,考虑两步建模:先用分类模型预测是否下雨,再用回归模型预测雨量。或者在数据变换层面做处理,比如用np.log1p压缩右偏,预测完再做逆变换。

import numpy as np zero_ratio = (df["rain"] == 0).mean() print("零值占比:", round(zero_ratio, 4)) rain_transformed = np.log1p(df["rain"]) # 压缩右偏分布

log1p对 0 值友好,log(1+0) 等于 0,不会产生负无穷。注意预测结果要用np.expm1做逆变换才能得到真实刻度。零值占比超过 30% 就要承认单模型不够用,直接分两阶段建模反而更省时间。

5.4 requirements.txt 的版本号不干净,复现时库冲突

现象:按pip install -r requirements.txt安装时报一堆“Cannot install XXX”,或者装完之后 import 直接报AttributeError: module 'pandas' has no attribute 'Panel'。

原因:旧项目用的是 pandas 0.x 或非常老的 statsmodels,新版本把很多旧 API 删了。最典型的是pandas.Panel在 1.0 版本后被移除,到处是pd.Panel的脚本在新环境必崩。

解决:看 requirements.txt 里是否写死了pandas==0.23.4这种版本号,如果写了,建议先按旧版本建独立环境,而不是升级库去迁就代码。操作上就是换 python 3.7 或 3.8 装 pandas 0.25,成本比改源码低很多。

conda create -n rain37 python=3.7 conda activate rain37 pip install pandas==0.25.3 statsmodels==0.11.1

不过与其跟老库搏斗,我更推荐花半小时把旧脚本里的pd.Panel、df.append这些过期 API 改成新写法,一劳永逸。时间序列项目的代码量通常不大,迁移成本远低于维护一个古老环境。

6. 用滚动验证检验模型:提前一步的预测与评估技巧

训练测试集一次性切分只是初步验证,它对模型在真实场景下的表现估计过分乐观。真实使用场景是,模型站在今天预测未来 30 天,明天到来后再用真实值校准。这就要求验证方式也按这个节奏走,滚动预测比单次切分更能暴露模型在长期预测时的误差累积。

history = list(train["rain"]) predictions = [] for t in range(len(test)): model = ARIMA(history, order=(1, 0, 1)).fit() yhat = model.forecast(steps=1)[0] predictions.append(yhat) history.append(test["rain"].iloc[t]) # 把真实值滚进历史 mae = sum(abs(p - a) for p, a in zip(predictions, test["rain"])) / len(test) print("滚动验证 MAE:", round(mae, 2))

逻辑说明:训练集作为初始历史窗口,循环中每预测一步就重新训练一次模型,然后把真实观测值加入历史,再预测下一步。这个过程模拟了线上每天更新数据、每天出预测的节奏。abs(p - a)计算绝对误差,累加后求平均得到 MAE。

参数说明:order=(1, 0, 1)是 ARIMA 的基准配置,滚动验证主要测的是流程而不是参数,参数调整放到验证之前做。steps=1表示只预测一步,因为多步预测误差会叠加,滚动验证的结果更真实。评估时除了 MAE,我还会看一眼 R² 和雨量分级命中率,比如把降雨分成无雨、小雨、中雨三个等级,看模型命中哪个等级的比例,这比单个数值指标更有业务参考价值。

如果滚动验证的 MAE 明显高于单次切分,说明模型对新数据适应能力差。这时候可以考虑换 SARIMA 显式加季节项,或者用 Prophet 试基线。LSTM 的门控循环结构对这类长周期序列也有效,但需要更长历史数据,少于 1000 天不建议上。我自己的习惯是先把 ARIMA 调到 AIC 最低,再用它做滚动验证,确认误差规模后才考虑更复杂的模型。

这个方向值不值得投入,判断标准很简单:如果你的数据是按小时或按天记录的、跨度超过三年、零值占比可控(低于 50%),时间序列分析就能交出可用结果;如果只有几十条月度均值,任何模型都救不了。我复现过不少课程设计包,最后能真正跑出稳定预测的,几乎都满足这两个条件。希望这些步骤和踩坑记录帮你在自己的数据上少走一段弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询