☰
基于Python的天气预测与可视化完整源码:从数据清洗到LSTM模型实战
2026/10/12 22:54:53 网站建设 项目流程

简介:这是一套面向Python初学者与数据分析爱好者的天气预测与可视化完整项目源码,适合用于课程设计、毕业设计或自学练手,帮助读者理解从数据采集、模型训练到前端展示的完整链路。压缩包共27个文件,约2.86MB,包含4个Python源码文件承载数据获取、处理与建模逻辑,4个CSV文件存储原始及处理后天气数据,1个Pickle模型文件保存训练结果,1个HTML文件用于前端可视化展示,另有12张jpg图像呈现预测与可视化效果,以及说明文档和编译缓存等辅助文件。目前已有955人学习下载,具备一定参考热度。读者可据此掌握天气数据的清洗与特征处理思路、预测模型的构建与序列化方法,并借助现成图像与网页模板快速复现可视化结果,目录结构清晰,便于按模块拆解学习与二次开发。

1. 从一份天气预测源码说起:Python 到底能预测到什么程度

很多人第一次搜「基于Python的天气预测与可视化完整源码」,心里想的其实是:下载下来跑一下,明天出门要不要带伞就有答案了。我先把预期摆正——用 Python 做天气预测,主流做法不是从零训练一个「明天几点下雨」的模型,而是拿公开气象历史数据做时间序列建模,预测气温、湿度、气压这类连续变量的短期走势,再用可视化把趋势画出来。它能解决的是「给定过去 N 天的观测,未来 3 到 7 天的数值大概怎么走」,适合数据分析入门者、想练完整项目流程的在校生,以及需要给业务系统加一个天气趋势模块的后端工程师。真正决定成败的不是模型多花哨,而是数据从哪来、特征怎么构造、评估怎么做。下面按我实际搭过的一套流程拆开讲。

2. 数据从哪来:气象数据源选型与清洗的四个关键动作

2.1 先想清楚用历史再分析数据还是实时观测

做天气预测,第一步不是写模型,是确定数据源。常见做法分两类:一类是历史再分析数据集,比如各类公开气象档案,字段规整、时间连续、适合训练和回测;另一类是实时观测接口,适合做在线推理和可视化刷新。我的建议是训练阶段用历史数据,推理阶段接实时接口,两者字段对齐后再串起来。如果你只是想跑通流程,直接找一份带时间戳、气温、湿度、气压、风速的 CSV 就够了,不必一上来就接复杂接口。

选数据时盯住三个指标:时间粒度(小时级还是天级)、缺失率、时间跨度。小时级数据做短期预测更有价值,但缺失和异常也更多。跨度至少覆盖一整年,否则模型学不到季节性。很多人翻车就翻在拿三个月的数据训练,结果模型在换季时预测全偏。

2.2 用 pandas 做缺失值处理和异常值剔除

拿到原始数据后,先做清洗。下面这段是我常用的最小清洗脚本,逻辑是:解析时间列、按时间排序、对数值列做线性插值补缺、再用分位数把极端异常值截断。

import pandas as pd import numpy as np # 读取原始气象数据,假设列名为 time, temp, humidity, pressure df = pd.read_csv("weather_raw.csv") # 1. 时间列解析并设为索引,保证后续按时间重采样 df["time"] = pd.to_datetime(df["time"]) df = df.sort_values("time").set_index("time") # 2. 数值列线性插值补缺,limit_direction 保证首尾也能补 num_cols = ["temp", "humidity", "pressure"] df[num_cols] = df[num_cols].interpolate(method="linear", limit_direction="both") # 3. 用 1% 和 99% 分位数截断异常值,避免传感器跳变污染训练 for col in num_cols: low, high = df[col].quantile([0.01, 0.99]) df[col] = df[col].clip(low, high) # 4. 按小时重采样,缺失小时用前值填充 df = df.resample("1h").mean().ffill() print(df.describe())

逻辑说明:插值解决的是短时缺失,分位数截断解决的是传感器异常尖峰,重采样保证时间轴等间隔——这三步做完,数据才能喂给时间序列模型。参数上,quantile([0.01, 0.99])的阈值可以按数据质量调整,数据越脏阈值收得越紧;resample("1h")的频率要和你预测目标一致,想预测天级就换成"1D"。

2.3 特征工程:把时间戳拆成模型能吃的信号

