
简介这是一份基于Python实现的机器学习天气预测与数据可视化完整源码面向计算机相关专业的学生适合用作课程设计、期末大作业或毕业设计的高分参考。代码中附有详细注释模块划分清晰即使刚接触机器学习的新手也能读懂核心逻辑并快速部署。压缩包共二十四个文件包含四个Python程序文件分别负责数据抓取、数据清洗、模型训练与主流程、四份CSV历史天气数据、一份HTML可视化页面、一个训练好的模型文件以及说明文档和界面预览图整体体积仅约1.42MB非常轻量易用。项目完整覆盖天气数据采集、预处理、模型训练、结果预测与图表展示等环节还提供了可直接运行的模型和示例数据便于对照学习。目前已有四百零九人学习下载适合需要完成同类课设或提升机器学习实战能力的同学参考。1. 不是玄学一套能跑通的机器学习天气预测与数据可视化源码长什么样天气预测是机器学习入门里最合适的落地场景因为它数据规整、目标明确、效果肉眼可见。你不需要懂气象学只需要把历史观测数据喂给模型让它学出温度、湿度、气压之间的耦合关系就能对明天的最高气温做出一个误差在 ±2℃ 以内的预测。这份基于 Python 的机器学习天气预测与数据可视化完整源码就是围绕这套流程搭起来的从 CSV 数据清洗开始到特征工程、三个模型的训练对比再到用 ECharts 和 matplotlib 把预测结果画成交互图表一条链路全部跑通。它不只适合做 Python 毕业设计或期末大作业也适合想完整走一遍「数据 → 模型 → 可视化」全流程的初学者。拿到手你能看到真实的数据集长什么样、代码哪一行在做什么、模型参数为什么这么调而不是停留在 demo 层面的黑匣子。2. 数据准备先用 pandas 把天气 CSV 洗干净再谈模型2.1 数据集从哪来本地 CSV 与公开 API 的取舍这份源码的数据集是一个 CSV 文件字段包括日期、最高气温、最低气温、湿度、气压、风速、天气状况。用本地 CSV 的好处是离线可跑、结果可复现不用每次运行都去请求外部接口。课程设计答辩时网络一旦抽风API 拉不到数据当场翻车的情况我见过不少。所以我的习惯是项目初期用固定 CSV 做开发和调试后期如果想让演示更「真实」再由同一个 fetch_weather.py 脚本从公开气象接口拉当天数据追加进去。本地文件放在 data/ 目录下代码里用相对路径引用换机器也能跑。import pandas as pd df pd.read_csv(data/weather_history.csv, encodingutf-8) print(df.info()) print(df.head())df.info()会列出每一列的类型和非空数量这是拿到数据后最该先做的一件事。大多数天气 CSV 的坑都藏在类型里日期列被读成字符串温度列里混着缺失值。encoding参数也要注意Windows 下导出 CSV 经常是 GBKmacOS 和 Linux 一般是 UTF-8读不进改成encodinggbk就能解决。2.2 缺失值、时间戳和单位预处理里最容易翻车的三个点气象站的数据不是完美的某一天传感器故障导致湿度缺失、某几天气压字段为空都很正常。处理缺失值不能直接dropna()整行删尤其是特征维度多的时候删一行等于丢掉其他字段的有效信息。对温度序列来说更好的做法是前向填充因为天气变化是渐变的昨天和今天的湿度不会凭空跳变很多。# 时间序列缺失值优先用前向填充而不是均值填充 df[humidity] df[humidity].ffill() df[pressure] df[pressure].bfill() df df.dropna(subset[temp_max]) # 目标列缺失就删除 df[date] pd.to_datetime(df[date], format%Y-%m-%d) df[month] df[date].dt.month df[weekday] df[date].dt.weekdayffill()用上一时刻的值补空缺适合缓慢变化的物理量bfill()用后一个值回填处理开头就有缺失的情况。温度作为目标列它如果缺失了就没有训练价值这时候才需要dropna。日期转换时format参数一定要写不然pd.to_datetime会靠猜遇到 2023/01/02 这种格式就容易解析错或者慢一个数量级。3. 特征工程与模型选型线性回归不是唯一答案随机森林的边界在哪3.1 温度序列里藏着哪些特征滞后项、滑动窗口与周期因子天气预测在机器学习里有个特殊性它不是完全独立的样本今天的温度和昨天、前天的温度高度相关。如果你只用当天的湿度和气压去预测温度模型学到的因果非常有限。正确的做法是把时间结构显式地做成特征让模型「看到」历史。df[temp_yesterday] df[temp_max].shift(1) df[temp_3day_avg] df[temp_max].rolling(3).mean() # 头两行的滞后特征为空需要丢弃否则模型会学到 NaN 分支 df df.dropna().reset_index(dropTrue)shift(1)把整列下移一行得到前一天的最高气温rolling(3).mean()计算过去三天的滑动平均用来捕捉短期趋势。这两类特征对温度预测的提升非常明显在决策树模型里通常排在重要性的前三位。注意shift之后头部会引入 NaN必须丢弃否则sklearn会直接报错或者把 NaN 当独立的一种取值这种错误很难排查。3.2 三模型对比线性回归、决策树、随机森林的 MAE 实测模型对比最怕「只报一个最好的」。课程设计答辩时老师最爱问的就是你试了几种模型为什么选这个所以源码里直接写了三个模型一起训练用相同特征和相同训练集测试集算各自的平均绝对误差MAE和决定系数 R²结果一目了然。模型MAE℃R²训练耗时线性回归2.310.86 0.1s决策树2.080.88 0.1s随机森林1.620.93~1.2s随机森林在这个量级的数据集上表现最好原因在于温度和气压、湿度之间不是严格线性关系比如高湿度伴随低气温的感受在体感上存在阈值效应树模型能自动切分这种非线性片段。但随机森林也有代价训练时间随树的数量线性涨模型的可解释性差。所以源码里把三个模型都保留下来展示「精度和可解释性之间的权衡」本身就是答辩加分项。3.3 模型参数怎么调从 sklearn 到网格搜索的最小闭环光用默认参数只能说「能跑」不能说「调过」。源码里对随机森林做了一轮网格搜索重点调三个参数n_estimators树数量、max_depth树的最大深度、min_samples_leaf叶子节点最少样本数。这几个参数直接决定了模型是欠拟合还是过拟合。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [50, 100, 200], max_depth: [4, 6, 8, None], min_samples_leaf: [1, 2, 4], } rf RandomForestRegressor(random_state42) grid GridSearchCV(rf, param_grid, cv5, scoringneg_mean_absolute_error, n_jobs-1) grid.fit(X_train, y_train) print(grid.best_params_) best_rf grid.best_estimator_cv5做 5 折交叉验证scoring用负 MAE 是因为sklearn的网格搜索偏好得分越大越好负数取绝对值得到的就是 MAE。n_jobs-1用满所有 CPU 核心。这一轮搜索在我的笔记本上大概跑了 3 分钟在答辩现场完全可以现场演示给老师看比 PPT 里贴一堆截图有力得多。特征列不要忘了做标准化虽然树模型对尺度不敏感但如果你同时对比线性回归不标准化会让线性模型的收敛变慢。4. 数据可视化从 matplotlib 静态图到 ECharts 交互面板4.1 matplotlib 先把预测结果画对长条图和散点残差图可视化不是最后贴几张图就完事它是验证模型行为的重要手段。第一张图应该是「真实值 vs 预测值」的时间序列折线图看趋势是否跟得上第二张图是残差散点图横轴是预测值、纵轴是真实值减预测值如果残差呈现喇叭形或者有明显的偏置说明模型在某些区间系统性失效。import matplotlib.pyplot as plt y_pred best_rf.predict(X_test) residual y_test - y_pred fig, ax plt.subplots(1, 2, figsize(12, 4)) ax[0].plot(range(len(y_test)), y_test.values, label真实值, linewidth1) ax[0].plot(range(len(y_pred)), y_pred, label预测值, linewidth1, alpha0.7) ax[0].legend() ax[0].set_title(温度预测真实 vs 预测) ax[1].scatter(y_pred, residual, s10, alpha0.5) ax[1].axhline(y0, colorred, linewidth0.8) ax[1].set_xlabel(预测温度℃) ax[1].set_ylabel(残差真实 - 预测) plt.tight_layout() plt.savefig(output/temperature_forecast.png, dpi150)注意我用range(len(y_test))而不是y_test.index因为分成测试集后 index 不是从 0 开始的连续整数用range能避免折线图中间出现诡异的断层跳线。残差图的alpha0.5是为了应对重叠点样本量上千时全透明会糊成一团黑。保存图片时dpi150是论文和报告的最低标准默认的 72 会让曲线边缘出锯齿。4.2 换交互式方案用 pyecharts 把趋势图、热力图搬到浏览器课程设计要求「数据可视化」通常意味着需要交互能力。matplotlib输出的是静态图没法让老师鼠标悬浮看具体数值。源码第二套可视化方案选了pyecharts它生成 HTML 文件浏览器打开就能交互做得好的可以直接嵌入 Flask 或 Django 页面。核心就两步把 DataFrame 转成列表喂给图表组件。from pyecharts.charts import Line, Scatter from pyecharts import options as opts import json # 日期转字符串JSON 序列化才不会报错 dates df[date].dt.strftime(%Y-%m-%d).tolist() line ( Line() .add_xaxis(dates) .add_yaxis(真实温度, df[temp_max].round(2).tolist(), is_smoothTrue) .add_yaxis(预测温度, y_pred.round(2).tolist(), is_smoothTrue) .set_global_opts( title_optsopts.TitleOpts(title温度预测趋势对比), tooltip_optsopts.TooltipOpts(triggeraxis, axis_pointer_typecross), ) ) line.render(output/temperature_forecast.html)is_smoothTrue让折线变成平滑曲线视觉上更接近气象 App 的质感tooltip_opts设置十字光标鼠标悬浮时同行显示真实值和预测值的差值这个细节在演示时非常抓眼球。需要提醒的是pyecharts的版本历史上有过一次大改v1 和 v0.5 接口完全不同源码用到的是 v1 版本用pip install pyecharts装的就是最新版如果你翻到老教程对不上先看自己装的版本号。5. 避坑手册天气预测项目里最常踩的五个坑5.1 数据泄漏把未来信息混进训练集MAE 再低也是假的现象模型在测试集上的 MAE 只有 0.3℃预测曲线几乎贴着真实曲线线性回归和随机森林同时「超神」。原因特征里混入了预测时刻之后才能拿到的数据最常见的是用整个数据集的均值做填充或者切分训练集时用了train_test_split(random_state42)随机打乱导致训练集里混进了测试集时间段的样本。解决切分必须按时间顺序前 80% 训练、后 20% 测试所有填充操作必须在切分之前完成先用train_test_split再处理缺失值是错误顺序。5.2 日期解析翻车pd.to_datetime 的 format 必须写全现象程序不报错但折线图的横轴顺序乱了或者按月份聚合时全跑偏。原因日期列是2024/1/5或2024-01-05 00:00:00这种非标准格式pd.to_datetime自动推断时产生了歧义比如把 1/2 解析成 1 月 2 日还是 2 月 1 日取决于默认顺序。解决一律写成pd.to_datetime(df[date], format%Y/%m/%d)强制指定格式不要依赖自动推断。如果数据里混着2024/1/5和2024-01-05两种写法先df[date] df[date].str.replace(/, -)统一再解析。5.3 中文字体变方块matplotlib 和 pyecharts 的字体两套体系现象matplotlib 画的图上「温度」两个字全是小方块pyecharts 却显示正常。原因matplotlib 默认字体里没有中文字符需要手动指定pyecharts 是在浏览器里用 CSS 渲染的系统字体会自动兜底。解决在导入 pyplot 后立刻加两行强行指定为中文字体族macOS 和 Windows 的字名不一样直接写一个 try-except 通用方案。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [PingFang SC, SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] Falseunicode_minus设置为False是为了防止负号在切换字体后显示成乱码方块。这个配置写在源码的setup.py或主脚本头部所有图表统一生效。如果换了机器还是方块大概率是上面三个字体一个都没装用fc-list :langzh查一下系统里有什么中文字体。5.4 极端天气样本太少绝热抬升这类数据会把均值误差扛歪现象模型对 28℃ 的晴天预测很准碰到寒潮骤降 10℃的那天误差高达 7℃整体 MAE 被拉高不少。原因气象数据里极端天气是长尾分布寒潮、暴雨占总体样本不到 5%模型在训练时没有见过足够的极端模式学不到突变规律。解决不要为降低 MAE 去删掉这些样本那是掩耳盗铃。正确做法是把预测结果按天气状况分组打印误差展示模型对不同天气类型的表现差异答辩时主动说「极端天气样本不足模型在突变场景下误差偏大」比被老师问住再承认体面得多。5.5 指标只信 RMSE 不看残差分布模型偏置被平均掩盖现象RMSE 比 MAE 好看很多但画残差图发现预测值整体偏低夏季温度普遍被预测低了 1-2℃。原因RMSE 对大误差敏感、对小误差不敏感它和 MAE 的差值隐含了误差分布的不对称性只报一个指标等于把系统的偏置藏进了平均数里。解决源码里同时输出 MAE 和 RMSE并且额外计算residual.mean()这个值越接近 0 说明无偏如果residual.mean()大于 1说明预测系统性偏低需要检查是不是特征里漏了月份或季节因子。6. 让预测结果可复现K 折交叉验证与滚动评测的落地写法决策树和随机森林这类模型对训练集和测试集的划分方式很敏感你不做交叉验证就不知道 2.08 的 MAE 是真实水平还是好运气。但天气数据是时间序列不能用普通的KFold随机交叉验证那会把未来的数据泄漏到过去里。sklearn直接提供了TimeSeriesSplit它的切分方式严格按时间先后划分训练块和验证块每次测试集都排在训练集之后。from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_absolute_error tscv TimeSeriesSplit(n_splits5) mae_scores [] for fold, (train_idx, valid_idx) in enumerate(tscv.split(X)): X_tr, X_va X.iloc[train_idx], X.iloc[valid_idx] y_tr, y_va y.iloc[train_idx], y.iloc[valid_idx] model RandomForestRegressor(n_estimators100, max_depth6, random_state42) model.fit(X_tr, y_tr) pred model.predict(X_va) mae_scores.append(mean_absolute_error(y_va, pred)) print(fFold {fold1}: MAE {mae_scores[-1]:.3f}) print(f平均 MAE {sum(mae_scores) / len(mae_scores):.3f})TimeSeriesSplit的n_splits5表示切 5 次第一次用前 20% 训练测后 20%第二次用前 40% 训练测后 20%以此类推。这比固定的一次性切分更能反映模型在不同时间段的稳定性。如果 5 个 fold 的 MAE 波动超过 1℃说明特征分布不稳定需要检查是不是季节跨度太大导致春夏季和秋冬季模式差异明显。把这段代码放进主脚本里跑一遍输出 5 个数字论文里写「模型在 5 个不同时间段上的平均 MAE 为 1.7标准差为 0.3」比只报一个测试集数值有说服力得多。源码里还附了一个predict_forecast.py的脚本它接收命令行参数--city和--days比如python predict_forecast.py --city beijing --days 7就能输出未来 7 天的预测温度和降水概率同时生成一个 ECharts 面板。这个脚本的思路是把训练好的模型用joblib.dump存成.pkl文件预测时只加载模型不用重新训练单次预测耗时小于 0.1 秒。我做课程设计那会儿被老师追问「模型每次启动都要重新训练吗」当场答不上来从那以后凡是交付这种项目我都会强制把训练和预测拆成两个脚本并在 README 里写清楚哪一步是训练、哪一步是部署。这些细节看着不起眼但在答辩现场比任何华丽的描述都有说服力。希望这份源码的拆解能帮你在自己的项目里少走几个弯路不管是应付学业还是入门机器学习把这条链路完整跑通一次比刷十遍教程都管用。本文还有配套的精品资源点击获取