简介:面向Python毕业设计场景的天气预测与可视化项目,完整覆盖天气数据的采集、清洗、模型训练、结果可视化与预测展示流程,代码注释详细,编程基础薄弱的新手也能读懂并完成部署。项目压缩包共24个文件,以4个功能明确的Python脚本为核心,分别负责数据获取、预处理、模型训练与主程序运行,配合4个CSV天气数据集、1个pkl模型文件、1个HTML可视化页面,以及12张运行效果示意图和1份说明文档,整体仅1.42MB,轻量易部署。当前已有704人学习下载,是个人手打的98分项目,获得导师高度认可,适合作为毕业设计、期末大作业或课程设计的高分参考。直接运行主程序即可查看天气预测与可视化效果,说明文档梳理了项目结构和实现思路,模块划分清晰,可替换数据集并改写成其他数据分析类项目,实用性强。
1. 天气预测和可视化,毕设怎么做才能让评委眼前一亮
天气预测与天气可视化是一个被写烂了的选题,但多数人的成品只是一张曲线图加一段“基于历史数据预测未来温度”的说明。真正能拉开差距的,是把数据采集、模型评估和可视化交互做成一条可验证的流水线,而不是把截图贴进论文。这个项目涉及的核心动作是:从公开气象接口拉取历史数据,做清洗和特征构造,用时间序列模型预测未来几天的气温与降水概率,再把这些结果渲染成折线图、热力图和地理分布图。适合已经有 Python 基础、想用一份完整代码和文档快速完成毕业设计的同学,也适合想理解“预测+可视化”工程化套路的人。下面这套方案不依赖商业气象数据,免费接口就能跑通,关键是每一步都有可检查的中间结果。
2. 数据从哪来,预测用什么模型:先定技术路线
任何一个天气预测项目,数据质量决定了模型上限。与其一开始就追求复杂的深度学习,不如先把数据链路做扎实。这一章会解决“数据源怎么选”“预测模型怎么对比”“特征怎么构造”这三个问题,并给出一段可直接运行的最小训练代码。
2.1 气象数据源怎么选:免费接口与本地数据
常见的气象数据获取方式有三种:开放 API、公开数据集、自行采集。对毕业设计来说,优先推荐使用开放 API,因为它们有稳定的响应格式和明确的字段含义,能省掉大量清洗脏数据的时间。
我一般会选择 OpenWeatherMap 或者和风天气的免费套餐,注册后即可获得 API Key。每月有免费调用次数限制,对毕设来说足够用。以 OpenWeatherMap 为例,请求当前天气和 5 天预报的接口路径是:
curl "https://api.openweathermap.org/data/2.5/forecast?q=Beijing&appid=你的API_KEY&units=metric&lang=zh_cn"返回值是一个 JSON,里面的list数组每 3 小时一个预测点,包含main.temp、main.humidity、weather[0].description等字段。为了拿到历史数据做训练,可以使用https://api.openweathermap.org/data/2.5/onecall/timemachine这个接口,它支持按时间戳回溯过去 5 天的数据。如果接口不好申请,也可以直接去 Kaggle 下载城市级历史气象 CSV,字段通常包括date、temperature_avg、precipitation等。
参数说明:q=Beijing是城市名,units=metric让温度返回为摄氏度,lang=zh_cn让天气描述中文显示。把appid换成你自己的 Key 即可。考虑到接口返回的字段名在不同版本间有差异,建议在代码里做一次字段映射,统一成内部使用的date, temp, humidity, weather四个字段。
2.2 时间序列预测:ARIMA、Prophet还是LSTM?
天气数据本质上是带周期性的时间序列,温度会按天和按年波动,降水则是间歇性的。对这一类数据,三个模型各有适用场景:
| 模型 | 适合场景 | 优点 | 缺点 |
|---|---|---|---|
| ARIMA | 单变量、平稳或差分后平稳的序列 | 可解释性强,训练快 | 对非线性趋势和节假日效应处理弱 |
| Prophet | 强季节性、有节假日影响的数据 | 自动处理周期性,缺失值容忍度高 | 对突变点敏感,调参需要经验 |
| LSTM | 长序列、多变量输入 | 能捕捉复杂非线性关系 | 需要大量数据,训练慢,超参敏感 |
对毕设来说,建议把 ARIMA 作为基线模型,再尝试 Prophet 或 LSTM 作为改进方案。这样论文里可以写“对比实验”,而不是只跑一个模型。注意不要一上来就用 LSTM,很多答辩评委第一句话会问“你的训练集多大”,300 条数据训练 LSTM 很难有说服力。
2.2.1 模型选型的判断依据
先做平稳性检验。可以用statsmodels里的adfuller函数检查 Adf 检验的 p 值。如果 p 值大于 0.05,说明序列不平移,需要做一阶差分。ARIMA 的d参数通常就来自差分次数。温度序列一般一阶差分就能平稳,降水序列可能经过平方根变换后再差分。
2.3 数据清洗与特征构造
从接口拿到的数据不能直接进模型,至少有四件事要做:去重、补缺失、归一化、构造滞后特征。
去重是指同一个时间点出现两次时保留后一条;补缺失可以用前后均值插值;归一化对 LSTM 尤其重要,否则 loss 会震荡。滞后特征是指用前 1 天、前 2 天……前 7 天的温度作为当前预测的特征,这能让模型看到历史趋势。
常用做法是用一个函数串起所有处理步骤:
import pandas as pd def build_features(df, target='temp', lags=7): data = df[['date', target]].copy() data['date'] = pd.to_datetime(data['date']) data = data.sort_values('date').drop_duplicates(subset='date', keep='last') for i in range(1, lags + 1): data[f'lag_{i}'] = data[target].shift(i) data['hour'] = data['date'].dt.hour data['dayofweek'] = data['date'].dt.dayofweek data = data.dropna().reset_index(drop=True) return data这段代码的作用是生成 lags 列,把历史 7 天的温度拼到当前行。shift(i)是 pandas 里把整个序列下移 i 行的操作,dropna()删除前 7 行没有完整历史的数据。hour和dayofweek特征可以帮助模型捕捉日内和一周内的周期性。
2.4 最小可复现的训练代码
准备好特征后,先跑一个 ARIMA 作为基线。下面的代码使用了statsmodels库,它内置了 ARIMA 模型,不需要自己写梯度下降算法。
import pandas as pd from statsmodels.tsa.arima.model import ARIMA # df 是经过清洗的 DataFrame,包含 date 和 temp series = df.set_index('date')['temp'].astype(float) series = series.asfreq('H') # 按小时对齐 series = series.interpolate() # 填补缺失 model = ARIMA(series, order=(2, 1, 2)) fit = model.fit() # 预测未来 24 小时 forecast = fit.forecast(steps=24) print(forecast)order=(2, 1, 2)分别对应 AR、差分、MA 的阶数。(2,1,2)是一个常见起点,但不是最优值。后面可以结合 AIC 做网格搜索。asfreq('H')的作用是把索引对齐到小时频率,缺失时间戳会变成 NaN,再由interpolate()线性填充。forecast(steps=24)表示预测下一个 24 个时间点,如果数据是小时粒度的,就代表未来一天。
训练完成后,一定要把预测结果和真实值画在一起看,而不是只打印几个数字。这一步会直接暴露模型是否只预测了“昨天的温度”,也就是滞后期过于严重的问题。
3. 天气可视化:从折线图到地图热力图的实现
可视化的价值不只是展示结果,它还能帮你和评委快速判断模型是否合理。一个专业的天气可视化项目至少要包含时序趋势图、多指标对比图和地理分布图。这里我会用 Matplotlib 和 Pyecharts 实现一套可交互的看板,并说明每个图表的数据格式和参数含义。
3.1 用Matplotlib绘制温度变化曲线
Matplotlib 适合输出论文用的静态图,简洁且可控。绘制未来 24 小时温度曲线时,需要将 DataFrame 的索引转成 datetime 类型,防止横坐标乱排。
import matplotlib.pyplot as plt forecast_df = pd.DataFrame({ 'time': pd.date_range(start=df['date'].iloc[-1], periods=24, freq='H'), 'temp': forecast }) plt.figure(figsize=(10, 4)) plt.plot(forecast_df['time'], forecast_df['temp'], marker='o', linewidth=2) plt.title('24-Hour Temperature Forecast') plt.xlabel('Time') plt.ylabel('Temperature (°C)') plt.grid(alpha=0.3) plt.xticks(rotation=45) plt.tight_layout() plt.savefig('forecast_temperature.png', dpi=150)这个图能直接看到温度波动的相位和幅度。如果曲线是条直线,说明模型退化成“用历史均值预测未来”,这时候要检查差分阶数或者特征构造是否有问题。savefig里的dpi=150保证图片在 Word 论文中不失真。
3.2 用Pyecharts实现交互式天气仪表盘
静态图不能满足演示需求。Pyecharts 是百度 ECharts 的 Python 封装,生成的 HTML 文件可以双击打开,不需要启动服务器。
3.2.1 折线图与柱状图组合
下面代码实现了一个双轴图:折线表示温度,柱状表示降雨概率。这种组合图非常适合展示天气数据的多维信息。
from pyecharts.charts import Line, Bar from pyecharts import options as opts time_list = forecast_df['time'].dt.strftime('%m-%d %H:%M').tolist() temp_list = forecast_df['temp'].round(1).tolist() rain_list = [0.1, 0.2, 0.3, 0.0, 0.0, 0.1] # 示例概率 line = ( Line() .add_xaxis(time_list) .add_yaxis("温度(°C)", temp_list, yaxis_index=0) .extend_axis(yaxis=opts.AxisOpts(name="降雨概率", type_="value", min_=0, max_=1)) .set_global_opts(title_opts=opts.TitleOpts(title="24小时天气预测")) ) bar = ( Bar() .add_xaxis(time_list) .add_yaxis("降雨概率", rain_list, yaxis_index=1) ) line.overlap(bar) line.render("weather_dashboard.html")在 Pyecharts 中,add_yaxis的yaxis_index参数指定该系列挂在哪个 Y 轴,extend_axis可以创建第二个 Y 轴。注意两个图必须共用xaxis_index为 0 的 X 轴,否则图表会错位。overlap方法负责把两个图合并在同一个坐标系中,这是制作组合图的关键。
3.3 天气地图:地理坐标与热力图
如果项目涉及多个城市,可以用地图热力图展示各地温度或空气质量。Pyecharts 的地图组件需要先注册地理坐标,示例如下:
from pyecharts.charts import Map, Geo geo_data = [ ["北京", 25], ["上海", 27], ["广州", 30], ["成都", 23], ] geo = ( Geo() .add_schema(maptype="china") .add_coordinate("北京", 116.4074, 39.9042) .add_coordinate("上海", 121.4737, 31.2304) .add_coordinate("广州", 113.2644, 23.1291) .add_coordinate("成都", 104.0665, 30.5723) .add("温度", geo_data, type_="effectScatter") .set_series_opts(label_opts=opts.LabelOpts(is_show=False)) ) geo.render("weather_map.html")add_coordinate需要手动传入经纬度,因为有些地名没有内置在内。type_="effectScatter"让散点带上涟漪效果,演示时更醒目。如果你用国家基础地理信息平台的 GeoJSON,还可以直接用Map组件绘制区域着色图,但需要额外下载地图 JSON 文件,这里不展开。
3.4 可视化组件选型表
| 可视化需求 | 推荐组件 | 输出形式 | 注意事项 |
|---|---|---|---|
| 温度时序趋势 | Matplotlib Line | PNG | 设置 dpi,调整横坐标密度 |
| 多指标对比 | Pyecharts Line+Bar | HTML | 注意 yaxis_index 轴索引 |
| 城市分布 | Pyecharts Geo | HTML | 手动添加坐标,检查名称 |
| 综合看板 | Pyecharts Tab/Page | HTML | 适合集成多个图表 |
4. 把项目组装成可交付的毕设:结构、文档与排错
光有代码还不够,毕业设计需要完整的项目结构和说明文档。这一章会给出一个能直接套用的目录结构,以及关键模块的职责拆分,最后列出运行阶段最常见的报错和解决办法。
4.1 推荐的项目目录结构
如果你是从零开始,建议采用下面的结构。它遵循“数据、代码、文档、输出”四分离原则,答辩时演示和查日志都很清晰。
weather_project/ ├── config/ │ └── settings.py # 存放 API Key、城市列表等配置 ├── data/ │ ├── raw/ # 原始 JSON/CSV │ └── processed/ # 清洗后的 CSV ├── src/ │ ├── data_fetcher.py # 拉取 API 数据 │ ├── data_processor.py # 清洗与特征工程 │ ├── model_trainer.py # 训练与预测 │ └── visualizer.py # 生成图表与 HTML ├── output/ │ ├── figures/ # PNG 图片 │ └── report/ # HTML 看板 ├── docs/ │ ├── 需求文档.md │ ├── 设计文档.md │ └── 测试报告.md └── main.py # 入口,串联所有模块config/settings.py单独剥离配置的好处是:当你的 API Key 泄露或者要换城市时,不需要改动其他代码。入口main.py只做流程调度,不写具体业务逻辑。
4.2 核心模块设计:数据采集、预测、可视化三分离
模块之间用函数返回值传递数据,而不是共享全局变量。以data_fetcher为例:
# src/data_fetcher.py import requests from config.settings import API_KEY, BASE_URL def fetch_forecast(city, days=5): params = { 'q': city, 'appid': API_KEY, 'units': 'metric', 'lang': 'zh_cn', 'cnt': days * 8 # 每3小时一个点,一天8个点 } resp = requests.get(BASE_URL, params=params, timeout=10) resp.raise_for_status() data = resp.json() return data['list']cnt参数控制返回点数,timeout=10防止请求卡死。raise_for_status()会在 HTTP 4xx/5xx 错误时抛出异常,方便排查是 Key 错误还是网络问题。
在model_trainer里,训练函数只接受处理后的 DataFrame,返回模型对象和预测结果列表;visualizer只负责把数据渲染成图表,不关心数据从哪来。这样每个文件都可以单独测试。
4.3 用配置文件管理API Key与参数
config/settings.py的典型写法如下:
import os API_KEY = os.getenv('WEATHER_API_KEY', '在这里填写你的key') BASE_URL = 'https://api.openweathermap.org/data/2.5/forecast' CITIES = ['Beijing', 'Shanghai', 'Guangzhou'] FORECAST_HOURS = 24 MODEL_ORDER = (2, 1, 2)使用环境变量WEATHER_API_KEY可以避免把 Key 硬编码在代码里。如果你不想配置环境变量,也可以直接填字符串,但千万别把代码发给别人之前忘记删 Key。MODEL_ORDER是 ARIMA 的阶数,放在配置中便于统一调参。
4.4 常见运行报错与解决方案
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
KeyError: 'list' | 接口返回的是错误信息 JSON,比如城市名不存在或 API Key 无效 | 先打印 resp.text 查看具体返回 |
ValueError: Unknown string format | 时间字符串格式不统一 | 使用pd.to_datetime(df['dt'], unit='s')处理 Unix 时间戳 |
statsmodels.exceptions.PandasWrapper: pandas版本不兼容 | statsmodels 要求特定 pandas 版本 | 直接安装最新版 statsmodels,或降级 pandas 到 2.0 |
TypeError: object of type 'Response' has no len() | 没有调用.json()就使用返回对象 | 检查是否漏掉resp.json() |
遇到报错时,我习惯先加print()打印关键变量的 shape 和 dtype,因为 80% 的问题发生在数据格式不符合模型输入要求。
4.5 文档清单:需求文档、设计文档、测试报告怎么写
毕设文档不需要写得像软件工程教材一样长,但四个部分必须有:需求背景、技术栈选型、模块设计、测试验证。需求文档中要写明“系统能够获取哪些城市的实时天气”“预测未来几小时”“输出哪些图表”。
设计文档可以包含上述目录结构图和数据流程图。测试报告重点写模型的预测误差、API 调用的成功率、以及可视化页面在不同浏览器下的兼容性。建议准备一个 “实验记录” 部分,把训练集大小、模型参数、RMSE 数值列成一个表格,这是答辩加分的核心。
5. 进阶:用自动调参与定时任务让项目“活”起来
如果想让毕设从“能用”变成“有自己的亮点”,可以加入自动调参、缓存机制和定时更新三个能力。这一章给出每个能力的实现思路和关键代码。
5.1 模型参数自动搜索
ARIMA 的order参数不值得手调。可以用网格遍历候选值,根据 AIC 自动选择最优组合:
from statsmodels.tsa.arima.model import ARIMA import itertools best_aic = float('inf') best_order = None p_range = range(0, 4) q_range = range(0, 4) for p, q in itertools.product(p_range, q_range): try: model = ARIMA(series, order=(p, 1, q)) fit = model.fit() if fit.aic < best_aic: best_aic = fit.aic best_order = (p, 1, q) except Exception: continue print(best_order, best_aic)itertools.product生成所有 (p, q) 组合,try/except跳过不收敛的参数组合。AIC 越小代表模型在拟合能力和复杂度之间取得平衡。一个常见误区是只看训练集上的 AIC,应该在时间序列交叉验证下也会选类似结果,才能说明参数稳定。
5.2 用SQLite做缓存,避免重复请求
免费 API 有访问频率限制,比如每分钟 60 次。如果你的代码在调试时反复请求同一个城市的预报,很容易触发限流。缓存历史请求结果到本地 SQLite 是一个轻量级方案。
import sqlite3 conn = sqlite3.connect('weather_cache.db') conn.execute('''CREATE TABLE IF NOT EXISTS cache( city TEXT, date TEXT, payload TEXT, PRIMARY KEY(city, date) )''') def get_cached(city, date): cur = conn.execute('SELECT payload FROM cache WHERE city=? AND date=?', (city, date)) row = cur.fetchone() return row[0] if row else None def set_cache(city, date, payload): conn.execute('REPLACE INTO cache(city, date, payload) VALUES(?,?,?)', (city, date, payload)) conn.commit()这里把接口返回的 JSON 字符串存进payload字段,用城市和日期作为联合主键。REPLACE INTO在已有记录时直接覆盖,保证缓存始终是最新数据。在data_fetcher中先查缓存,命中就直接返回,否则请求 API 并写缓存。
5.3 定时更新与预测结果的验证
毕业论文通常需要展示系统能在一段时间内持续运行。可以用schedule库实现每天定时拉取数据并重新训练预测。这个库比APScheduler更轻量,适合简单任务。
import schedule import time def job(): print("Updating forecast...") from src.data_fetcher import fetch_forecast from src.model_trainer import train_and_predict train_and_predict() schedule.every().day.at("06:00").do(job) while True: schedule.run_pending() time.sleep(60)这里要注意:time.sleep(60)意味着检查周期是 60 秒,对每日一次的定时任务足够。如果你的电脑休眠,醒来后调度器会立即补跑错过的任务吗?schedule库不会,它只检查当前时间是否匹配。要补充漏跑逻辑,可以在启动时手动判断当前时间并触发一次任务。
5.4 最终演示:把预测误差和可视化放在一起
一个综合演示脚本应该同时完成三件事:拉取历史真实数据、计算预测值和真实值的 RMSE、输出误差对比图。建议在visualizer.py里新增一个plot_accuracy(y_true, y_pred)函数,把真实曲线和预测曲线画在同一个坐标系上,并在图例中标注 RMSE 数值。答辩时这张图比任何文字都更有说服力。
最后,别忘了把定时任务、自动调参、缓存机制写进文档的“创新点”一节。当评委问“你的模型预测会不会越来越不准”时,你可以展示误差曲线和每日重训练的调度日志,这就是“活”的项目。
本文还有配套的精品资源,点击获取