原始时间戳对模型没用,要拆成周期特征。小时、星期、月份都可以做正余弦编码,让模型理解「23 点和 0 点很近」这种循环关系。另外加滑动窗口统计量,比如过去 6 小时均值、过去 24 小时最大值,这些是预测短期趋势最有效的特征。

# 周期特征:小时和月份做正余弦编码 df["hour_sin"] = np.sin(2 * np.pi * df.index.hour / 24) df["hour_cos"] = np.cos(2 * np.pi * df.index.hour / 24) df["month_sin"] = np.sin(2 * np.pi * df.index.month / 12) df["month_cos"] = np.cos(2 * np.pi * df.index.month / 12) # 滑动窗口特征:过去 6 小时和 24 小时的均值与极值 df["temp_roll6"] = df["temp"].rolling(6).mean() df["temp_roll24_max"] = df["temp"].rolling(24).max() df = df.dropna() # 去掉窗口产生的空值

窗口长度是核心参数:6 小时抓日内波动,24 小时抓日周期。想预测更长的趋势就加 72 小时窗口,但特征一多,过拟合风险也上来,建议先用这两组跑基线。

3. 模型怎么选:从线性回归到 LSTM 的取舍与最小实现

3.1 先用线性模型定基线,别一上来就上深度学习

我见过太多人直接抄 LSTM 代码,结果数据量不够、调参不会,最后连基线都不如。正确顺序是:先跑一个线性回归或 ARIMA 做基线,记录误差;再上树模型或神经网络,只有明显超过基线才值得用。基线模型的价值在于给你一个「及格线」,后面所有复杂模型都拿它对比。

用 scikit-learn 做线性回归基线,特征就是上一节的周期特征加滑动窗口,目标是预测下一时刻气温:

from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error features = ["hour_sin", "hour_cos", "month_sin", "month_cos", "temp_roll6", "temp_roll24_max", "humidity", "pressure"] X = df[features] y = df["temp"].shift(-1) # 预测下一小时气温 X, y = X[:-1], y[:-1] # 对齐长度 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, shuffle=False) # 时间序列不能随机打乱 model = LinearRegression().fit(X_train, y_train) pred = model.predict(X_test) print("MAE:", mean_absolute_error(y_test, pred))

关键点:shuffle=False是时间序列的硬规矩,随机打乱会让未来数据泄漏到训练集,评估结果虚高。MAE 记下来,这就是你的基线。

3.2 LSTM 的输入形状和三个必调参数

如果基线误差不够低,再考虑 LSTM。LSTM 吃的是三维张量:样本数、时间步长、特征数。时间步长决定模型回看多久,常用 24 或 48。下面是最小可跑的实现:

import torch import torch.nn as nn class WeatherLSTM(nn.Module): def __init__(self, input_size, hidden_size=64, num_layers=2): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) self.fc = nn.Linear(hidden_size, 1) # 输出下一时刻气温 def forward(self, x): out, _ = self.lstm(x) return self.fc(out[:, -1, :]) # 取最后一个时间步 # 构造滑窗样本:用过去 24 小时预测下一小时 def make_sequences(data, seq_len=24): xs, ys = [], [] for i in range(len(data) - seq_len): xs.append(data[i:i+seq_len]) ys.append(data[i+seq_len, 0]) # 第 0 列是气温 return torch.tensor(xs, dtype=torch.float32), \ torch.tensor(ys, dtype=torch.float32).unsqueeze(1)

三个必调参数:hidden_size一般 32 到 128,数据少就往小调;num_layers1 到 2 层够用,再深容易过拟合;学习率从 1e-3 起,配合早停。训练时把验证集损失盯住,连续几轮不降就停,这是最省事的防过拟合手段。

3.3 评估指标别只看一个,MAE 和 RMSE 一起看

MAE 反映平均误差,RMSE 对大误差更敏感。两个一起看,如果 RMSE 远大于 MAE,说明存在个别预测严重偏离的样本,要去查是不是异常天气事件没被特征覆盖。另外建议画预测值和真实值的对比曲线,肉眼看趋势比数字更直观,这一步直接接到下一章的可视化。

4. 可视化怎么做:把预测结果画成能看懂的图

4.1 用 Matplotlib 画预测对比曲线的最小模板

可视化不是装饰,是验证手段。下面这段画真实值、预测值、置信区间三条线,横轴时间,纵轴气温:

