ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python气象预测与可视化工程实践:从数据清洗到双模型对比

Python气象预测与可视化工程实践:从数据清洗到双模型对比 简介这是一份面向Python初学者与高校课程设计学生的天气预测与可视化综合实践项目聚焦气象数据爬取、时序建模预测及多维度图表呈现三大核心环节可直接用于期末大作业或数据分析实训。资源共24个文件包含4个核心Python脚本main.py、GetData.py等实现数据获取与模型训练、4个CSV数据集含训练/验证/测试及实时天气数据、12张效果截图展示预测曲线、热力图、地图渲染等可视化成果以及README说明文档、HTML报告页和预训练Model.pkl模型文件压缩包仅1.42MB轻量易部署。已有203人学习下载项目经助教审定、本地完整编译运行通过评审得分95分以上代码结构清晰、注释充分配套文档详述环境配置、数据准备、各模块调用逻辑及常见问题解决路径真正实现开箱即用与原理可溯。1. 这不是调个 API 就完事的“天气小工具”它是一套能跑通从原始气象数据清洗、时序建模预测到多维可视化闭环的 Python 工程实践期末大作业常被当成“交差项目”但真正拉开差距的从来不是界面有多炫而是背后有没有可复现、可调试、可解释的数据流闭环。这个标题里的“天气预测和天气可视化”绝不是用requests.get(https://api.xxx.com/weather)拿回 JSON 再matplotlib.pyplot.plot()画条折线就叫完成——那是 demo不是工程。它实际覆盖的是本地化气象数据接入支持 CSV/NetCDF/API 三路输入、基于 ARIMA/LSTM 的双模型对比预测框架、带地理坐标映射与时间滑动窗口的动态热力图渲染、以及面向教学场景的模块解耦设计每个.py文件职责单一注释密度 ≥30%。适合两类人一是需要交付有技术纵深感、答辩能讲清每一步为什么这么选的本科生二是想快速搭建一个不依赖云服务、离线可运行、参数全开放的轻量级气象分析基座的初学者。它不追求 72 小时精准预报但保证你能看清数据怎么脏、模型怎么训、误差怎么算、图怎么让老师一眼看懂趋势而非噪音。2. 从零搭起预测骨架数据加载、特征工程与双模型训练流程2.1 支持三种输入源的统一数据加载器CSV / NetCDF / 实时 API项目不强制绑定某一家气象服务商而是提供data_loader.py封装了三类数据源的标准化读取逻辑。核心是抽象出统一的WeatherDataFrame结构必须含timestamp,temperature,humidity,pressure,wind_speed,wind_direction六列时间列为datetime64[ns]类型缺失值用np.nan标记。这样后续所有模块只认这个结构彻底解耦数据来源。# data_loader.py import pandas as pd import xarray as xr import requests from datetime import datetime, timedelta def load_from_csv(filepath: str) - pd.DataFrame: 从本地 CSV 加载自动处理常见时间格式2023-01-01 12:00, 01/01/2023 12:00 df pd.read_csv(filepath) # 尝试多种时间解析失败则抛异常 for fmt in [%Y-%m-%d %H:%M, %m/%d/%Y %H:%M, %Y/%m/%d %H:%M]: try: df[timestamp] pd.to_datetime(df[timestamp], formatfmt) break except ValueError: continue else: raise ValueError(无法解析 timestamp 列请检查格式是否为 YYYY-MM-DD HH:MM) return df[[timestamp, temperature, humidity, pressure, wind_speed, wind_direction]].dropna() def load_from_netcdf(filepath: str) - pd.DataFrame: 读取 NetCDF如 CMIP6 或本地观测站数据提取 surface-level 变量 ds xr.open_dataset(filepath) # 假设变量名符合 CF 标准air_temperature_2m, relative_humidity_2m 等 df pd.DataFrame({ timestamp: ds.time.values, temperature: ds.air_temperature_2m.mean(dim[lat, lon]).values, humidity: ds.relative_humidity_2m.mean(dim[lat, lon]).values, pressure: ds.surface_air_pressure.mean(dim[lat, lon]).values, wind_speed: (ds.eastward_wind_10m**2 ds.northward_wind_10m**2)**0.5 .mean(dim[lat, lon]).values, wind_direction: (180 np.arctan2(ds.northward_wind_10m, ds.eastward_wind_10m) * 180/np.pi) .mean(dim[lat, lon]).values }) return df def load_from_api(city: str Beijing, days: int 7) - pd.DataFrame: 调用免费 Open-Meteo API无需密钥限频合理 url fhttps://api.open-meteo.com/v1/forecast params { latitude: 39.9042, # 北京示例坐标实际应查 geocoding longitude: 116.4074, hourly: temperature_2m,relative_humidity_2m,surface_pressure,wind_speed_10m,wind_direction_10m, forecast_days: days } res requests.get(url, paramsparams) res.raise_for_status() data res.json() timestamps pd.to_datetime(data[hourly][time]) return pd.DataFrame({ timestamp: timestamps, temperature: data[hourly][temperature_2m], humidity: data[hourly][relative_humidity_2m], pressure: data[hourly][surface_pressure], wind_speed: data[hourly][wind_speed_10m], wind_direction: data[hourly][wind_direction_10m] })提示load_from_api中的经纬度硬编码仅作演示实际使用时应先调用https://geocoding-api.open-meteo.com/v1/search?namexxx获取城市坐标避免固定值导致其他城市数据错位。代码里没写这步是因为它属于“可选增强”而本项目定位是“最小可行闭环”优先保证主干路径清晰。2.2 面向时序预测的特征工程滑动窗口 时间周期编码 缺失值鲁棒填充气象数据天然存在周期性日循环、年循环和自相关性当前温度强依赖前几小时。直接喂原始序列给 LSTM 效果差必须做两件事构造监督学习样本把时序转成(X, y)对注入时间先验知识告诉模型“现在是凌晨3点”比“数值是3”更有意义。# features.py import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler def create_sliding_window(df: pd.DataFrame, window_size: int 24, target_col: str temperature) - tuple: 构造滑动窗口样本X.shape (n_samples, window_size, n_features), y.shape (n_samples,) 注意这里 X 包含所有特征温度、湿度等y 是目标列未来1步值 # 时间周期编码sin/cos 表达小时、日、月的循环性 df[hour_sin] np.sin(2 * np.pi * df[timestamp].dt.hour / 24) df[hour_cos] np.cos(2 * np.pi * df[timestamp].dt.hour / 24) df[day_sin] np.sin(2 * np.pi * df[timestamp].dt.dayofyear / 365.25) df[day_cos] np.cos(2 * np.pi * df[timestamp].dt.dayofyear / 365.25) # 特征列原始 编码 feature_cols [temperature, humidity, pressure, wind_speed, wind_direction, hour_sin, hour_cos, day_sin, day_cos] X_raw df[feature_cols].values y df[target_col].shift(-1).dropna().values # 预测下一步 # 对齐 X 和 yX 取前 len(y) 行每行是 window_size 步历史 X [] for i in range(len(y)): if i window_size len(X_raw): X.append(X_raw[i:iwindow_size]) X np.array(X) # 标准化对每个特征维度单独 scaler避免未来信息泄露 scaler StandardScaler() X_reshaped X.reshape(-1, X.shape[-1]) X_scaled scaler.fit_transform(X_reshaped).reshape(X.shape) return X_scaled, y[:len(X)], scaler # 返回 scaler 供预测时复用 # 示例调用 df load_from_csv(data/beijing_2023.csv) X, y, scaler create_sliding_window(df, window_size24) print(fX shape: {X.shape}, y shape: {y.shape}) # X shape: (8736, 24, 9), y shape: (8736,)这段代码的关键在于shift(-1)确保 y 是 X 对应窗口的“下一个时刻”值这是单步预测的标准做法StandardScaler在 reshape 后 fit保证每个特征如 temperature 和 hour_sin独立归一化且 scaler 被返回后续预测时必须用同一个 scaler 处理新数据未使用插值填充缺失值而是靠dropna()丢弃含 nan 的行——因为气象数据缺失往往成片如传感器故障数小时线性插值会引入虚假连续性不如明确剔除。2.3 ARIMA 与 LSTM 双模型并行训练为什么必须对比因为单模型会骗你很多同学只跑一个 LSTM 就交差但气象预测中简单统计模型ARIMA在短期24h常比深度模型更稳。本项目强制双模型对比不是为了炫技而是教你看清你的数据到底适不适合深度学习误差来源是模型能力不足还是数据噪声太大# models.py from statsmodels.tsa.arima.model import ARIMA from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam import numpy as np def train_arima(df: pd.DataFrame, col: str temperature, order: tuple (1,1,1)) - ARIMA: 训练 ARIMA 模型order(p,d,q) 需手动调参本项目默认 (1,1,1) 作为起点 model ARIMA(df[col], orderorder) fitted model.fit() return fitted def train_lstm(X: np.ndarray, y: np.ndarray, units: int 50, dropout_rate: float 0.2, epochs: int 50, batch_size: int 32) - Sequential: LSTM 模型定义2 层 LSTM Dropout 全连接输出 model Sequential([ LSTM(units, return_sequencesTrue, input_shape(X.shape[1], X.shape[2])), Dropout(dropout_rate), LSTM(units, return_sequencesFalse), Dropout(dropout_rate), Dense(1) ]) model.compile(optimizerAdam(learning_rate0.001), lossmse) model.fit(X, y, epochsepochs, batch_sizebatch_size, verbose0) return model # 训练示例 df load_from_csv(data/beijing_2023.csv) arima_model train_arima(df, coltemperature, order(1,1,1)) X, y, scaler create_sliding_window(df, window_size24) lstm_model train_lstm(X, y, units64, dropout_rate0.3, epochs30)为什么 ARIMA 的order不自动搜索因为期末作业场景下auto_arima会消耗大量时间且结果不稳定。我们教学生手动试(1,1,1),(2,1,1),(1,1,2)三组用 AIC 值最低的为准——这本身就是理解时间序列平稳性、差分必要性的过程。而 LSTM 的units和dropout_rate也非越大越好units64在 24 步窗口下已足够捕获日周期再大易过拟合dropout_rate0.3是经验阈值低于 0.1 正则不足高于 0.5 模型学不到有效模式。3. 让预测结果“看得懂”地理热力图 时间趋势图 误差分布直方图三位一体可视化3.1 基于 Matplotlib Cartopy 的地理热力图不用 Basemap拒绝过时依赖Basemap已停止维护本项目采用Cartopy推荐 0.22 版本绘制中国区域气象热力图。关键点底图用 Natural Earth 数据离线可用坐标系严格匹配 WGS84插值用scipy.interpolate.griddata而非imshow粗暴拉伸——后者会让沿海和内陆温度看起来一样“平滑”掩盖真实梯度。# viz_geo.py import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.feature as cfeature import numpy as np from scipy.interpolate import griddata def plot_temperature_heatmap(lats: np.ndarray, lons: np.ndarray, temps: np.ndarray, title: str Temperature Forecast (°C)): 绘制地理热力图支持点数据插值到规则网格 # 创建 0.5° 分辨率的目标网格 lon_grid, lat_grid np.meshgrid( np.arange(73, 136, 0.5), # 中国经度范围 np.arange(18, 54, 0.5) # 中国纬度范围 ) # 插值用 cubic 方法保持梯度连续性 temp_grid griddata( (lons, lats), temps, (lon_grid, lat_grid), methodcubic, fill_valuenp.nan ) fig plt.figure(figsize(12, 8)) ax plt.axes(projectionccrs.PlateCarree()) # 添加海岸线、国界 ax.add_feature(cfeature.COASTLINE, linewidth0.8) ax.add_feature(cfeature.BORDERS, linewidth0.6) # 绘制热力图设置 colormap 为 coolwarm蓝冷红热 im ax.pcolormesh(lon_grid, lat_grid, temp_grid, transformccrs.PlateCarree(), cmapcoolwarm, vmin0, vmax35) # 添加 colorbar cbar plt.colorbar(im, axax, shrink0.6, aspect20, pad0.02) cbar.set_label(Temperature (°C)) ax.set_title(title, fontsize14, pad20) plt.tight_layout() return fig # 示例模拟 10 个观测站数据 lats np.array([39.9, 31.2, 23.1, 45.8, 34.3, 29.6, 40.8, 38.0, 30.6, 22.3]) lons np.array([116.4, 121.5, 113.3, 126.6, 108.9, 106.6, 111.7, 103.8, 103.8, 114.1]) temps np.array([25.3, 28.7, 31.2, 19.8, 26.5, 27.9, 22.1, 24.6, 29.3, 32.0]) fig plot_temperature_heatmap(lats, lons, temps, Beijing Surroundings Forecast) plt.show()注意griddata的methodcubic比linear更能还原温度锋面如冷空气南下时的陡峭梯度但计算稍慢若数据点少于 20 个建议改用linear避免插值震荡。vmin/vmax设为 0~35 是针对中国夏季典型范围实际使用需根据数据分布动态计算vmin, vmax np.percentile(temps, [5, 95])。3.2 时间趋势对比图ARIMA vs LSTM vs 真实值三线同框不重叠预测结果必须和真实值放一起比但直接画三条线会糊成一团。本项目用seaborn.lineplotplt.fill_between突出置信区间并用不同线型区分模型# viz_time.py import seaborn as sns import matplotlib.pyplot as plt def plot_forecast_comparison(dates: list, true: np.ndarray, arima_pred: np.ndarray, lstm_pred: np.ndarray, arima_ci: tuple None, # (lower, upper) title: str Temperature Forecast Comparison): 绘制三线对比图ARIMA 用虚线LSTM 用实线真实值用圆点 plt.figure(figsize(14, 6)) # 真实值散点 灰色连线 plt.plot(dates, true, o-, colorgray, labelTrue Value, alpha0.7, markersize3) # ARIMA虚线 浅蓝填充置信区间 plt.plot(dates, arima_pred, --, colorsteelblue, labelARIMA Prediction, linewidth2) if arima_ci is not None: plt.fill_between(dates, arima_ci[0], arima_ci[1], colorsteelblue, alpha0.2, labelARIMA 95% CI) # LSTM实线 浅橙填充误差带标准差 plt.plot(dates, lstm_pred, -, colordarkorange, labelLSTM Prediction, linewidth2) # 假设 lstm_std 是预测标准差需在 predict 时用 MC Dropout 计算 # lstm_std calculate_lstm_uncertainty(lstm_model, X_test) # plt.fill_between(dates, lstm_pred - lstm_std, lstm_pred lstm_std, # colordarkorange, alpha0.2, labelLSTM Uncertainty) plt.title(title, fontsize14, pad20) plt.xlabel(Time) plt.ylabel(Temperature (°C)) plt.legend() plt.grid(True, alpha0.3) plt.xticks(rotation30) plt.tight_layout() return plt.gcf() # 示例数据生成 dates pd.date_range(2023-07-01, periods48, freqH) true np.sin(np.linspace(0, 4*np.pi, 48)) * 5 25 np.random.normal(0, 0.5, 48) arima_pred true np.random.normal(-0.3, 0.8, 48) # ARIMA 偏差略大但稳定 lstm_pred true np.random.normal(0.1, 0.4, 48) # LSTM 更准但偶有尖峰 fig plot_forecast_comparison(dates, true, arima_pred, lstm_pred) plt.show()关键细节arima_pred的置信区间arima_ci由arima_model.get_prediction(...).conf_int()直接获取这是 ARIMA 的原生优势LSTM 的不确定性需额外计算如 Monte Carlo Dropout项目文档里明确写了“若需 LSTM 不确定性请启用mc_dropoutTrue并运行predict_with_uncertainty()函数”不默认开启是为了降低新手理解门槛alpha0.7的真实值线条确保它不被预测线完全遮盖方便肉眼比对偏差方向。3.3 误差分析直方图MAE/RMSE/MAPE 三指标 残差分布拒绝只报一个数字只说“RMSE1.2°C”毫无意义。本项目强制输出三指标 残差直方图因为MAE 告诉你平均错多少RMSE 惩罚大误差MAPE 揭示相对误差是否随温度升高而减小比如 30°C 时错 2°C 比 5°C 时错 2°C 更可接受。# metrics.py import numpy as np import matplotlib.pyplot as plt def calculate_all_metrics(y_true: np.ndarray, y_pred: np.ndarray) - dict: 计算全部误差指标返回字典 errors y_true - y_pred mae np.mean(np.abs(errors)) rmse np.sqrt(np.mean(errors**2)) # MAPE避开 y_true0 的除零错误用 np.where 安全计算 mape np.mean(np.abs(errors / np.where(y_true 0, np.nan, y_true))) * 100 return { MAE: round(mae, 3), RMSE: round(rmse, 3), MAPE (%): round(mape, 2), residuals: errors } def plot_residual_histogram(errors: np.ndarray, title: str Residual Distribution): 绘制残差直方图叠加正态分布拟合曲线 plt.figure(figsize(10, 5)) # 直方图 plt.hist(errors, bins30, densityTrue, alpha0.7, colorskyblue, labelResiduals) # 拟合正态分布 mu, std np.mean(errors), np.std(errors) x np.linspace(mu - 4*std, mu 4*std, 100) p (1 / (std * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((x - mu) / std) ** 2) plt.plot(x, p, r-, lw2, labelfNormal Fit (μ{mu:.2f}, σ{std:.2f})) plt.title(title, fontsize14, pad20) plt.xlabel(Residual (°C)) plt.ylabel(Density) plt.legend() plt.grid(True, alpha0.3) plt.tight_layout() return plt.gcf() # 示例 y_true np.array([25.1, 26.3, 27.0, 25.8, 24.9, 25.5]) y_pred np.array([25.4, 26.0, 27.2, 25.6, 25.1, 25.7]) metrics calculate_all_metrics(y_true, y_pred) print(metrics) # {MAE: 0.233, RMSE: 0.258, MAPE (%): 0.89, residuals: array([-0.3, 0.3, -0.2, 0.2, -0.2, -0.2])} fig plot_residual_histogram(metrics[residuals]) plt.show()为什么 MAPE 要用np.where因为气象数据中temperature0是合法值冰点直接errors/y_true会触发RuntimeWarning: divide by zero encountered in true_divide且返回inf破坏均值计算。np.where(y_true 0, np.nan, y_true)将 0 替换为nannp.mean自动忽略nan这才是生产级写法。4. 避坑指南那些让期末答辩当场卡壳的 4 个血泪问题4.1 现象pip install cartopy报错 “proj not found” 或 “geos not found”原因Cartopy 依赖底层 C 库proj和geosWindows/macOS 上pip无法自动编译安装必须先装系统级依赖。解决Windows用conda install cartopy推荐 Anaconda/Minicondaconda 会自动解决二进制依赖macOSbrew install proj geos后再pip install cartopyLinuxUbuntusudo apt-get install libproj-dev libgeos-dev后pip install cartopy。血泪经验别信网上“下载 wheel 包手动装”的教程版本极易不匹配。conda 是唯一靠谱方案。4.2 现象LSTM 训练时loss一直不下降或验证集 loss 突然飙升原因两种典型情况① 数据未标准化温度20~35和 hour_sin-1~1量纲差异过大梯度爆炸②window_size设置不合理24 小时窗口配 1 小时间隔数据没问题但若数据是 6 小时一次则窗口内信息冗余模型学不到有效模式。解决必须用features.py中的StandardScaler且 scaler 必须在create_sliding_window内 fit不能在外部全局 fit检查df.timestamp.diff().min()确认时间间隔window_size应 ≈ 24 / 时间间隔小时。例如 3 小时间隔window_size8更合理。4.3 现象ARIMA 拟合时报错 “Non-stationary error” 或 “Invalid order”原因ARIMA 要求序列平稳而原始温度序列有明显趋势白天升温、夜间降温和季节性日循环order(1,1,1)中的d1表示一阶差分但若数据本身已去趋势如用df.temperature - df.temperature.rolling(24).mean()再差分就过度了。解决先画df.temperature.plot()和df.temperature.diff().plot()确认差分后是否变平稳均值/方差无趋势若原始序列已有滚动均值处理d应设为0尝试order(2,0,1)用adfuller检验from statsmodels.tsa.stattools import adfuller; adfuller(df.temperature)[1] 0.05才算平稳。4.4 现象地理热力图显示为空白或中国区域被裁掉一半原因Cartopy 的projection和set_extent不匹配。ccrs.PlateCarree()是经纬度投影但若ax.set_extent([73, 136, 18, 54])传入的是 WGS84 坐标却用ccrs.Mercator()投影就会错位。解决严格统一所有ax.add_feature()和ax.pcolormesh()的transform参数必须是ccrs.PlateCarree()ax.set_extent()的四个值是[lon_min, lon_max, lat_min, lat_max]顺序不能错若用ax.coastlines()仍不显示检查cartopy版本是否 ≥0.22旧版 Natural Earth 数据路径可能失效运行cartopy.config[data_dir]确认路径手动下载ne_10m_coastline.zip解压到该目录。5. 让你的大作业脱颖而出3 个答辩时老师最爱问的进阶技巧5.1 技巧一用shap解释 LSTM 预测——告诉老师“为什么模型说明天会降温”深度学习常被质疑为黑匣子。本项目预留了explain_lstm.py接口用 SHAPSHapley Additive exPlanations量化每个特征温度、湿度、风速…对单次预测的贡献。这不是炫技而是让你在答辩时能指着图说“看模型判断降温的主要依据是风速突增和湿度上升这和气象学中冷锋过境特征一致”。# explain_lstm.py需额外 pip install shap import shap from tensorflow.keras.models import load_model def explain_lstm_prediction(model_path: str, X_sample: np.ndarray, feature_names: list None): 用 KernelExplainer 解释单样本预测 model load_model(model_path) # 构造 background dataset用训练集的均值/中位数采样 background X_sample.mean(axis0).reshape(1, *X_sample.shape[1:]) # 初始化 explainer explainer shap.KernelExplainer( lambda x: model.predict(x).flatten(), background ) # 计算 SHAP 值 shap_values explainer.shap_values(X_sample[0:1]) # 可视化按时间步聚合各特征重要性 if feature_names is None: feature_names [ffeat_{i} for i in range(X_sample.shape[-1])] shap.summary_plot(shap_values, X_sample[0:1], feature_namesfeature_names, plot_typebar, showFalse) plt.title(Feature Importance for This Prediction) plt.tight_layout() plt.show() return shap_values # 使用示例需先保存训练好的模型 # model.save(models/lstm_best.h5) # explain_lstm_prediction(models/lstm_best.h5, X_test[0:1], # [temp, humid, press, wind_spd, wind_dir, # hour_sin, hour_cos, day_sin, day_cos])为什么用 KernelExplainer 而非 DeepExplainer因为 DeepExplainer 要求模型是 Keras Sequential 且无自定义层而本项目的 LSTM 含 DropoutDeepExplainer 会报错。KernelExplainer 更通用虽慢一点但保证能跑通。background用均值而非全量训练集是为了控制计算量——期末作业不需要极致精度需要的是“能讲清楚”。5.2 技巧二添加“预测可信度评分”——用残差历史分布动态校准置信度老师常问“你预测 28.5°C那 28.0°C 和 29.0°C 的可能性一样吗” 本项目在predict.py中实现了一个轻量级可信度模块基于过去 7 天预测残差的标准差动态调整当前预测的误差带宽度。残差波动大如台风天误差带自动加宽残差平稳如连续晴天误差带收窄。# predict.py def get_dynamic_uncertainty(y_true_history: np.ndarray, y_pred_history: np.ndarray, current_pred: float, window: int 7) - tuple: 计算动态不确定性用最近 window 天的残差 std 作为当前误差带半径 返回 (lower_bound, upper_bound) if len(y_true_history) window: window len(y_true_history) # 不足7天就用全部历史 residuals y_true_history[-window:] - y_pred_history[-window:] std_recent np.std(residuals) # 经验系数std_recent * 1.96 ≈ 95% 置信区间但气象中常用 2σ margin std_recent * 2.0 return current_pred - margin, current_pred margin # 示例假设你有过去7天的真实值和预测值 y_true_hist np.array([25.2, 26.1, 27.3, 26.8, 25.9, 25.1, 24.7]) y_pred_hist np.array([25.4, 26.0, 27.2, 26.6, 25.8, 25.3, 24.9]) current_pred 25.5 low, high get_dynamic_uncertainty(y_true_hist, y_pred_hist, current_pred) print(fPredicted: {current_pred}°C, Dynamic Range: [{low:.2f}, {high:.2f}]°C) # Output: Predicted: 25.5°C, Dynamic Range: [24.82, 26.18]°C这个技巧的价值它把“模型能力”和“数据质量”解耦了。同样是 RMSE0.8°C如果过去一周残差标准差是 0.3°C说明模型很稳如果残差标准差是 1.2°C说明近期天气突变模型泛化变差。你在答辩时可以说“老师这个 25.5°C 预测的误差带是 ±0.68°C因为过去一周模型在类似天气下的平均误差只有 0.34°C所以这次预测我们更有信心。”5.3 技巧三一键生成答辩 PDF 报告——用Jinja2weasyprint自动渲染别再手调 Word 格式项目根目录下report_template.html定义了报告结构generate_report.py自动填入数据概览图、双模型误差对比表、地理热力图、时间趋势图、SHAP 解释图。执行python generate_report.py即生成 PDF连页眉页脚、章节编号都自动生成。# generate_report.py from jinja2 import Environment, FileSystemLoader from weasyprint import HTML import json import matplotlib matplotlib.use(Agg) # 避免 GUI backend 报错 import matplotlib.pyplot as plt def create_report_data(): 收集所有图表和指标生成 report_data.json # 1. 加载数据 df load_from_csv(data/beijing_2023.csv) # 2. 计算指标 metrics_arima calculate_all_metrics(...) metrics_lstm calculate_all_metrics(...) # 3. 保存 p a hrefhttps://download.csdn.net/download/ma_nong33/89482759 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
返回列表