天气预测机器学习实战:从特征工程到模型评估与可视化

天气预测机器学习实战:从特征工程到模型评估与可视化 简介面向毕业设计、期末大作业与课程设计的Python机器学习天气预测与数据可视化项目是一份可快速部署的完整方案适合需要参考高分项目的学生与开发者。压缩包共二十四个文件体积约一点四二MB目前已有四百零三人学习。项目内含四个Python源码覆盖数据采集、数据清洗、特征处理、模型训练与可视化展示等环节四个CSV数据文件分别作为训练集、验证集与待预测数据十二张JPG图片呈现各阶段可视化图表另有HTML页面用于交互展示PKL文件保存已训练模型MD文档提供说明与使用指引。代码注释详细逻辑清晰新手也能逐步理解部署简单、数据完整可直接复现天气预测与数据展示全流程。作者自述为手打九十八分项目导师认可度高是毕业设计、课程设计及期末大作业的高分参考。1. 从一份天气数据集到可答辩的机器学习项目天气预测是机器学习入门最容易被低估的场景数据自带时间序列属性特征之间有明显相关性又天然适合用可视化呈现结论。很多人在课程设计或求职作品集里选这个题目但最终提交的往往是一份调好参数的 Jupyter Notebook外加几张 matplotlib 折线图——功能上没错却离“高分项目”的标准差得很远。真正的差距不在模型精度而在数据是否被充分理解、特征是否做了有效构造、可视化是否支撑了结论。这篇博文围绕一个典型项目形态展开用 Python 完成天气数据清洗、特征工程、多模型训练与评估再借助可视化把预测结果和真实趋势对照呈现。项目结构按“数据处理 → 建模 → 评估 → 可视化”推进适合正在做课程设计、准备面试作品集或想系统走一遍机器学习流程的开发者。读完你会得到一套可以直接复用的代码路径也会知道哪些环节最容易扣分、怎么补才能拿到高分。2. 天气预测的数据预处理与特征工程2.1 天气数据集里藏着哪些可预测信息常见天气数据集来自气象站逐小时或逐日记录字段通常包括日期时间、气温、湿度、气压、风速、风向、降水量、天气状况等。直接拿原始字段丢给模型也能跑但预测效果会很粗糙原因在于天气数据有三个特点强周期性、强自相关性、存在突变点。周期性来自昼夜交替和季节变化气温在一天内呈近似正弦波动在一年内也有明显冷暖周期自相关性体现在“今天的温度大概率接近昨天”这比任何外部特征都更能预测明天突变点则来自冷空气过境、降雨前后气压骤降等。模型要捕捉这三类规律就必须在特征层面对时间信息做展开而不是把日期当成一个普通数值。2.2 用 Pandas 完成时间序列数据的清洗与重采样拿到原始 CSV 后第一步永远是先看数据形态。天气数据最常见的坑包括时间列是字符串且格式不统一、存在缺失值、存在极端异常值传感器故障、日期有跳变。下面是一段完整的清洗流程import pandas as pd import numpy as np # 读取原始数据解析时间列 df pd.read_csv(weather_data.csv, parse_dates[date]) # 统一时间频率按小时重采样缺失值向前填充 df df.set_index(date).resample(H).ffill() # 去除明显异常气温超出物理范围的值直接置空再插值 df.loc[df[temperature] 50, temperature] np.nan df.loc[df[temperature] -50, temperature] np.nan df[temperature] df[temperature].interpolate(methodtime) # 删除完全为空的行 df df.dropna(subset[temperature, humidity]) # 打出数据概况 print(df.info()) print(df.describe())参数说明resample(H)将数据统一到小时粒度避免时间间隔不齐导致后续特征错位ffill()适合短时间缺失但长时间缺失会导致特征失真更稳妥的做法是用interpolate(methodtime)按时间间隔线性插值温度阈值 50 和 -50 是气象数据的物理边界不同数据集可能需要调整。重采样这一步非常关键它决定了后续所有滞后特征是否对齐。2.3 构造滞后特征、滑动窗口特征与周期特征模型预测明天 14 点的温度本质上是回归问题。要让模型理解“今天和昨天的温度关系”需要把历史值变成特征。常见做法是按滞后阶数构造移位列同时计算滑动均值来平滑噪声# 滞后特征过去1小时、3小时、6小时、24小时的气温 for lag in [1, 3, 6, 24]: df[ftemp_lag_{lag}] df[temperature].shift(lag) # 滑动窗口特征过去6小时和24小时的均值、标准差 df[temp_rolling_mean_6h] df[temperature].rolling(window6).mean() df[temp_rolling_std_6h] df[temperature].rolling(window6).std() df[temp_rolling_mean_24h] df[temperature].rolling(window24).mean() # 周期特征小时和月份转成三角函数避免“23点和0点”被切开 df[hour_sin] np.sin(2 * np.pi * df.index.hour / 24) df[hour_cos] np.cos(2 * np.pi * df.index.hour / 24) df[month_sin] np.sin(2 * np.pi * df.index.month / 12) df[month_cos] np.cos(2 * np.pi * df.index.month / 12) # 气压变化率过去3小时气压差对降雨和温度突变有指示意义 df[pressure_diff_3h] df[pressure].diff(3)周期特征用正弦余弦而不是原始数值是这类项目最容易加分的地方。直接用 hour23 和 hour0 会被模型当成完全不同的两个值但用三角函数表达后两者距离很近符合时间循环的直觉。气压变化率是天气预测里常被忽略却很有用的特征冷锋过境前气压会快速下降这个数值往往比气压绝对值更有效。2.4 训练集与测试集的切分不能随机打乱天气数据是典型的时间序列切分方式与普通分类任务不同不能用train_test_split随机划分否则会造成数据泄漏模型“偷看”了未来数据测试分数虚高。正确做法是按时间顺序切分# 按时间顺序切分前80%训练后20%测试 feature_cols [col for col in df.columns if col ! temperature] X df[feature_cols].dropna() y df.loc[X.index, temperature] cutoff int(len(X) * 0.8) X_train, X_test X.iloc[:cutoff], X.iloc[cutoff:] y_train, y_test y.iloc[:cutoff], y.iloc[cutoff:] # 对特征做标准化注意只能用训练集的均值方差 from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)scaler只对训练集fit测试集直接用同一组参数transform这是容易被忽略的细节。如果对整体数据做标准化相当于测试集的信息混入了训练过程在严格评估下属于泄漏。3. 机器学习模型的选型、训练与效果对比3.1 回归模型怎么选从线性回归到树模型天气预测的标签是连续数值属于回归任务。在这个项目里候选模型按复杂度递增大致是线性回归、Ridge/Lasso、随机森林、XGBoost/LightGBM。线性回归适合做基线验证特征是否有预测力树模型能捕捉非线性关系在实际天气数据上通常明显优于线性模型。LightGBM 是这类项目的首选因为它训练快、自带处理缺失值能力、对特征尺度不敏感而且正则化参数容易调。XGBoost 表现同样出色但调参成本略高。如果项目要求必须用经典机器学习算法随机森林是最稳妥的中间选择。3.2 用 LightGBM 训练天气预测模型并输出特征重要性下面是完整的训练与评估代码import lightgbm as lgb from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score # 构建 LightGBM 数据集 train_data lgb.Dataset(X_train_scaled, labely_train) test_data lgb.Dataset(X_test_scaled, labely_test, referencetrain_data) # 参数配置学习率调低用早停防止过拟合 params { objective: regression, metric: mae, learning_rate: 0.05, num_leaves: 63, max_depth: 7, min_child_samples: 20, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, verbose: -1 } model lgb.train( params, train_data, num_boost_round2000, valid_sets[test_data], callbacks[lgb.early_stopping(stopping_rounds50)] ) # 预测与评估 y_pred model.predict(X_test_scaled, num_iterationmodel.best_iteration) 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}°C, RMSE: {rmse:.2f}°C, R²: {r2:.4f}) # 特征重要性按分裂次数 importance pd.DataFrame({ feature: feature_cols, importance: model.feature_importance(importance_typesplit) }).sort_values(importance, ascendingFalse) print(importance.head(10))参数说明learning_rate0.05是经验值学习率越小越不容易过拟合但训练时间变长num_leaves63对应max_depth7附近的容量两者要配合调整feature_fraction和bagging_fraction都是抑制过拟合的随机采样参数对树模型效果显著。early_stopping会自动选择最优迭代轮数避免手动调num_boost_round的麻烦。特征重要性输出会告诉你哪些特征真正起了作用——通常滞后 24 小时的气温和滑动均值排在前面。3.3 多模型对比的意义与可视化呈现课程项目拿高分的关键之一是有对比。单独跑一个 LightGBM 只能说明“能跑”但对比线性回归、随机森林和 LightGBM 的误差指标才能体现你对不同算法的理解。建议将多个模型的结果整理成一个对比表格并配合柱状图展示 MAE 差异from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor # 线性回归基线 lr LinearRegression() lr.fit(X_train_scaled, y_train) y_pred_lr lr.predict(X_test_scaled) # 随机森林 rf RandomForestRegressor(n_estimators200, max_depth10, min_samples_leaf5, n_jobs-1, random_state42) rf.fit(X_train_scaled, y_train) y_pred_rf rf.predict(X_test_scaled) # 汇总指标 results [] for name, pred in [(Linear Regression, y_pred_lr), (Random Forest, y_pred_rf), (LightGBM, y_pred)]: results.append({ Model: name, MAE: mean_absolute_error(y_test, pred), RMSE: np.sqrt(mean_squared_error(y_test, pred)), R²: r2_score(y_test, pred) }) results_df pd.DataFrame(results) print(results_df)n_estimators200是随机森林的常用配置继续增加树的数量收益递减max_depth10和min_samples_leaf5控制单棵树复杂度防止随机森林在少量样本上过拟合。三个模型用同一份标准化后的数据训练评估指标才有可比性。4. 用可视化呈现预测结果、误差分布与特征关系4.1 matplotlib 绘制预测值与真实值的对比曲线可视化的价值不只是“好看”。在这类项目里图表承担两种功能一是验证模型效果二是暴露模型的系统性偏差。最基本的一张图是测试集时间段内的预测值与真实值对比曲线import matplotlib.pyplot as plt import matplotlib.dates as mdates plt.figure(figsize(14, 5)) test_dates X_test.index plt.plot(test_dates, y_test, labelActual, linewidth1.5, color#333333) plt.plot(test_dates, y_pred, labelPredicted, linewidth1.2, color#d62728, alpha0.8) plt.xlabel(Date) plt.ylabel(Temperature (°C)) plt.title(Temperature Prediction: Actual vs LightGBM Predicted) plt.legend() plt.grid(True, alpha0.3) # 控制横轴刻度密度 plt.gca().xaxis.set_major_locator(mdates.AutoDateLocator()) plt.gca().xaxis.set_major_formatter(mdates.DateFormatter(%m-%d)) plt.tight_layout() plt.savefig(prediction_curve.png, dpi150) plt.show()mdates.AutoDateLocator会自动根据时间跨度调整刻度密度避免横轴标签挤成一团。对比曲线能直观看出模型在哪些时段误差更大——通常是温度突变处冷空气过境和昼夜交替的峰值点。如果预测曲线整体滞后于真实曲线说明滞后特征还不够或模型对突变不敏感。4.2 误差分布图比单次预测误差更能说明问题MAE 是 1.5°C 还是 0.8°C听上去只是个数字但误差分布才能揭示深层信息。将误差按小时或月份分组画箱线图是天气预测项目常用的诊断手段# 计算误差并按小时分组 errors (y_pred - y_test).to_frame(nameerror) errors[hour] test_dates.hour # 按小时画箱线图 plt.figure(figsize(12, 5)) errors.boxplot(columnerror, byhour, gridFalse, showfliersFalse) plt.axhline(0, colorred, linewidth0.8, linestyle--) plt.title(Prediction Error Distribution by Hour of Day) plt.suptitle() plt.xlabel(Hour of Day) plt.ylabel(Error (°C)) plt.tight_layout() plt.savefig(error_by_hour.png, dpi150) plt.show()这个图能直接暴露模型的薄弱时段。如果凌晨 2-4 点误差中位数明显偏高通常是因为夜间温度变化受云量和辐射影响大而单纯的历史温度特征无法表达这些信息。箱线图用showfliersFalse隐藏极端离群点避免个别传感器异常干扰视图。4.3 特征重要性可视化与相关性热力图除了预测结果可视化项目报告里还应该包含特征重要性和特征相关性的图表。这两种图展示的不是“模型有多准”而是“你理解数据有多深”。特征重要性用横向柱状图最清晰相关性热力图用 Seaborn 绘制import seaborn as sns # 特征重要性横向柱状图 top_features importance.head(10) plt.figure(figsize(10, 6)) plt.barh(top_features[feature][::-1], top_features[importance][::-1], color#4C72B0) plt.xlabel(Split Count) plt.title(Top 10 Feature Importance (LightGBM)) plt.tight_layout() plt.savefig(feature_importance.png, dpi150) plt.show() # 相关性热力图只选部分有代表性的列 corr_cols [temperature, humidity, pressure, wind_speed, temp_lag_1, temp_lag_24, temp_rolling_mean_6h] plt.figure(figsize(8, 6)) sns.heatmap(df[corr_cols].corr(), annotTrue, fmt.2f, cmapRdBu_r, center0) plt.title(Correlation Matrix of Key Features) plt.tight_layout() plt.savefig(correlation_heatmap.png, dpi150) plt.show()热力图的annotTrue会在格子里显示相关系数数值fmt.2f控制小数位。这张图通常能发现一些有趣的事实比如温度与滞后 1 小时温度的相关系数高达 0.98而温度与湿度呈负相关——这些结论放进项目报告里比单独贴模型指标更有说服力。4.4 用交互式图表提升项目展示效果如果项目要求较高可以在静态 matplotlib 图表之外额外生成一份交互式可视化。Plotly 是 Python 生态中上手最快的交互式绘图库能在一个 HTML 文件里同时展示预测曲线、误差分布和特征重要性无需额外部署服务。这份 HTML 可以作为项目报告的附件提交也可以直接打开给评委演示。import plotly.graph_objects as go from plotly.subplots import make_subplots fig make_subplots( rows2, cols1, shared_xaxesTrue, subplot_titles(Temperature Prediction, Error) ) fig.add_trace(go.Scatter(xtest_dates, yy_test, nameActual, linedict(color#333333, width1.5)), row1, col1) fig.add_trace(go.Scatter(xtest_dates, yy_pred, namePredicted, linedict(color#d62728, width1.2)), row1, col1) fig.add_trace(go.Bar(xtest_dates, yerrors[error], nameError, marker_color#4C72B0), row2, col1) fig.update_layout(height600, titleInteractive Weather Forecast Result, hovermodex unified) fig.write_html(weather_prediction_interactive.html)hovermodex unified让鼠标悬停在某个时间点时上下两个子图同时显示对应数值交互体验比静态图好很多。这个 HTML 文件不依赖 Python 环境任何浏览器都能打开在答辩现场演示的效果远超翻 Notebook 截图。5. 把项目从七十分做到九十分的三件事5.1 加一列“预测区间”而不是只给一个点很多天气预测项目只输出温度点预测值但真实场景中用户更关心的是可能性范围。用分位数回归或直接计算残差标准差可以给出置信区间这个思路在答辩时非常加分# 用 LightGBM 的 quantile objective 预测上下界 params_lower params.copy() params_lower[objective] quantile params_lower[alpha] 0.1 # 10% 分位 params_upper params.copy() params_upper[objective] quantile params_upper[alpha] 0.9 # 90% 分位 model_lower lgb.train(params_lower, train_data, num_boost_roundmodel.best_iteration) model_upper lgb.train(params_upper, train_data, num_boost_roundmodel.best_iteration) y_pred_lower model_lower.predict(X_test_scaled) y_pred_upper model_upper.predict(X_test_scaled) # 计算实际落在区间内的比例 coverage np.mean((y_test y_pred_lower) (y_test y_pred_upper)) print(f80% 预测区间覆盖率: {coverage:.2%})分位数回归比“均值加减标准差”更稳健因为它不假设误差服从正态分布。天气数据在极端天气下的误差往往呈长尾分布分位数法能更准确地描述上下界。如果覆盖率在 80% 附近说明区间估计合理如果远低于 80%说明模型低估了不确定性。5.2 用 CSV 导出预测结果让评估可以被复核高分项目的共同特征是结果可复核。除了在 Notebook 中打印指标建议将测试集的预测结果导出为 CSV包含时间、真实值和各模型预测值。这样评委可以用自己的方法验证你的结论或者直接用 Excel 画图。同时为源码附加 README 也是隐性的加分项说明环境依赖、运行步骤和文件结构。# 导出测试集的预测结果 result_df pd.DataFrame({ date: test_dates, actual: y_test.values, lr_pred: y_pred_lr, rf_pred: y_pred_rf, lgb_pred: y_pred, lgb_lower: y_pred_lower, lgb_upper: y_pred_upper }) result_df.to_csv(prediction_results.csv, indexFalse)5.3 一个实用的验证技巧预测未来 24 小时除了在测试集上做回归评估另一个常见做法是展示模型对“未来未知数据”的预测能力。具体操作是取数据最后一小时作为基准用滞后特征循环预测未来 24 小时——每次将上一小时的预测值作为下一小时的滞后特征输入。这种递归预测策略能检验模型的长期稳定性也是时间序列项目里一项经典评估方法。即使不把结果写进报告也应该自己跑一遍确认模型不会因为误差累积而快速发散。如果发现 24 小时后预测曲线变成一条水平线说明模型过度依赖近期滞后项需要在特征构造上补充更长周期的信息。本文还有配套的精品资源点击获取