news 2026/9/17 1:05:09

基于Python的天气预测课程设计:从数据清洗到可视化全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Python的天气预测课程设计:从数据清洗到可视化全流程

简介:基于Python的天气预测与可视化项目源码,面向高校Python课程设计或期末大作业场景,覆盖天气数据采集、清洗、建模、预测与可视化完整流程,无需修改即可运行,适合具备基础Python语法、希望完成数据分析和可视化综合项目的学生作为高分结课参考。压缩包共24个文件,大小约1.42MB,核心包括4个Python脚本(数据采集、数据处理、模型训练、主程序)、4个CSV天气数据集、已训练好的Model.pkl模型文件、一个HTML天气展示页面,以及12张结果图表和readme使用说明,目录结构便于按模块查阅。已有575人学习下载。项目附带的说明文档和可视化结果图能帮助快速对照运行效果,数据与模型齐全,可深入理解天气预测从数据清洗到结果展示的实现细节,也便于在此基础上二次扩展或改写,完成课程设计报告。

1. 从天气数据到预测结果:这个项目的真实边界

很多拿到“天气预测”作业的人第一反应是直接上 LSTM,但真正能拿高分的课程设计,胜在数据链路完整,而不是模型有多复杂。这个项目用 Python 把数据采集、清洗、建模、可视化串成了一条可运行的主线,比较适合期末大作业或者课程设计拿来当骨架,改一改就能应付答辩。项目里真正有含金量的不是某个算法,而是那套分工明确的关系:GetData.py 负责把天气数据抓下来,ProcessData.py 负责清洗和特征构造,GetModel.py 训练好模型并保存成 Model.pkl,最后 main.py 加载模型,把预测结果渲染到天气网.html 页面上。理解了这些脚本之间的数据传递关系,即使不改一行代码,也能把答辩讲清楚,顺带知道怎么扩展城市、怎么更新模型。

2. 数据链路:GetData.py 与三个 CSV 的时间序列拼图

2.1 三个 CSV 的分工:训练集、测试集与全国实况

打开项目后第一眼值得注意的就是 date_train.csv、date_test.csv 和 china_today.csv 这三个文件。date_train.csv 是历史训练样本,里面保存着带历史真实观测值的天气记录,用来训练模型;date_test.csv 是准备预测的样本,特征结构与训练集一致,但没有完整的历史标签,或者标签留作验证用;china_today.csv 则像一张“全国今日天气快照”,为可视化页面提供地理维度的数据来源,比如地图上每个城市的当前温度、湿度、天气现象。

这三个文件不能混用。最常见的问题是把 china_today.csv 当训练集用,训练时看着没报错,但实际上它缺失大量历史字段,预测结果完全不可解释。动手第一步应当是读入文件,确认数据类型,而不是直接跑 main.py。下面这段代码建议每次打开项目先执行一遍:

import pandas as pd # 用 parse_dates 把 date 列转成 datetime,后面才能按时间做特征 train = pd.read_csv('date_train.csv', parse_dates=['date']) test = pd.read_csv('date_test.csv', parse_dates=['date']) today = pd.read_csv('china_today.csv') # 看字段类型与缺失值,先摸清数据长什么样 print(train.dtypes) print(test.columns) print(today.head())

参数说明:parse_dates=['date']会在读入时自动将日期字符串解析为 datetime 类型,比事后pd.to_datetime更省一步;打印dtypes是为了发现哪些列仍然是 object 类型,那些列往往是后续清洗的重点区域。日期特征决定了一个天气预测项目能不能按“时间序列”去处理,如果这一步漏掉,后面构造“月份”“星期”之类的特征会直接报错。

从结构上看,这个项目不是多个 CSV 的无序堆放,而是有一个隐性的时间线:train 是过去,test 是未来,today 是当前状态。答辩时能说出这一条,分数起点就不一样。

2.2 清洗与类型转换:pandas 能容忍的脏数据长什么样

爬虫拿到的天气数据里面,最容易出问题的不是格式错误,而是“看起来是数字,实际是字符串”。比如接口返回的null值被保存成"None",湿度字段混入"100.0%",温度字段出现"--"。这时候直接用int()转换会抛异常,正确做法是用 pandas 的pd.to_numeric以宽松模式清洗。

ProcessData.py 里应该承担这层职责,把原始 CSV 转成模型能直接读取的数值型 DataFrame。以下是常见的清洗规则,对应代码可以直接用在课程设计里:

# 将字符串型数字转成浮点数,转换失败的位置保留 NaN train['temp_max'] = pd.to_numeric(train['temp_max'], errors='coerce') train['temp_min'] = pd.to_numeric(train['temp_min'], errors='coerce') # 删掉没有温度的样本,保留缺失湿度等次要字段的样本 train = train.dropna(subset=['temp_max', 'temp_min']) # 物理范围过滤:湿度 0~100,温度 -50~55 train = train[(train['humidity'] >= 0) & (train['humidity'] <= 100)] train = train[(train['temp_max'] >= -50) & (train['temp_max'] <= 55)]

这段代码中的errors='coerce'是核心参数,它的含义是把无法解析的值置为 NaN,而不是让程序中断;随后用dropna(subset=...)只删除目标列缺失的样本,而不是把含有任何 NaN 的行全部删掉。这样做的好处是保留了一部分湿度缺失但温度正常的样本,让训练样本数量最大化。

筛选湿度范围时注意边界值:湿度等于 100 在高湿度地区是合理数据,不能因为过滤条件写成<100而误删;温度范围也要根据项目所覆盖的城市调整。如果项目只做南方城市,-50 这种下限基本没有影响,但放到北方冬季城市时,低于 -40 的历史数据确实存在,过滤条件不能拍脑袋写死。这一节不仅解决“能不能跑”,还能解释为什么训练出的模型对异常值不敏感。

2.3 特征构造:把“日期”和“天气”变成模型能读的列

原始 CSV 里最常见的列有 date、temp_max、temp_min、humidity、pressure、weather 这类字段,其中 weather 是中文文本,比如“晴”“多云转阴”。直接把字符串喂给 sklearn 会报错,所以要做特征编码。日期字段也需要拆开,因为“月份”“是否周末”对温度有相关性,而“完整日期字符串”是无法参与数值计算的。

常见做法是把日期拆成年、月、日、星期几、是否周末,再做一列滞后温度。滞后特征意味着用前一天的观测值预测今天,这对天气这样的连续过程非常有效。实现如下:

# 必须先按日期排序,再构造滞后特征 train = train.sort_values('date').reset_index(drop=True) train['year'] = train['date'].dt.year train['month'] = train['date'].dt.month train['day'] = train['date'].dt.day train['weekday'] = train['date'].dt.weekday train['is_weekend'] = train['weekday'].isin([5, 6]).astype(int) # 天气文本转成分类编码 train['weather_code'] = train['weather'].astype('category').cat.codes # 用前一天的最高温作为新特征,NaN 会自动出现在每个序列的第一行 train['temp_max_lag1'] = train['temp_max'].shift(1)

参数说明:sort_values('date')必须先执行,否则shift(1)取到的“前一天”并不是真正的时间前一位,而是当前文件里的上一行,这在多城市数据混排时会引入跨城市错误,导致预测结果异常好或者异常差。astype('category').cat.codes是 Python 类型转换里比较隐晦的一种,它将类别文本映射成整数编码,注意这个编码顺序不一定有大小含义,但在树模型里完全可以接受。

对特征有个基本判断:month、weekday 这类周期性特征能帮模型区分季节和周末效应;lag1 特征让模型学到“今天和昨天温度不会差太多”这条朴素规律。如果你的 date_train.csv 足够长,甚至可以构造 lag2、lag3,但要注意每个滞后特征都会拿走一行有效样本,特征多了样本少,反而得不偿失。

3. 天气预测模型:GetModel.py 训练了什么,Model.pkl 里存了什么

3.1 模型选型:为什么不是神经网络也能拿高分

项目里既然生成了 Model.pkl,说明核心预测部分不是逻辑代码,而是用一个机器学习模型保存下来的权重或结构。对于这种量级的课程设计,我不建议一上来就套 LSTM 或 Transformer,原因有三:第一,天气预测需要可解释性,随机森林可以直接输出特征重要性,答辩时能指着特征排名讲;第二,训练数据量通常只有几百到几千行,深度学习很容易过拟合;第三,神经网络的训练时间不可控,课堂演示环境不一定能复现结果。

这个项目最合理的选型是随机森林或梯度提升树,它们能处理非线性关系、对缺失值有一定容忍度,并且训练速度快。用Model.pkl这个文件后缀也能看出,模型是用 joblib 或 pickle 保存的,这正好是 scikit-learn 生态的常见保存方案。随机森林用于回归时,预测值是所有决策树结果的平均值,因此相比单棵决策树稳定很多。

3.2 训练与持久化:从 DataFrame 到 joblib 文件

GetModel.py 的核心任务可以拆成三部分:读取 date_train.csv,选择特征列,训练模型并保存。这里给出一个可以直接套在项目上的训练骨架:

