恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Python随机森林回归实战:基于气象数据的多变量气温预测
首页
资讯中心
/
Python随机森林回归实战:基于气象数据的多变量气温预测
Python随机森林回归实战:基于气象数据的多变量气温预测
发布时间:2026/10/1 21:14:04
简介这是一份面向高校学生与开发者的随机森林气温预测项目源码适用于毕业设计、课程设计及机器学习入门实践。项目以Python实现借助Scikit-learn构建随机森林模型覆盖数据预处理、特征选择、模型训练与评估等完整流程帮助读者理解多变量气象因素与气温之间的复杂关系。压缩包共19个文件约4.23MB以py脚本、csv数据集、xml配置、txt说明及zbak备份为主其中py文件承担数据读取、参数调优与预测主逻辑csv提供训练与扩展气温数据说明文档辅助快速上手。目前已有117人学习。通过该资源读者可获得一套可运行的预测方案与模块化目录结构掌握随机森林在时间序列预测中的应用思路并积累从设计、实现到测试维护的完整项目经验适合作为学习研究参考不得用于商业用途。1. 从一份气温观测表说起随机森林回归到底能解决什么手头有一份气象站逐日观测数据字段包括平均气温、最高最低气温、气压、湿度、风速、日照时数你想预测未来某天的气温。线性回归跑出来 R² 只有 0.6 出头残差图上一团糟——气温和气压、湿度之间根本不是线性关系季节项还带着明显的周期性。这时候换随机森林回归往往能把 R² 拉到 0.85 以上而且不需要你对特征做复杂的多项式变换。这就是「python实现基于随机森林的气温预测」这个题目真正要解决的问题用集成学习里最稳的回归器之一把多变量气象观测映射成连续的温度值。这个方向适合两类人一类是正在做毕业设计或课程设计的学生需要一个有真实数据集、有完整代码链路、能画出对比图、还能写进论文的题目另一类是刚接触 sklearn 的 Python 学习者想找一个比鸢尾花分类更有实际感的回归项目练手。它不涉及深度学习框架一台普通笔记本就能跑完数据量通常在几千到几万条之间训练时间以秒计。接下来我会把数据准备、特征工程、模型训练、调参、评估这条链路完整走一遍中间该踩的坑一个不落。2. 数据从哪来、长什么样气温预测的数据准备与特征工程2.1 数据集选择与字段理解做气温预测数据质量决定上限。常见做法是找一份公开的历史气象数据集比如某地区多年的逐日观测记录。字段一般包含日期、平均气温、最高气温、最低气温、气压、相对湿度、风速、降水量、日照时数等。你要预测的目标列通常是「平均气温」或「次日最高气温」这个必须在动手前定死不然后面特征工程全乱。拿到数据先做三件事看形状、看缺失、看时间跨度。用 pandas 几行就能摸清底细import pandas as pd import numpy as np # 读取气象数据假设是 CSV 格式 df pd.read_csv(weather_data.csv, parse_dates[date]) # 基本信息行数、列数、各列类型 print(df.shape) print(df.dtypes) # 缺失值统计 print(df.isnull().sum()) # 时间范围 print(df[date].min(), df[date].max()) # 目标列分布 print(df[avg_temp].describe())这段代码的作用是快速判断数据能不能直接用。parse_dates把日期列转成 datetime 类型后面做时间特征全靠它。isnull().sum()告诉你哪些列缺得多——如果某列缺失超过 30%考虑直接丢掉缺得少的用插值补。describe()看目标列的均值、标准差、最大最小值如果出现 -999 这种异常值基本是传感器故障码要替换成 NaN 再处理。参数上注意两点parse_dates里的列名必须和 CSV 表头完全一致如果数据是中文列名读进来后建议统一改成英文避免后续 sklearn 报编码错误。2.2 时间特征与滞后特征的构造原始气象数据里日期本身不是数值但时间信息极其重要。气温有强烈的季节性和日周期性你必须把「第几个月」「一年中的第几天」这类信息提取出来。更关键的是滞后特征——今天的气温跟昨天、前天的气温高度相关把前几天的值作为特征喂给模型预测精度会明显提升。# 按日期排序确保时间顺序正确 df df.sort_values(date).reset_index(dropTrue) # 提取时间特征 df[month] df[date].dt.month df[day_of_year] df[date].dt.dayofyear df[day_of_week] df[date].dt.dayofweek # 构造滞后特征前1天、前2天、前3天的平均气温 for lag in [1, 2, 3]: df[favg_temp_lag{lag}] df[avg_temp].shift(lag) # 构造滑动窗口均值过去3天、7天的平均气温 df[avg_temp_roll3] df[avg_temp].shift(1).rolling(window3).mean() df[avg_temp_roll7] df[avg_temp].shift(1).rolling(window7).mean() # 删除因 shift 产生的空值行 df df.dropna().reset_index(dropTrue)这里有几个容易翻车的地方。第一shift(1)表示用前一天的值如果你不小心写成shift(-1)那就是用未来的数据预测现在属于数据泄露模型评估会虚高得离谱。第二滑动窗口必须先shift(1)再rolling否则当天的气温会混进窗口均值里同样是泄露。第三dropna()会删掉前几行如果数据本身只有几百条删完可能不够训练这时候要么减少滞后阶数要么用填充值补。特征构造完之后用df.corr()[avg_temp]看一眼各特征和目标的相关性相关性低于 0.1 的特征可以考虑剔除减少噪声。2.3 训练集与测试集的切分方式气温数据是时间序列不能随机打乱切分。常见做法是按时间顺序前 80% 做训练后 20% 做测试。如果你用train_test_split默认的随机切分模型会「偷看」到未来数据评估结果不可信。from sklearn.model_selection import TimeSeriesSplit # 方法一按时间点硬切 split_idx int(len(df) * 0.8) train_df df.iloc[:split_idx] test_df df.iloc[split_idx:] # 特征列和目标列 feature_cols [c for c in df.columns if c not in [date, avg_temp]] X_train train_df[feature_cols] y_train train_df[avg_temp] X_test test_df[feature_cols] y_test test_df[avg_temp] # 方法二TimeSeriesSplit 交叉验证调参时用 tscv TimeSeriesSplit(n_splits5)TimeSeriesSplit在调参阶段比普通 KFold 更合适它保证每次验证集都在训练集之后。参数n_splits5表示切 5 折数据量少于 1000 条时建议降到 3 折否则每折验证集太小评分波动大。3. 随机森林回归模型怎么搭从默认参数到可用的基线3.1 随机森林回归的核心参数与选型理由随机森林回归的本质是建很多棵决策树每棵树用自助采样bootstrap抽一部分样本、随机选一部分特征来分裂最后把所有树的预测值取平均。相比单棵决策树它不容易过拟合相比线性回归它能自动捕捉非线性和特征交互。对气温预测这种「多特征、非线性、有噪声」的场景它是性价比很高的选择。sklearn 里用RandomForestRegressor几个关键参数必须理解参数含义常用取值影响n_estimators树的数量100~500越多越稳但训练变慢max_depth树的最大深度None / 5~20太深过拟合太浅欠拟合min_samples_split节点分裂最小样本数2~10越大越保守min_samples_leaf叶节点最小样本数1~5越大越平滑max_features每次分裂考虑的特征数1.0 / sqrt / 0.5越小越随机方差越低新手最容易犯的错是只改n_estimators其他全默认。实际上max_depth和min_samples_leaf对过拟合的控制更直接。我一般先跑一版默认参数做基线再针对性调。3.2 训练基线模型并输出评估指标先把模型跑起来拿到一个可对比的基线分数。回归任务常用 MAE、RMSE、R² 三个指标。from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score # 基线模型默认参数固定随机种子保证可复现 rf RandomForestRegressor(n_estimators100, random_state42, n_jobs-1) rf.fit(X_train, y_train) # 预测 y_pred rf.predict(X_test) # 评估 mae mean_absolute_error(y_test, y_pred) rmse np.sqrt(mean_squared_error(y_test, y_pred)) r2 r2_score(y_test, y_pred) print(fMAE: {mae:.2f}) print(fRMSE: {rmse:.2f}) print(fR2: {r2:.4f})random_state42保证每次跑结果一致写论文时这点很重要。n_jobs-1让所有 CPU 核心参与训练数据量大时能省不少时间。评估指标里MAE 告诉你平均误差多少度RMSE 对大误差更敏感R² 反映模型解释了多大比例的方差。气温预测里MAE 能压到 2 度以内、R² 到 0.85 以上就算一个能写进报告的基线了。如果 R² 低于 0.7先别急着调参回头检查特征工程——滞后特征是不是漏了时间特征是不是没加缺失值处理是不是有问题。特征上的问题调参救不回来。3.3 特征重要性分析与特征筛选随机森林自带特征重要性输出这是它比很多模型好用的地方。跑完基线后把重要性排序画出来能帮你判断哪些特征真正在起作用。import matplotlib.pyplot as plt # 获取特征重要性 importances rf.feature_importances_ feat_imp pd.Series(importances, indexfeature_cols).sort_values(ascendingFalse) print(feat_imp.head(10)) # 可视化 feat_imp.head(15).plot(kindbarh, figsize(8, 6)) plt.xlabel(Importance) plt.title(Feature Importance) plt.gca().invert_yaxis() plt.tight_layout() plt.savefig(feature_importance.png, dpi150)通常你会发现滞后 1 天的气温、滑动窗口均值、day_of_year 排在前列这符合直觉。如果某个特征重要性接近 0可以试着删掉再训练看 R² 有没有下降——没降就说明它是冗余的。但注意重要性低不代表一定没用可能是它和另一个特征高度相关被「分摊」了重要性。删特征要一次删一个逐步验证。4. 调参、交叉验证与结果可视化让模型真正能写进报告4.1 用 GridSearchCV 做超参数搜索基线跑通之后下一步是调参。手工试参数效率低用GridSearchCV配合TimeSeriesSplit自动搜。注意网格别开太大否则跑一晚上都出不来。from sklearn.model_selection import GridSearchCV # 定义参数网格 param_grid { n_estimators: [100, 200, 300], max_depth: [None, 10, 20], min_samples_leaf: [1, 2, 4], max_features: [sqrt, 0.5] } # 时间序列交叉验证 tscv TimeSeriesSplit(n_splits3) grid GridSearchCV( estimatorRandomForestRegressor(random_state42, n_jobs-1), param_gridparam_grid, cvtscv, scoringneg_mean_absolute_error, n_jobs-1, verbose1 ) grid.fit(X_train, y_train) print(最佳参数:, grid.best_params_) print(最佳MAE:, -grid.best_score_)scoringneg_mean_absolute_error是因为 sklearn 的评分函数统一「越大越好」MAE 本身越小越好所以取负。cvtscv保证每折都是时间顺序切分。verbose1会打印搜索进度网格大的时候方便看跑到哪了。搜完之后用grid.best_estimator_拿到最优模型在测试集上再评一次这个分数才是最终写进报告的。参数网格的组合数是各参数取值个数的乘积上面这个网格是 3×3×3×254 种组合每种跑 3 折总共 162 次训练。如果数据量大把n_estimators的范围缩小或者先用RandomizedSearchCV粗搜再精调。4.2 预测结果对比图与残差分析论文和报告里光有数字不够图能直观说明问题。至少画两张一张是预测值和真实值的时间序列对比一张是残差分布。import matplotlib.pyplot as plt fig, axes plt.subplots(2, 1, figsize(12, 8)) # 图1真实值 vs 预测值 axes[0].plot(y_test.values, labelActual, alpha0.8) axes[0].plot(y_pred, labelPredicted, alpha0.8) axes[0].set_xlabel(Sample Index) axes[0].set_ylabel(Temperature) axes[0].legend() axes[0].set_title(Actual vs Predicted Temperature) # 图2残差分布 residuals y_test.values - y_pred axes[1].hist(residuals, bins30, edgecolorblack) axes[1].axvline(0, colorred, linestyle--) axes[1].set_xlabel(Residual) axes[1].set_ylabel(Frequency) axes[1].set_title(Residual Distribution) plt.tight_layout() plt.savefig(prediction_result.png, dpi150)残差图如果呈现明显的偏态或者有周期性规律说明模型还有系统性偏差可能需要加特征或者换模型。理想情况下残差应该近似正态分布均值接近 0。如果残差在某个温度区间特别大说明模型对极端温度预测能力弱这是随机森林的常见短板——它倾向于预测到训练集均值附近对极端值不敏感。4.3 模型持久化与推理脚本训练好的模型要存下来不然每次预测都重新训练不现实。sklearn 推荐用 joblib 保存。import joblib # 保存模型 joblib.dump(grid.best_estimator_, rf_temp_model.pkl) # 加载并预测新数据 loaded_model joblib.load(rf_temp_model.pkl) # 假设有一条新样本 new_sample X_test.iloc[[0]] pred loaded_model.predict(new_sample) print(f预测温度: {pred[0]:.2f})保存时注意模型文件依赖训练时的 sklearn 版本换环境可能加载失败。写毕业设计的话在 README 里注明 sklearn 版本号。推理脚本里新样本的特征列顺序必须和训练时完全一致少一列或者顺序错了预测结果会莫名其妙地偏。稳妥做法是把feature_cols也存下来推理前用它来对齐列。5. 避坑与排查气温预测项目里最容易翻车的五个地方5.1 数据泄露滞后特征用错方向现象模型在测试集上 R² 高达 0.99但实际预测未来气温时误差巨大。原因构造滞后特征时用了shift(-1)或者滑动窗口没先shift导致特征里混入了目标当天的值。模型在训练时「看到」了答案评估自然虚高。解决所有滞后和滑动窗口特征一律先shift(1)再计算。写完特征工程后用df.corr()[avg_temp]检查如果某个特征和目标相关性超过 0.98大概率有泄露重点排查。5.2 时间切分错误随机打乱导致评估失真现象用train_test_split随机切分R² 比时间顺序切分高出一大截。原因随机切分让训练集里混入了测试集时间点附近的数据模型相当于「插值」而不是「外推」。气温数据时间相邻的样本高度相似随机切分等于变相泄露。解决统一用时间顺序切分或TimeSeriesSplit。如果论文里需要对比可以两种都跑但以时间顺序切分的结果为准并说明原因。5.3 缺失值处理不当插值方法引入偏差现象填充缺失值后模型在缺失较多的月份误差明显偏大。原因用了全局均值填充忽略了气温的季节性。1 月的缺失值被 7 月的均值填了特征完全失真。解决按月份分组做均值填充或者用时间序列插值interpolate(methodtime)。缺失比例超过 30% 的列直接删除别硬填。5.4 特征重要性误读相关特征分摊重要性现象删掉一个重要性很低的特征后模型 R² 反而下降了。原因两个特征高度相关重要性被分摊单独看每个都不高但删掉一个后另一个无法完全替代。解决删特征前先看特征间的相关性矩阵相关性超过 0.9 的特征成组处理。用permutation_importance做排列重要性检验比默认的基于不纯度的的重要性更可靠。5.5 极端值预测偏保守随机森林的固有短板现象高温和低温天气的预测值总是偏向中间极端天气误差大。原因随机森林的预测是多数树的平均平均操作天然会把极端值拉向均值。这是集成模型的特性不是 bug。解决如果极端值预测很重要可以尝试分位数回归森林RandomForestQuantileRegressor或者对目标值做变换后再训练。也可以在特征里加入「是否极端天气」的标记让模型学到分段模式。6. 让这个项目再上一个台阶三个可落地的进阶技巧第一个技巧是加入外部特征。纯气象站数据的信息量有限如果能拿到地理位置纬度、海拔、周边站点数据甚至当天的天气类型编码模型精度还能再提。我试过在特征里加「前一天的天气状况」独热编码MAE 降了大概 0.3 度。做法很简单把类别列用pd.get_dummies展开拼到特征矩阵里就行但注意训练集和测试集的编码要一致用align对齐列。第二个技巧是用RandomizedSearchCV替代网格搜索。当参数空间大的时候随机搜索能在更短时间内找到接近最优的组合。我一般先随机搜 50 组看最优参数落在哪个区间再在那个区间做小网格精搜。这样比一上来就开大网格省一半以上时间。第三个技巧是模型融合。随机森林可以和梯度提升树GradientBoostingRegressor或 XGBoost 做加权平均通常能再降一点误差。权重不用手动调用验证集上的误差反比来定就行。代码上就是把两个模型的预测值按权重相加from sklearn.ensemble import GradientBoostingRegressor # 训练第二个模型 gbr GradientBoostingRegressor(n_estimators200, learning_rate0.05, random_state42) gbr.fit(X_train, y_train) # 两个模型的预测 pred_rf rf.predict(X_test) pred_gbr gbr.predict(X_test) # 按验证集误差反比加权 w_rf, w_gbr 0.6, 0.4 pred_ensemble w_rf * pred_rf w_gbr * pred_gbr print(融合后 R2:, r2_score(y_test, pred_ensemble))权重 0.6 和 0.4 是我在验证集上试出来的你的数据不一定适用自己跑一遍验证集确定。融合的收益通常不大R² 提升 0.01 到 0.03 左右但写进论文里是一个加分项说明你考虑了模型层面的优化。最后说个血泪经验这个项目最大的坑不在模型在数据。我见过太多人花三天调参结果发现数据里有一列是字符串格式的温度sklearn 直接报错或者静默转成类别编码模型学出来的东西毫无意义。动手前用df.info()和df.head(20)把每一列的类型和取值看一遍比什么调参技巧都管用。希望帮到你。本文还有配套的精品资源点击获取