import matplotlib.pyplot as plt plt.figure(figsize=(12, 5)) plt.plot(y_test.index, y_test, label="真实值", linewidth=1.5) plt.plot(y_test.index, pred, label="预测值", linestyle="--") plt.fill_between(y_test.index, pred - 1.5, pred + 1.5, alpha=0.2, label="误差带") plt.xlabel("时间") plt.ylabel("气温") plt.legend() plt.xticks(rotation=45) plt.tight_layout() plt.savefig("prediction.png", dpi=150)

fill_between的上下界用验证集 MAE 的 1.5 倍,给读者一个误差范围的直观感受。横轴标签太密是常见问题,rotation=45加tight_layout基本能解决。

4.2 交互式图表:ECharts 和 Plotly 怎么选

静态图适合报告,交互图适合网页展示。如果前端是 Web,用 ECharts 最省事,数据用 JSON 传过去即可;如果只在 Python 环境里看,Plotly 一行代码就能出可缩放图表。选型看交付场景:给非技术同事看,Plotly 的 HTML 导出最方便;嵌到已有前端项目,ECharts 生态更成熟。别为了炫技上大屏方案,数据量不大的时候纯属浪费。

5. 避坑与排查:天气预测项目里最容易翻车的五件事

5.1 数据泄漏:随机打乱时间序列

现象:测试集 MAE 低得离谱,上线后预测完全不准。原因:用train_test_split默认随机打乱,未来数据混进了训练集。解决:时间序列必须按时间切分,shuffle=False,或者直接用前 80% 训练、后 20% 测试。

5.2 特征穿越:滑动窗口用了未来值

现象:模型在训练集表现极好,验证集一塌糊涂。原因:构造滑动特征时用了rolling但没注意对齐,或者目标变量shift方向搞反。解决:目标一定是shift(-1)表示下一时刻,特征只能用当前及过去的数据,构造完打印几行肉眼核对。

5.3 缺失值处理过度:插值把真实波动抹平

现象:模型预测曲线过于平滑,抓不住突变。原因:长时间缺失用插值硬补,把真实的气象突变填成了平滑趋势。解决:缺失超过连续 3 个点的区段直接丢弃,不要强行插值;短缺失才用插值。

5.4 评估指标选错:分类指标用在回归任务上

现象:准确率 95% 但预测值完全没法用。原因:把气温预测当分类做,或者用了不合适的指标。解决:回归任务用 MAE、RMSE、MAPE,别用准确率;如果一定要分类(比如是否下雨),单独做一套分类流程。

5.5 可视化横轴时间错乱

现象:曲线走势对但时间标签全乱。原因:DataFrame 索引没设成时间类型,或者保存时丢了索引。解决:pd.to_datetime后set_index,画图前print(df.index[:5])确认是时间类型。

6. 把预测结果接进实际业务:一个可复用的推理封装技巧

前面跑通的是离线流程,真正要用起来,得把模型封装成一个可调用的推理函数,输入最近 N 小时观测,输出未来几小时预测。我一般会写一个类,把特征构造、模型加载、预测三步封在一起,这样换模型或换特征时只改一个地方。

class WeatherPredictor: def __init__(self, model_path, seq_len=24): self.model = torch.load(model_path) self.model.eval() self.seq_len = seq_len def predict(self, recent_df): # recent_df 需包含与训练一致的数值列,长度 >= seq_len seq = recent_df.tail(self.seq_len).values x = torch.tensor(seq, dtype=torch.float32).unsqueeze(0) with torch.no_grad(): return self.model(x).item()

这个封装的价值在于:推理时不再关心特征工程细节,只要保证输入列顺序和训练一致。列顺序是最容易出错的地方,建议把特征列名存成常量,训练和推理共用。

验证封装是否正确,有个笨但有效的办法:拿训练集最后一段数据,用封装函数预测,和离线流程的预测结果对比,数值一致才说明封装没引入偏差。这一步我每次都会做,省得后面排查半天。

最后说个习惯:我做完任何一个预测项目,都会把基线模型的误差、最终模型的误差、以及两者差距记在一个小本子上。差距不明显就说明复杂模型白上了,下次直接省掉。天气预测这个方向,数据质量永远比模型复杂度重要,把清洗和特征做扎实,线性模型也能给出能用的结果。希望帮到你。

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

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

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

立即咨询