import joblib from sklearn.ensemble import RandomForestRegressor # 特征列来自 ProcessData 构造好的字段 feature_cols = ['month', 'day', 'weekday', 'humidity', 'pressure', 'weather_code', 'temp_max_lag1'] X = train[feature_cols] # 预测目标是当天最高温 y = train['temp_max'] # 有缺失的样本需要剔除,尤其是第一行 lag1 缺失 train_clean = train.dropna(subset=feature_cols + ['temp_max']) X = train_clean[feature_cols] y = train_clean['temp_max'] model = RandomForestRegressor( n_estimators=200, max_depth=8, min_samples_leaf=3, random_state=42 ) model.fit(X, y) joblib.dump(model, 'Model.pkl')

参数说明:n_estimators=200是决策树的数量,数量越多预测越稳定,但训练时间和文件体积也会上升;max_depth=8限制每棵树的深度,防止单个特征把分支切得过细;min_samples_leaf=3规定每个叶子节点至少保留 3 个样本,这是一种常见的正则化手段。random_state=42保证每次运行生成完全相同的随机森林,否则两次训练结果不一致,答辩演示时容易出现“上一次跑出一个结果,这次跑出另一个结果”的尴尬情况。

模型保存和加载必须注意文件路径。joblib.dump(model, 'Model.pkl')保存的对象不只有模型参数,还包括模型内部的类别编码和特征名称;如果之后移动 HTML 或脚本目录,要保持相对路径一致,否则 main.py 调用joblib.load('Model.pkl')会直接抛 FileNotFoundError。常见做法是把 Model.pkl 放在项目根目录,所有脚本用绝对路径或基于__file__的路径来定位。

3.3 时间序列切分与评估:别把“数据泄露”写进大作业

很多初学者在评估模型时直接用train_test_split(X, y, random_state=42)随机切分,这对普通分类任务没问题,但用在天气时间序列上会形成数据泄露。原因是随机切分会把未来的样本放进训练集,让模型“提前看到”后面几天的真实值,导致验证分数虚高。答辩时老师只要问一句“你的测试集是随机切分的吗”,就很容易露馅。

正确做法是按时间顺序切分,使用前 80% 训练,后 20% 验证:

# 时间序列必须按时间顺序切分,不能用随机切分 split = int(len(X) * 0.8) X_train, X_test = X.iloc[:split], X.iloc[split:] y_train, y_test = y.iloc[:split], y.iloc[split:] model.fit(X_train, y_train) y_pred = model.predict(X_test)

然后计算回归指标。对天气预测来说,MAE 和 RMSE 是高频使用的两个指标,它们的含义不同:MAE 反映平均误差,单位就是摄氏度,直观;RMSE 因为对误差取平方后再开根,所以对大误差更敏感。如果 RMSE 明显大于 MAE,说明存在个别日子的预测偏差特别大,常见于强降温和台风天气。

指标计算方法关注点
MAEmean(abs(y_true - y_pred))平均偏了多少度,解释直观
RMSEsqrt(mean((y_true - y_pred)^2))对少数大误差更敏感
R^21 - SS_res/SS_tot模型解释了多少比例的目标方差

计算这两个指标只需要两行代码:

from sklearn.metrics import mean_absolute_error, mean_squared_error mae = mean_absolute_error(y_test, y_pred) rmse = mean_squared_error(y_test, y_pred, squared=False) print(f'MAE: {mae:.2f}, RMSE: {rmse:.2f}')

参数说明:squared=False是 sklearn 1.x 之后推荐的写法,直接返回 RMSE,而不是 MSE;如果使用旧版 sklearn,squared=False可能不兼容,需要写np.sqrt(mean_squared_error(...))。顺带提一句,很多示例代码打印 R^2 时会把model.score(X_test, y_test)当作准确率来宣传,这不太严谨,R^2 不是准确率,它只能反映模型对方差解释的百分比。

4. main.py 与可视化:从预测结果到一张“天气网”

4.1 main.py 的职责:加载模型、生成数据、渲染页面

main.py 是整个项目的入口,它做的事并不神秘:加载 Model.pkl,读取待预测的数据,调用模型拿到预测结果,再把结果传给网页展示。这个结构对应了课程设计里“后端计算”和“前端可视化”的天然分界。

在这个项目里,最合适的 Web 框架是 Flask。Flask 比 Django 轻,只需要一个路由就能把页面跑起来,代码量少,适合大作业演示。main.py 的流程大致如下:

