FEATURED · 精选文章

Python机器学习天气预测实战:从数据清洗到随机森林模型调优

发布时间 / 2026/9/14 23:53:02
来源 / 创域科博编辑部
栏目 / 资讯中心
Python机器学习天气预测实战:从数据清洗到随机森林模型调优 简介这是一套基于Python机器学习的长春地区天气预测与可视化课程设计资源适合作为毕设、课程设计、大作业或工程实训参考面向有Python基础、希望上手完整数据挖掘流程的学习者。项目覆盖天气数据爬取、清洗、模型训练与评估、结果可视化等环节代码按功能拆分为数据获取、处理、建模与主控制模块并借助pyecharts实现图表展示便于理解从数据到预测的完整链路。压缩包共25个文件包含4个Python脚本、4个CSV数据文件、1个训练好的模型文件及HTML可视化结果等整体仅1.43MB轻量易用另附12张运行效果图可直观对照输出结果。资源已有631人学习浏览适合作为入门机器学习项目的参考蓝本也可在此基础上扩展其他城市或优化预测算法。1. 为什么天气预测是 Python 机器学习课程设计里最扎实的选题课程设计选题有个规律数据好拿、效果可见、链路完整分数才扎实。基于 Python 机器学习ML实现的天气预测和天气可视化恰好三条全占历史数据容易取得预测结果随时对照真实值图表放进报告也很直观。别误以为天气预测必须上 LSTM。气象序列强周期、强滞后天级别数据用线性回归或随机森林已经能拿到可解释、可答辩的结果。评审老师更在意你理解不理解特征怎么构造、误差怎么归因而不是模型名够不够新。下面按数据采集、特征工程、模型训练、可视化、验证评估的顺序完整走一遍。普通电脑加开源 Python 环境就能跑通不需要 GPU 或云服务这恰好贴近课设的真实硬件条件。2. 天气数据管线采集、清洗与特征工程的落地顺序做天气预测的第一步不是选模型而是拿到一段够长、够连续、带真实观测值的历史数据。常见做法有两条走第三方气象 API或者下载整理好的历史 CSV。API 字段规范、日期连续JSON 结构统一适合脚本一键落地CSV 的优势是离线可用、不依赖网络答辩演示不容易出岔子。我一般建议课设优先选 CSV除非题目明确要求“实时获取”这个加分项。原因很直接答辩实验室的网络状态不可控现场演示时接口超时属于高频翻车事故。如果仍要用 API常见方案是 Open-Meteo 这类免费接口。它按经纬度拉取逐日或逐时资料请求参数能直接映射成 DataFrame。下面这段是标准的拉取写法import requests import pandas as pd url https://archive-api.open-meteo.com/v1/archive params { latitude: 39.9042, longitude: 116.4074, start_date: 2022-01-01, end_date: 2024-12-31, daily: temperature_2m_mean,relative_humidity_2m_mean,precipitation_sum, timezone: Asia/Shanghai } resp requests.get(url, paramsparams, timeout30) data resp.json() df pd.DataFrame({ date: data[daily][time], temp_mean: data[daily][temperature_2m_mean], humidity: data[daily][relative_humidity_2m_mean], precip: data[daily][precipitation_sum] }) df[date] pd.to_datetime(df[date]) print(df.head())daily参数声明你需要的逐日要素这里取了平均气温、平均湿度和日降水量三项timezone必须显式指定否则 UTC 时间会让日期整体偏移一天特征里的“昨天”会变成“前天”。timeout30是给请求设硬超时避免脚本卡在网络等待上。拉取失败时优先看返回的reason字段免费接口有并发限制连续请求之间加 1 到 2 秒延时是稳妥做法。开工之前先把 Python 环境配置好。最低要求是 Python 3.9 加 pandas、scikit-learn、matplotlib再补一个 plotly直接用 conda 建一个新环境把所有依赖一次装齐后面换机器演示会省很多事。环境问题在课设里出现的频率远高于模型精度问题不值得在答辩前夜重装解释器。2.1 数据选型还要关注时间跨度两年逐日数据约 730 条足够支撑线性回归和随机森林的课设需求三年以上更好能覆盖更多季节边界。如果数据只有一年以内构造滞后特征时会丢掉大量有效样本模型效果会明显缩水。另一个建议是只锁定单一城市、单一数据源不要混用多站资料。不同气象站对“平均气温”的统计口径并不一致有的取整点观测均值有的取日最高最低均值混用会把统计误差直接带进特征里。2.2 数据清洗缺失值、时间戳与异常值公开数据集最常见的三种问题日期不连续、同一天多条记录、个别天数值异常。处理的顺序是先对齐时间轴再填充缺失最后检测异常。如果先填值再重采样插值结果会被重复行干扰顺序反了会引入隐性噪声。df df.set_index(date).sort_index() df df.groupby(level0).mean() # 同一天多条记录取均值 df df.resample(D).asfreq() # 建立连续日期索引 df[temp_mean] df[temp_mean].interpolate(methodlinear) df[humidity] df[humidity].fillna(df[humidity].median()) df[precip] df[precip].fillna(0)重采样后会有 NaN 行出现此时不能让所有列共用同一种填充策略。气温做线性插值因为相邻日期的气温天然平滑过渡湿度用中位数填充是因为湿度序列偏态明显个别极端降水日的湿度值会拉高均值降水量缺失对应“无雨”的概率高填 0 是合理的领域假设。三列三种策略背后是三个不同的统计判断写进报告比单纯贴代码得分高。异常值用滚动窗口检测。判断标准是某天气温偏离近 7 天均值超过 3 倍滚动标准差即视为传感器噪声。window_mean df[temp_mean].rolling(7, centerTrue).mean() window_std df[temp_mean].rolling(7, centerTrue).std() anomaly (df[temp_mean] - window_mean).abs() 3 * window_std df.loc[anomaly, temp_mean] np.nan df[temp_mean] df[temp_mean].interpolate()centerTrue让窗口同时参考前后各三天检测更平衡冷空气过境造成的真实降温不会被误判。窗口长度取 7 和天气系统的自然周期一致比 3 天稳比 30 天敏感。2.3 特征工程滞后、滚动统计与周期编码天气预测的特征工程核心是一句话把“今天之前发生了什么”翻译成模型能读懂的数值。常用三组特征对应短期惯性、中期趋势和季节周期特征列含义构造方式temp_lag1 / temp_lag7前1天/前7天平均气温shift(1)/shift(7)humidity_3近3天平均湿度.rolling(3).mean()temp_std7近7天气温波动幅度.rolling(7).std()day_sin / day_cos季节周期编码sin(2πd/365)与余弦is_weekend是否周末0/1构造滞后特征要注意边界约束滞后阶数越大可用样本越少。两年数据 730 天构造滞后 7 天只损失约 1% 的样本滞后 30 天就要损失 4%滞后 90 天超过 12%小数据集里这个代价不可忽略。for lag in [1, 2, 3, 7]: df[ftemp_lag{lag}] df[temp_mean].shift(lag) df[humidity_3] df[humidity].rolling(3, min_periods1).mean() df[temp_std7] df[temp_mean].rolling(7, min_periods1).std() df[dayofyear] df.index.dayofyear df[day_sin] np.sin(2 * np.pi * df[dayofyear] / 365) df[day_cos] np.cos(2 * np.pi * df[dayofyear] / 365) df df.dropna()day_sin和day_cos是一组正交的周期编码比单独用正弦更能表达“靠近哪一天”的连续季节信息。min_periods1让滚动统计在前几天样本不足时也能计算避免序列开头的样本被整体丢弃。最后统一dropna()进入模型的每一行都必须是完整向量。如果想做“预测未来第三天”的扩展实验把滞后特征整体前移例如以temp_lag3作为最短滞后项其余代码不用改精度会下降但框架不动这个差异可以作为灵敏度分析写进报告。3. 从基线模型到随机森林预测算法的选型、训练与调参3.1 基线模型天气预测的“及格线”怎么定机器学习应用流程里第一步不是直接上最强模型而是先定义一个简单到不可能失败的基线。天气预测最自然的基线上持久性预测把今天的观测值直接当作明天的预测值然后计算均方根误差 RMSE。如果后续模型跑不过这个数字问题大概率出在数据或特征层面而不是模型能力不够。from sklearn.metrics import mean_squared_error import numpy as np y_actual df[temp_mean].iloc[1:].values y_baseline df[temp_mean].iloc[:-1].values rmse_base np.sqrt(mean_squared_error(y_actual, y_baseline)) print(f持久性基线 RMSE {rmse_base:.2f} °C)持久性基线对天气预测是严格的参照系因为天气系统有较强的自相关性气温在前一天之后往往变化不大。如果连续几天天气稳定基线 RMSE 会低到让复杂模型怀疑人生。因此基线不是用来刷成绩的而是用来判断“模型增加复杂度是否值回票价”。建议把基线数字写进课设报告的第一张实验表作为后面所有对比的锚点。3.2 回归模型对比线性回归、决策树与随机森林天气预测本质是回归任务课设里覆盖三种机器学习算法就足够线性回归、决策树、随机森林。三者对应了机器学习三大假设在数据集上的逐步放松——线性回归要求特征与目标线性相关决策树与随机森林不做线性假设能捕获温湿度交互等非线性关系。线性回归在训练时使用梯度下降迭代更新权重损失函数对每个参数的梯度给出下降最快的方向。实际代码不需要手写梯度直接用 scikit-learn 封装即可。from sklearn.linear_model import LinearRegression from sklearn.tree import DecisionTreeRegressor from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split feature_cols [temp_lag1, temp_lag2, temp_lag3, temp_lag7, humidity_3, temp_std7, day_sin, day_cos, is_weekend] X df[feature_cols].values y df[temp_mean].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, shuffleFalse )shuffleFalse是时序数据建模必须记住的第一条规则。如果让 sklearn 默认随机打乱样本测试集里会混入大量与训练集相邻的日期模型相当于提前看见答案分数虚高到失去意义。时序预测的交叉验证、网格搜索、特征选择所有环节都要以“时间顺序不可破坏”为前提。三个模型的训练与评估放在一个循环里models { LinearRegression: LinearRegression(), DecisionTree: DecisionTreeRegressor(max_depth8, random_state42), RandomForest: RandomForestRegressor( n_estimators200, max_depth10, random_state42 ) } for name, model in models.items(): model.fit(X_train, y_train) pred model.predict(X_test) rmse np.sqrt(mean_squared_error(y_test, pred)) print(f{name}: RMSE {rmse:.3f} °C)实际跑下来线性回归在日平均气温上不会太差因为气温与前一天气温本身就是强线性相关随机森林通常会再压掉 0.30.8°C 的误差差异主要来自非线性交互项比如高湿度与降温趋势同时出现时的综合影响。裸的决策树基本必过拟合max_depth压到 8 以下才能拿到合理结果。三者的对比结论可以直接进报告模型训练耗时过拟合风险可解释性课设推荐度线性回归极低低极高经典基线决策树低高中需要限深随机森林中中中综合效果最均衡3.3 参数调优用 TimeSeriesSplit 替代普通 KFold给随机森林调参时直接套GridSearchCV会踩坑默认 5 折是随机切分会破坏时序顺序。需要手动指定TimeSeriesSplit作为交叉验证器让每一折验证集严格排在训练集之后。from sklearn.model_selection import TimeSeriesSplit, GridSearchCV param_grid { n_estimators: [100, 200], max_depth: [6, 10, 14], min_samples_leaf: [2, 5, 10] } tscv TimeSeriesSplit(n_splits5) search GridSearchCV( RandomForestRegressor(random_state42), param_grid, cvtscv, scoringneg_mean_squared_error, n_jobs-1 ) search.fit(X_train, y_train) best_model search.best_estimator_ print(search.best_params_)neg_mean_squared_error作为评分指标是因为网格搜索默认选分数最高的一组参数而 MSE 越小越好取负之后“越大越好”等价于“误差越小越好”。参数组合数是 2 × 3 × 3 18 组5 折交叉验证共拟合 90 次普通 CPU 上几十秒能跑完如果数据量到几万条先压缩n_estimators的候选范围否则一次调参可能要等十分钟。调参结束后不要立刻宣布胜利。用完整训练集重新拟合最优参数组合在测试集上做最终确认。这一步是为了防止你从 18 组参数里挑出最好成绩时带入选型偏差属于统计上容易被忽略的常规动作。4. 天气可视化落地从预测曲线到可交互图表可视化在课设里的定位不是装饰而是支撑结论的证据。每放一张图都得能回答“这张图说明什么”的问题。按这个标准规划至少需要三张图全时段对比曲线、残差分布图、交互式展示图。4.1 第一张图真实值与预测值的对比曲线第一张必出的是测试集上真实气温与预测气温的时间序列对比它能直观暴露模型在哪段时间偏冷或偏热。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(12, 5)) t_index df.index[-len(y_test):] ax.plot(t_index, y_test, label真实值, linewidth1.2) ax.plot(t_index, best_model.predict(X_test), label预测值, linewidth1.2, alpha0.8) ax.set_xlabel(日期) ax.set_ylabel(平均气温 (°C)) ax.set_title(随机森林测试集气温预测效果) ax.legend() fig.autofmt_xdate() plt.tight_layout() plt.savefig(prediction_curve.png, dpi200)df.index[-len(y_test):]取的是与测试集长度严格对应的最后一段日期索引保证两张曲线的横轴时间点完全对齐。plt.rcParams[font.sans-serif] [SimHei]让中文字体正常显示如果在 Linux 环境先装中文字体否则所有中文标签都会变方块。dpi200保证图片插入报告时不糊。4.2 第二张图残差分布暴露系统偏差对比曲线看总体残差图看细节。残差是真实值减预测值画在纵轴上观察是否围绕 0 对称分布是定位系统误差最直接的手段。residuals y_test - best_model.predict(X_test) fig, ax plt.subplots(figsize(12, 4)) ax.scatter(range(len(residuals)), residuals, s8, alpha0.5) ax.axhline(0, colorred, linestyle--, linewidth1) ax.fill_between(range(len(residuals)), -1.5, 1.5, colorgray, alpha0.1) ax.set_xlabel(测试集样本序号按时间) ax.set_ylabel(残差 (°C)) ax.set_title(随机森林预测残差分布) plt.tight_layout() plt.savefig(residual_plot.png, dpi200)残差按时间顺序排列据此能判断模型是否存在季节性偏差。如果负残差集中在冬季、正残差集中在夏季说明模型对极端温度的响应滞后应当回头检查季节编码和滞后特征如果残差围绕 0 高频抖动但幅度不大说明误差来自天气系统自身的不可预测部分当前特征集的能力边界已经到头。4.3 交互式图表的低成本方案Plotly 与 HTML 导出答辩演示时能实时缩放的交互式图表比静态 PNG 更抓人眼球。Plotly 的write_html能把图表导出为独立 HTML 文件答辩机器不需要安装 Python 环境双击就能打开。import plotly.graph_objects as go fig go.Figure() fig.add_trace(go.Scatter( xdf.index[-len(y_test):], yy_test, modelines, name真实值 )) fig.add_trace(go.Scatter( xdf.index[-len(y_test):], ybest_model.predict(X_test), modelines, name预测值 )) fig.update_layout( title天气预测结果可缩放, xaxis_title日期, yaxis_title平均气温 (°C), hovermodex unified ) fig.write_html(forecast_interactive.html)hovermodex unified是演示体验提升最明显的一项配置鼠标悬停在某个日期上真实值和预测值同时显示在提示框中可以直接对着某一天的气温偏差展开讲解。write_html生成的文件内嵌了 Plotly 的 JavaScript 库离线也能渲染不需要额外部署任何服务。4.4 地图可视化多城市温度对比的轻量做法课设里如果需要展示多个城市的天气差异散点地图比多个子图更高效。用经纬度作为坐标温度映射到颜色伸缩条城市名做成悬停字段一屏就能看出空间分布。实现时核心工作是把“城市、温度”组织成经纬度表格再绑定到marker.color。地图在答辩现场是加分项但要提前确认底图瓦片能否加载演示时图出不来比图难看更尴尬。建议只把地图作为附加展示不作为讲解主线。5. 时序交叉验证与误差归因课设验收前最后要做的事5.1 滚动回测让分数不再是“一次性运气”GridSearchCV 完成了参数选优但那只保证模型在单一切分上的表现。为了让报告里报出的数字更可信建议额外跑一次滚动回测。用 TimeSeriesSplit 在完整特征矩阵上做多轮训练拿每折验证误差的均值与标准差汇报re_rmse [] tscv TimeSeriesSplit(n_splits5) for train_idx, valid_idx in tscv.split(X): rf RandomForestRegressor(**search.best_params_, random_state42) rf.fit(X[train_idx], y[train_idx]) pred rf.predict(X[valid_idx]) re_rmse.append(np.sqrt(mean_squared_error(y[valid_idx], pred))) print(滚动回测 RMSE %.3f ± %.3f °C % (np.mean(re_rmse), np.std(re_rmse)))输出格式给出均值与波动范围例如1.214 ± 0.107 °C。均值是模型的期望误差标准差刻画不同时间段上的稳定性。如果某个窗口误差显著高于均值对照该窗口对应的季节与天气事件通常能定位“为什么这段不准”。5.2 误差归因的三个层次回答“为什么不准”答辩被问误差时按三层结构组织回答比“数据不够多”有说服力得多。第一层数据粒度日平均气温预测的 RMSE 天然低于逐小时气温先声明任务边界。第二层特征边界机器学习模型只能从你给的特征中获取信息缺少气压、风速、云量时部分天气过程无法被前置捕捉。第三层时序长度短滞后特征对日平均气温贡献最大滞后超过 14 天后信息量衰减明显继续堆滞后收益小于采集更多气象要素。围绕这三层展开等于把课设缺陷讲成了主动的工程取舍。5.3 一个能直接放进报告的收官验证残差直方图如果还想再加一张有说服力的图滚动回测的全部验证集残差汇总后画直方图并叠加正态曲线。残差接近正态分布说明误差是随机白噪声模型没有遗漏可被利用的结构信息如果直方图出现双峰意味着有两类天气过程的误差模式截然不同这是继续做特征工程的明确信号。绘图代码就是plt.hist加scipy.stats.norm.pdf两行但在报告里它代表的意思是“现有特征的信息已经被榨干”——这一个判断比任何一行代码都更能证明你理解了评估的本质。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