
简介这份资源是面向高校学生与Python初学者的机器学习天气预测与数据可视化完整项目源码适用于课程设计、期末大作业及毕业设计等场景新手也能快速上手。压缩包共24个文件约1.42MB包含4个Python脚本分别负责数据获取、数据处理、模型训练与主程序调度4个CSV数据文件用于训练与验证另有1个已训练模型文件、1个网页可视化页面、1份说明文档及多张运行效果截图结构清晰、注释完整。目前已有410人学习下载具备一定参考热度。项目将机器学习预测与天气数据可视化结合读者可据此理解从数据采集、特征处理到模型训练与结果展示的完整链路并直接部署运行省去从零搭建的繁琐对需要提交高质量作业或快速掌握实战流程的人具有较高参考价值。1. 从一份天气预测源码说起机器学习到底在气象数据里做了什么很多人第一次接触机器学习都是从天气预测这类带时间序列的表格数据入手的。原因很直接数据公开、字段清晰、结果直观而且能同时练到特征工程、模型训练和数据可视化三条线。这份基于 Python 的机器学习天气预测与数据可视化完整源码核心思路并不复杂——把历史气象观测数据整理成监督学习样本用回归或分类模型预测未来的温度、湿度、降水概率等指标再把预测结果和真实观测叠在同一张图上做对比。它适合两类人一类是刚学完机器学习入门课程、想找一个能跑通全流程的项目练手的学生另一类是需要快速搭一套气象数据看板、验证某个预测思路是否可行的工程师。真正决定这份源码能不能用的不是模型多花哨而是数据清洗和特征构造这两步有没有做扎实。2. 天气预测源码的骨架数据、特征、模型、可视化四层怎么搭2.1 先看清数据长什么样再决定用什么模型拿到一份天气数据集第一件事不是急着fit而是把字段含义和缺失情况摸清楚。常见的公开气象数据一般包含时间戳、气温、露点、湿度、气压、风速、风向、降水量、云量等列。不同来源的字段名和单位差异很大有的温度是摄氏度有的是华氏度有的风速用米每秒有的用节。如果不在读入阶段统一后面模型学到的就是错的东西。我一般会先做三件事确认时间列是不是等间隔、统计每列缺失比例、看目标列的分布形态。时间列如果存在跳变或重复需要先重采样到固定频率比如按小时或按天对齐。缺失比例超过三成的列除非业务上特别关键否则直接丢掉比强行插值更稳。目标列如果严重偏态回归任务可以考虑对目标做对数变换分类任务则要注意类别不平衡。import pandas as pd import numpy as np # 读入原始气象数据parse_dates 把时间列直接转成 datetime df pd.read_csv(weather_history.csv, parse_dates[timestamp]) df df.sort_values(timestamp).set_index(timestamp) # 统一重采样到逐小时缺失值先标记不急着填 df df.resample(1h).mean() # 看缺失比例决定哪些列保留 missing_ratio df.isna().mean().sort_values(ascendingFalse) print(missing_ratio) # 丢掉缺失超过 30% 的列 keep_cols missing_ratio[missing_ratio 0.3].index.tolist() df df[keep_cols]这段代码的逻辑是先把时间轴对齐再按缺失比例做列筛选。resample(1h).mean()会把同一小时内的多条记录取平均如果原始数据本身就是逐小时的这一步不会改变数据但能防止时间戳重复带来的索引冲突。missing_ratio 0.3这个阈值不是固定的数据量小的时候可以放宽到 0.5数据量大且字段冗余时可以收紧到 0.2。注意resample之后会产生一些原本不存在的时间点这些位置的缺失是重采样引入的和原始缺失要区分对待。2.2 特征工程把时间戳拆成模型能吃的数值原始时间戳对大多数模型来说没有直接意义需要拆成周期性特征。小时、星期、月份这些字段本身是离散的但如果直接当数值喂给模型会出现“23 点和 0 点距离很远”的荒谬结论。常见做法是做正弦余弦编码把周期性压到 [-1, 1] 区间。除了时间特征滞后特征和滑动窗口统计是天气预测里最有效的构造方式。气温有明显的日周期和自相关性用过去 1 小时、3 小时、6 小时、24 小时的值作为输入模型能学到趋势。滑动平均和滑动标准差则能刻画短期波动。# 时间周期特征小时和月份做正余弦编码 df[hour] df.index.hour df[hour_sin] np.sin(2 * np.pi * df[hour] / 24) df[hour_cos] np.cos(2 * np.pi * df[hour] / 24) df[month] df.index.month df[month_sin] np.sin(2 * np.pi * df[month] / 12) df[month_cos] np.cos(2 * np.pi * df[month] / 12) # 滞后特征过去 1、3、6、24 小时的气温 for lag in [1, 3, 6, 24]: df[ftemp_lag_{lag}] df[temperature].shift(lag) # 滑动窗口统计过去 6 小时的均值和标准差 df[temp_roll_mean_6] df[temperature].rolling(6).mean() df[temp_roll_std_6] df[temperature].rolling(6).std() # 构造完特征后丢掉因 shift/rolling 产生的空值 df df.dropna()shift(lag)把当前行往前推 lag 行得到的是“lag 小时之前的值”这个操作必须在排序后的时间索引上做否则会串行。rolling(6).mean()计算的是包含当前行在内的过去 6 行均值如果只想用历史信息预测当前需要改成rolling(6).mean().shift(1)。这一步最容易翻车的地方是忘记dropna()导致训练集里混入大量 NaN模型报错或者静默产出错误结果。2.3 模型选型从线性回归到梯度提升别一上来就上深度学习天气预测这类表格型时间序列任务梯度提升树如 LightGBM、XGBoost通常是性价比最高的选择。线性回归可以作为基线用来判断特征里到底有没有可用信号。如果线性回归的误差和均值预测差不多说明特征构造有问题换再复杂的模型也救不回来。随机森林和梯度提升树能自动处理特征交互和非线性关系对缺失值也有一定容忍度。深度学习模型在数据量足够大、特征维度足够高时才有优势普通规模的气象数据集上调参成本远高于收益。from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_absolute_error import lightgbm as lgb # 按时间顺序切分不能用随机切分 tscv TimeSeriesSplit(n_splits5) features [c for c in df.columns if c ! temperature] target temperature for train_idx, val_idx in tscv.split(df): X_train, y_train df.iloc[train_idx][features], df.iloc[train_idx][target] X_val, y_val df.iloc[val_idx][features], df.iloc[val_idx][target] model lgb.LGBMRegressor( n_estimators500, learning_rate0.05, num_leaves31, early_stopping_rounds50 ) model.fit(X_train, y_train, eval_set[(X_val, y_val)]) pred model.predict(X_val) print(MAE:, mean_absolute_error(y_val, pred))TimeSeriesSplit保证训练集始终在验证集之前避免未来信息泄漏。early_stopping_rounds50表示验证集误差连续 50 轮不下降就停止训练防止过拟合。num_leaves31是 LightGBM 的默认值数据量小的时候可以降到 15 左右数据量大且特征多时可以加到 63 或 127。注意eval_set传入的是验证集不是测试集测试集要留到最后一次性评估。2.4 可视化把预测值和真实值叠在同一张图上数据可视化不是装饰而是验证模型是否学到真实规律的手段。最直接的做法是把测试集上的真实气温和预测气温按时间画折线观察模型在哪些时段偏差大。如果模型在每天凌晨误差明显偏高说明夜间特征没构造好如果整体滞后于真实曲线说明滞后特征窗口设置不合理。import matplotlib.pyplot as plt plt.figure(figsize(14, 5)) plt.plot(y_val.index, y_val.values, label真实气温, linewidth1.5) plt.plot(y_val.index, pred, label预测气温, linewidth1.5, alpha0.8) plt.xlabel(时间) plt.ylabel(气温) plt.legend() plt.title(测试集预测值与真实值对比) plt.tight_layout() plt.savefig(prediction_vs_truth.png, dpi150)alpha0.8让预测线稍微透明两条线重叠时能看出重合程度。dpi150保证保存的图片在文档里清晰。如果横坐标太密集可以用plt.gcf().autofmt_xdate()自动旋转日期标签或者只显示部分刻度。这张图的价值在于它能把 MAE 这类数字指标翻译成肉眼可见的偏差模式帮助定位问题。3. 避坑与排查天气预测项目里最容易翻车的五个地方3.1 用随机切分代替时间切分指标虚高现象交叉验证的 MAE 很低但上线后预测误差大得离谱。原因train_test_split默认随机打乱训练集里混入了未来时间点的数据模型实际上在“偷看答案”。解决所有切分必须用TimeSeriesSplit或手动按时间截断验证集和测试集的时间戳必须晚于训练集。3.2 滞后特征构造时忘记对齐时间导致信息泄漏现象模型在训练集上表现完美验证集一塌糊涂。原因rolling或shift操作没有配合shift(1)使用当前行的目标值被间接编码进了特征。解决任何滑动统计量如果要用作当前时刻的输入必须整体往后移一行确保只用到历史信息。3.3 缺失值填充用了全局统计量引入未来信息现象模型在早期时间段误差偏大。原因用整列均值或中位数填充缺失而整列统计量包含了未来数据。解决填充值只能从当前时刻之前的数据计算或者直接用模型原生支持的缺失值处理如 LightGBM 的use_missingTrue。3.4 可视化时把训练集和测试集画在一起误判过拟合现象图上预测线和真实线几乎重合以为模型完美。原因把训练集预测结果和测试集预测结果混在同一张图里训练集部分当然拟合得好。解决训练集和测试集分开画或者只画测试集区间并在图上标注切分点。3.5 特征列里混入了目标列的衍生变量现象模型重要性排名里某个特征异常突出但去掉后效果骤降。原因构造特征时不小心把目标列的滞后值或滑动统计量也加了进去而这些特征在预测时不可得。解决维护一份明确的特征白名单每次新增特征后检查是否在预测时刻可获取。4. 把源码跑成自己的东西三个进阶技巧4.1 用多步预测替代单步预测更贴近真实需求单步预测只预测下一个时刻实际业务里往往需要未来 6 小时或 24 小时的温度曲线。做法有两种直接多步输出和递归多步预测。直接多步是训练一个模型同时输出多个时间点的值递归多步是用单步模型反复预测、把预测值当作下一步输入。递归方式容易累积误差直接多步更稳但需要把目标改造成多列格式。# 构造多步预测目标未来 1 到 6 小时的气温 for step in range(1, 7): df[ftarget_step_{step}] df[temperature].shift(-step) # 训练时用 MultiOutputRegressor 包装 from sklearn.multioutput import MultiOutputRegressor target_cols [ftarget_step_{s} for s in range(1, 7)] model MultiOutputRegressor(lgb.LGBMRegressor(n_estimators300)) model.fit(X_train, y_train[target_cols])shift(-step)把未来值拉到当前行构造出多列目标。MultiOutputRegressor会为每个目标列训练一个独立模型计算量随步数线性增长。如果步数多可以考虑用 LightGBM 的原生多输出或改成 Seq2Seq 结构。4.2 用特征重要性做一轮减法别只做加法很多人构造特征时只加不减最后几百列特征里一半是噪声。训练完一轮后用model.feature_importances_排序把重要性接近零的特征删掉再训一次。通常能减少三成特征而不掉点推理速度明显提升。特征类型典型数量建议保留策略时间周期编码4全保留滞后特征4-8保留重要性前 50%滑动统计6-12保留重要性前 30%原始气象字段5-10按缺失率和重要性筛选4.3 把可视化做成可复用的诊断面板与其每次手动画图不如写一个诊断函数输入真实值和预测值自动输出误差分布直方图、按时段聚合的误差柱状图、以及预测 vs 真实的散点图。这样每换一个模型或一组参数跑一次函数就能快速判断改进方向。def diagnose(y_true, y_pred, freqh): resid y_true - y_pred fig, axes plt.subplots(1, 3, figsize(16, 4)) axes[0].hist(resid, bins40) axes[0].set_title(误差分布) axes[1].scatter(y_true, y_pred, s4, alpha0.4) axes[1].set_title(预测 vs 真实) by_hour pd.DataFrame({resid: resid, hour: y_true.index.hour}) by_hour.groupby(hour)[resid].mean().plot(kindbar, axaxes[2]) axes[2].set_title(分时段平均误差) plt.tight_layout() return fig这个函数一次输出三个视角误差整体分布是否对称、预测值和真实值的线性关系是否偏离对角线、哪个时段系统性偏高或偏低。我自己的习惯是每调一次参数就跑一次diagnose比盯着单个 MAE 数字有用得多。这套流程跑通之后换数据集、换目标变量都只是改几行配置的事。希望帮到你。本文还有配套的精品资源点击获取