from flask import Flask, render_template, jsonify import joblib import pandas as pd app = Flask(__name__) model = joblib.load('Model.pkl') @app.route('/') def index(): # 复用 ProcessData 的清洗函数,生成待预测特征 df = prepare_latest_data() feature_cols = ['month', 'day', 'weekday', 'humidity', 'pressure', 'weather_code', 'temp_max_lag1'] pred = model.predict(df[feature_cols]) # 把日期和预测温度传入模板 return render_template( 'weather.html', dates=df['date'].astype(str).tolist(), pred_temps=pred.tolist() ) if __name__ == '__main__': app.run(debug=False, port=8080)

参数说明:render_template第一个参数是 HTML 模板路径,后续参数是模板里需要插入的数据;.astype(str)用于把日期转成字符串,避免 JSON 序列化时出现Timestamp类型错误;debug=False是为了防止演示时页面报错后直接暴露交互式调试器,改成 True 的话会带来安全隐患。prepare_latest_data这个函数在项目里可能没有单独命名,但它的作用一定是从 date_test.csv 或实时接口构造特征,逻辑上等同于把 ProcessData.py 的处理流程封装成可调用函数。

4.2 ECharts 展示:折线图、柱状图和全国概览怎么选

天气网.html 里最值得看的是前端如何接收预测数据。ECharts 是当前天气可视化的常用库,支持折线图、柱状图、地图等图表类型。对于未来几天的温度预测,折线图最直观;对“晴雨分布”或“风级分布”,柱状图更容易比较;如果想展示 china_today.csv 的全国信息,用 ECharts 地图加散点图更合适。

下面是一个最简的折线图模板片段,注意数据和后端变量如何衔接:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>天气预测可视化</title> <script src="static/echarts.min.js"></script> </head> <body> <div id="tempChart" style="width:100%;height:400px;"></div> <script> var chart = echarts.init(document.getElementById('tempChart')); chart.setOption({ title: { text: '未来几天气温预测' }, tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: {{ dates | tojson }} }, yAxis: { type: 'value', name: '温度(℃)' }, series: [{ type: 'line', data: {{ pred_temps | tojson }}, smooth: true }] }); </script> </body> </html>

这里{{ dates | tojson }}是 Jinja2 模板引擎提供的过滤器,作用是把 Python 列表转成合法的 JavaScript 数组,而不是直接输出 Python 的方括号和单引号。smooth: true只是画曲线时让折线圆润,不会改变预测数值。要注意的是,如果weather.html被单独双击打开,script src="static/echarts.min.js"这种依赖路径是无法解析的,必须通过 Flask 启动后再访问。

如果打开页面后只有坐标轴没有线,优先打开浏览器开发者工具,看 Console 面板是否报echarts is not defineddata is not defined。前者说明 ECharts 脚本没加载,后者说明后端没有正确传入变量。

4.3 启动与联调:本机跑通的最小步骤

这个项目拿到手后不能直接双击 HTML 就开始,完整启动顺序是:

  1. 确认当前 Python 环境已经安装 pandas、scikit-learn、flask、matplotlib 这些依赖,建议用pip install -r requirements.txt统一安装,如果没有 requirements.txt 就逐个安装。
  2. 先运行一次 GetModel.py,确保 Model.pkl 存在。main.py 启动时会加载这个文件,如果缺失会直接报错。
  3. 再运行python main.py,终端会显示 Flask 服务地址,一般是http://127.0.0.1:8080,复制到浏览器打开。
  4. 页面加载后,如果图表没有显示,检查是否还在命令行终端运行程序,关闭终端服务会立刻停止。
  5. 如果想改端口,打开app.run(port=8080)这一行,改成比如 5000,注意避免和系统其他服务冲突。

这里特别提醒一点:第一次运行最好在命令行用python main.py启动,而不是在 IDE 里直接点运行按钮。因为有些集成开发环境的工作目录和项目根目录不同,会导致相对路径找不到 date_train.csv 和 Model.pkl。如果你在用 vscode 做 Python 环境配置,请把项目文件夹直接作为 vscode 根目录打开,并在.vscode/settings.json中设置"python.terminal.executeInFileDir": true,能从根上避免这类路径问题。

5. 进阶实操:从“能跑”到“答辩能讲”

5.1 多城市预测:china_today.csv 之外的扩展方式

china_today.csv 当前只保存了全国今日天气快照,它本身没有历史过程,不能直接用来训练多城市模型。要扩展成“预测多个城市”,需要在 GetData.py 里循环多个城市 ID,分别抓取历史数据后合并到 date_train.csv。城市 ID 通常和爬虫 URL 中的参数一致,不能随便填数字。合并后需要给每个样本增加一列 city 并做编码处理,否则模型会把不同城市的差异完全忽略。这种扩展方式会让特征列变多,但也能让模型学到“同纬度城市温度相近”这样的跨城市规律。

# 假设抓取逻辑按城市ID落盘,合并时保留city列 all_frames = [] for city_id in ['101010100', '101020100']: df = pd.read_csv(f'{city_id}_train.csv') df['city'] = city_id all_frames.append(df) df_all = pd.concat(all_frames, ignore_index=True)

需要注意pd.concatignore_index=True要带上,否则合并后的 DataFrame 会保留每个文件的原始索引,之后做sort_values('date')时排序范围会受影响。城市 ID 在模型里应该作为特征参与训练,预测阶段也要提供同一列,否则 predict 会报特征数量不一致的错误。

5.2 增量更新与自动重训:给演示数据续命

课程设计交完以后,仓库里的 date_train.csv 是静态的。如果项目被放到服务器上持续演示,过两周预测就会偏离现实。常见做法是写一个增量刷新脚本,在 main.py 启动前检查 CSV 修改时间,超过一定天数就重新执行 GetData.py 和 GetModel.py,这样能保证演示数据不是陈旧数据。

import os import time # 超过3天没更新,就自动抓取并重训 csv_path = 'date_train.csv' last_update = os.path.getmtime(csv_path) if time.time() - last_update > 86400 * 3: os.system('python GetData.py') os.system('python GetModel.py')

os.path.getmtime返回文件最后修改时间的时间戳,time.time()是当前时间戳,两数之差单位是秒。86400是一天的秒数,乘 3 就是三天。这个逻辑放在 main.py 初始化里,相当于给项目增加了一个轻量级自愈机制。不过要注意,爬虫抓取频次不能太高,否则很容易被封 IP,建议只在数据明显过期时才触发。

5.3 用预测对比真值图收尾

答辩时最有力的一张图,不是未来预测的曲线,而是用 date_test.csv 里的真实观测值和模型预测值画在同一个坐标系中。这张图可以直接验证模型不是“乱猜”,也能让评委一眼看出模型在哪些时段偏差大。matplotlib 画图时可以保存成 PNG,并在 HTML 页面中用 img 标签展示:

import matplotlib.pyplot as plt plt.figure(figsize=(10, 4)) plt.plot(y_test.values, label='real') plt.plot(y_pred, label='predicted') plt.legend() plt.title('Prediction vs Real Temperature') plt.savefig('static/validate.png', dpi=150)

dpi=150是清晰度参数,用于保证图片在 PPT 投影或 PDF 报告里不糊;figsize=(10, 4)控制画布宽高比,适合页面内嵌展示。绘制前必须把y_testy_pred的索引对齐,最稳妥的做法是plt.plot(y_test.reset_index(drop=True), label='real')plt.plot(pd.Series(y_pred), label='predicted'),避免因索引错位画出两条不相关的线。这张对比图放进去后,整个项目的结果落点就从“代码跑通”变成了“预测效果可视化”。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 1:03:32

LangChain前端SDK:智能体应用开发实战指南

1. LangChain Frontend 概述&#xff1a;从官方文档看智能体应用开发当我们需要为AI智能体构建交互界面时&#xff0c;传统聊天机器人框架往往只关注消息流的呈现。LangChain Frontend SDK的出现彻底改变了这一局面——它专为生产级智能体应用设计&#xff0c;将前端界面升级为…

作者头像 李华
网站建设 2026/9/17 0:59:14

PSST电力系统仿真工具箱:从zip安装到机组组合与经济调度实战

简介&#xff1a;这是基于MATLAB/Simulink开发的电力系统仿真工具箱PSST&#xff0c;面向电力系统研究人员、工程师及电气专业学生&#xff0c;用于暂态稳定分析、控制器设计、继电保护与频率电压控制等场景。压缩包共含106个文件&#xff0c;以py源码、m脚本、rst文档、ipynb示…

作者头像 李华
网站建设 2026/9/17 0:57:08

多任务并发时 Claude Code 报 401?TaoToken 的 Base URL 这样填

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 0:54:59

ThinkPHP5家庭财务系统实战:从部署到多人协作

简介&#xff1a;本资源是一套基于ThinkPHP5框架开发的家庭财务收支管理网站完整源码案例&#xff0c;面向PHP初学者与Web开发入门者&#xff0c;解决个人及家庭日常记账、预算控制与消费分析等实际需求。压缩包共1412个文件&#xff0c;涵盖320个HTML页面、277个JavaScript交互…

作者头像 李华