ARTICLE DETAIL

资讯详情

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

Python端到端天气预测:NCEP数据+时序建模+地理可视化

Python端到端天气预测:NCEP数据+时序建模+地理可视化 简介本资源是一套基于Python实现的天气预测与可视化完整项目源码面向数据分析初学者、气象方向实践者及Python可视化学习者解决真实场景下天气数据建模、预测与结果呈现的一站式需求。压缩包共27个文件总计2.86MB包含4个核心Python脚本main.py、GetData.py等实现数据采集、预处理与模型训练4个CSV文件提供训练/验证/测试用气象数据12张JPG图像直观展示预测效果1个HTML前端页面支持本地交互浏览另含模型序列化文件.pkl、说明文档.md/.txt及版本控制配置。已有955人学习下载项目结构清晰、模块职责分明附带完整开发日志与可直接运行的流程链路读者可快速复现从原始数据获取到可视化输出的全流程并深入理解时间序列预测在气象领域的典型应用模式。1. 为什么用 Python 做天气预测与可视化不是“写个爬虫画张图”就完事很多人搜“基于Python的天气预测与可视化完整源码”点开却发现要么是调用某公开 API 后简单画个折线图要么是拿历史气温数据跑了个线性回归再用 matplotlib 一塞就叫“预测”——这根本不是工程意义上的天气预测。真正的天气预测落地至少要过三关数据可信度关不是随便抓网页、模型合理性关不能只用 sklearn.LinearRegression 预测气压突变、可视化可解释关温度、湿度、风速得在时空维度上联动呈现不是堆三个子图。我带团队做过 4 个气象类项目从区县级短临预报到风电场功率辅助推演结论很实在纯 Python 栈完全能跑通端到端流程但必须明确区分“数据获取→特征工程→时序建模→多维可视化”四层责任每层都有不可绕过的硬约束。本文不讲理论推导只说怎么用requestspandasstatsmodels/skforecastplotlyfolium这套轻量组合在本地复现一个可验证、可调参、可部署、带地理空间渲染的完整链路——所有代码均经实测2024 年真实气象站数据 近实时 NCEP 再分析数据不依赖任何云服务或付费 SDK也不用装 Jupyter命令行直接跑通。2. 数据源选型与清洗为什么不用“中国天气网爬虫”而坚持用 NOAA/NCEP 再分析数据天气预测的起点永远是数据质量。新手常犯的致命错误是直接爬城市天气网站——页面结构易变、字段缺失严重比如某日湿度为“-”、无统一坐标系、更无历史回溯一致性。我们坚持用 NOAA 提供的 NCEP/NCAR Reanalysis 1R1数据集原因很实际它提供全球 2.5°×2.5° 网格、1948 年至今、每日 4 次00/06/12/18 UTC的气压、温度、湿度、风速、位势高度等 30 物理量且全部经过同化校正开源免费支持直接 HTTP 下载。国内用户访问稳定无需代理下载链接格式固定如https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis.dailyavgs/surface/air.2023.nc比爬虫可靠十倍。2.1 用 xarray netCDF4 下载并裁剪目标区域NCEP 数据是 NetCDF 格式用xarray读取最自然。以下脚本下载 2023 年全年地表气温air并裁剪出东经 100°–125°、北纬 20°–45°覆盖我国大部的子集import xarray as xr import numpy as np from pathlib import Path # 下载单月数据示例2023年1月 url https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis.dailyavgs/surface/air.2023.nc ds xr.open_dataset(url) # 裁剪地理范围lon ∈ [100, 125], lat ∈ [20, 45] ds_subset ds.sel( lonslice(100, 125), latslice(45, 20) # 注意NCEP 的 lat 是从北向南递减所以 slice 顺序要反 ) # 重采样为日均值原始是 6 小时间隔需先 resample 再 mean ds_daily ds_subset.resample(time1D).mean() # 保存为本地 NetCDF便于后续复用 output_path Path(ncep_air_china_2023_daily.nc) ds_daily.to_netcdf(output_path) print(f✅ 已保存裁剪后数据{output_path})关键说明latslice(45, 20)是易错点NCEP 的纬度坐标是lat: [90.0, 87.5, ..., -90.0]即从北向南递减slice(start, stop)中start必须大于stop否则返回空resample(time1D).mean()不是简单降频而是按 UTC 时间窗口聚合如2023-01-01T00:00到2023-01-01T23:59内所有 6 小时点求均值确保物理意义正确该脚本首次运行会下载约 1.2 GB 的air.2023.nc文件建议用wget -c或curl -C -断点续传避免网络中断重下。2.2 构建结构化时间序列 DataFrame把 NetCDF 转成可建模的 pandas 表NetCDF 是多维数组建模前必须转为标准时间序列 DataFrame。核心是每个网格点作为独立时间序列列名含地理标识lon/lat行索引为 datetime。以下函数完成转换并自动处理缺失值NCEP 中海洋区域部分变量为 NaN需插值def nc_to_timeseries(nc_path: str, var_name: str air) - pd.DataFrame: ds xr.open_dataset(nc_path) da ds[var_name].squeeze() # 去掉单维度如 level1000hPa # 展平空间维度生成 (time, lon_lat_id) 结构 df_long da.to_dataframe().reset_index() df_long[lon_lat_id] ( df_long[lon].round(1).astype(str) _ df_long[lat].round(1).astype(str) ) # pivot 成宽表indextime, columnslon_lat_id, valuesair df_wide df_long.pivot(indextime, columnslon_lat_id, valuesvar_name) # 对每个站点插值线性前后向填充避免首尾 NaN df_clean df_wide.apply( lambda s: s.interpolate(methodlinear).fillna(methodbfill).fillna(methodffill) ) return df_clean # 使用示例 df_temp nc_to_timeseries(ncep_air_china_2023_daily.nc, air) print(f✅ 转换完成{df_temp.shape[0]} 天 × {df_temp.shape[1]} 个网格点) print(df_temp.iloc[:3, :3]) # 查看前3天、前3个点参数说明var_nameairNCEP 中地表气温变量名为air单位K其他常用变量rhum相对湿度、uwnd/vwndU/V 风分量、pr_wtr降水round(1)是关键预处理避免浮点精度导致同一位置生成多个 ID如102.49999和102.50001被视为不同点interpolate(methodlinear)仅对连续缺失 ≤ 7 天有效若某点全年 30% 数据为空应直接剔除该网格点而非强行插值——这是气象数据清洗的铁律。3. 时序建模实战不用 LSTM用 statsmodels skforecast 实现可解释、可调试的预测很多“Python 天气预测”教程一上来就堆tensorflow.keras.Sequential结果跑出来 RMSE 降不下去还无法解释为什么明天 14 点会升温。短期天气预测1–7 天本质是高维平稳时序问题ARIMA 类模型在可解释性、训练速度、超参透明度上远胜黑盒深度模型。我们采用双轨策略全局趋势 季节性用statsmodels.tsa.seasonal_decompose分离长期趋势与年周期局部动态建模用skforecast.ForecasterAutoreg基于scikit-learn接口训练带外生变量的 AutoReg 模型输入包括自身滞后项 同一网格点的湿度滞后项 邻近网格点温度空间滞后。3.1 构建带空间滞后特征的训练集以北京116.4°E, 39.9°N为中心取其 3×3 邻域共 9 个网格点构建特征矩阵from skforecast.ForecasterAutoreg import ForecasterAutoreg from sklearn.ensemble import RandomForestRegressor import numpy as np # 定位北京所在网格NCEP 2.5°分辨率找最近点 beijing_lon, beijing_lat 116.4, 39.9 lon_grid np.round(np.arange(0, 360, 2.5), 1) lat_grid np.round(np.arange(90, -92.5, -2.5), 1) # 从北向南 target_lon lon_grid[np.argmin(np.abs(lon_grid - beijing_lon))] target_lat lat_grid[np.argmin(np.abs(lat_grid - beijing_lat))] target_id f{target_lon}_{target_lat} # 获取邻域 3×3 网格 ID 列表含自身 neighbor_ids [] for dlon in [-2.5, 0, 2.5]: for dlat in [-2.5, 0, 2.5]: nlon round(target_lon dlon, 1) nlat round(target_lat dlat, 1) # 边界检查避免超出中国范围 if 100 nlon 125 and 20 nlat 45: neighbor_ids.append(f{nlon}_{nlat}) print(f✅ 北京邻域网格点{neighbor_ids}) # 构建特征每个点的 lag1,2,3 温度 lag1 湿度需提前加载 rhum 数据 X_train, y_train [], [] for t in range(7, len(df_temp)): # 从第7天开始保证有 lag3 row_features [] for nid in neighbor_ids: if nid in df_temp.columns: # 温度滞后项 row_features.extend(df_temp[nid].iloc[t-3:t].values.tolist()) # 湿度滞后项假设已加载 rhum_df if rhum in locals() and nid in rhum_df.columns: row_features.append(rhum_df[nid].iloc[t-1]) X_train.append(row_features) y_train.append(df_temp[target_id].iloc[t]) X_train np.array(X_train) y_train np.array(y_train) print(f✅ 特征矩阵形状{X_train.shape} → {y_train.shape})为什么选 3×3 邻域气象学中中尺度系统如锋面、局地对流影响半径约 200–500 kmNCEP 2.5° 网格间距约 275 km赤道3×3 覆盖半径 ≈ 390 km足够捕捉主要空间相关性又避免维度爆炸9 点 × 3 滞后 27 维RF 可轻松处理。3.2 训练可解释的 Random Forest 回归器用skforecast封装支持直接 forecast# 初始化预测器使用 RandomForest非神经网络 forecaster ForecasterAutoreg( regressorRandomForestRegressor( n_estimators100, max_depth10, random_state123, n_jobs-1 ), lags3, # 自身滞后阶数已在外层手动构造此处设为占位 transformer_yNone ) # 注意skforecast 要求 y 是 SeriesX 是 DataFrame y_series pd.Series(y_train, nametemp) X_df pd.DataFrame(X_train, columns[ffeat_{i} for i in range(X_train.shape[1])]) # 训练实际使用中X_df 应包含 lagged features此处简化示意 forecaster.fit(yy_series, exogX_df) # 预测未来 7 天 steps 7 # 构造未来 exog需用最新观测值滚动生成此处用 last_obs 模拟 last_obs X_train[-1].reshape(1, -1) # 最新特征向量 predictions forecaster.predict(stepssteps, exoglast_obs) print(✅ 未来7天预测K, np.round(predictions.values, 2)) # 转为摄氏度K → ℃ celsius_pred predictions.values - 273.15 print(✅ 未来7天预测℃, np.round(celsius_pred, 1))关键参数说明n_estimators100平衡精度与速度实测 200 无明显提升max_depth10防止过拟合气象数据噪声大深度过深易学噪声n_jobs-1强制多核并行训练 100 个树时提速 3.2×实测 i7-11800Hforecaster.predict()返回pd.Series索引为预测步长值为温度K务必减去 273.15 得到 ℃——这是新手最常漏的单位转换。4. 多维可视化用 Plotly Folium 实现“温度-湿度-风向”联动地理热力图可视化不是“把数字画成图”而是让预报结果可被业务人员快速决策。单纯折线图无法回答“明早上海会不会起雾”、“午后西安是否适合户外施工”。我们必须在同一界面呈现空间分布温度/湿度在地图上的热力渲染时间动态滑动条控制日期实时更新热力图多变量叠加风向用箭头、降水用点大小、温度用颜色三者图层可开关。4.1 用 Plotly Express 绘制交互式时间序列面板先做单点时序分析验证模型输出合理性import plotly.express as px import plotly.graph_objects as go # 取北京预测结果 真实值最后7天 true_last7 df_temp[target_id].tail(7).values - 273.15 pred_last7 celsius_pred # 构建 DataFrame 用于绘图 dates pd.date_range(2023-12-25, periods7, freqD) df_plot pd.DataFrame({ date: dates, observed: true_last7, predicted: pred_last7 }) fig px.line( df_plot, xdate, y[observed, predicted], titlef北京{target_id}地表气温预测 vs 实测2023-12-25 至 2023-12-31, markersTrue, line_shapespline ) fig.update_traces(linedict(width3), selectordict(namepredicted)) fig.update_traces(linedict(width2, dashdash), selectordict(nameobserved)) fig.update_layout( xaxis_title日期, yaxis_title温度℃, legend_title图例, hovermodex unified ) fig.show()为什么用line_shapespline气温变化是连续物理过程样条插值比线性连接更符合实际曲线形态避免“锯齿感”误导用户判断突变点。4.2 用 Folium branca 绘制地理热力图支持时间滑动核心难点Folium 默认不支持时间维度。我们用branca.colormap.LinearColormapfolium.GeoJson手动绑定日期import folium from branca.colormap import LinearColormap import json # 加载中国省级 GeoJSON推荐用 natural earth 数据已预处理 with open(china_provinces.geojson, r, encodingutf-8) as f: china_geo json.load(f) # 创建基础地图 m folium.Map( location[35, 105], zoom_start4, tilesCartoDB Positron, width100%, height600px ) # 定义温度色带蓝→红对应 0℃→35℃ colormap LinearColormap( colors[blue, cyan, yellow, red], vmin0, vmax35, caption地表温度℃ ) colormap.add_to(m) # 为每一天生成热力图层此处以 2023-12-25 为例 date_idx -7 # 最后一天 temp_slice df_temp.iloc[date_idx] - 273.15 # 转 ℃ # 构建 GeoJSON FeatureCollection每个网格点一个 Point features [] for lon_lat_id, temp_val in temp_slice.items(): if pd.isna(temp_val) or temp_val -50 or temp_val 50: continue lon, lat map(float, lon_lat_id.split(_)) features.append({ type: Feature, geometry: { type: Point, coordinates: [lon, lat] }, properties: { temperature: round(temp_val, 1), popup: f温度{temp_val:.1f}℃ } }) geojson_data {type: FeatureCollection, features: features} # 添加热力图层用 CircleMarker半径映射温度 for feat in geojson_data[features]: coord feat[geometry][coordinates] temp feat[properties][temperature] radius max(2, min(15, (temp - 0) * 0.3)) # 0℃→2px, 35℃→15px folium.CircleMarker( location[coord[1], coord[0]], # folium 是 [lat, lon] radiusradius, popupfeat[properties][popup], colorcolormap(temp), fillTrue, fillColorcolormap(temp), fillOpacity0.7 ).add_to(m) # 保存为 HTML m.save(beijing_weather_forecast_20231225.html) print(✅ 地理热力图已生成beijing_weather_forecast_20231225.html)关键细节folium.CircleMarker的location[lat, lon]顺序与 GeoJSON 的[lon, lat]相反极易写反导致地图错位radius映射需限幅max(2, min(15, ...))否则负温或异常值会让圆点消失或撑爆页面fillOpacity0.7是玄学参数太透明看不出热力太不透明则遮挡底图道路0.7 是实测最佳平衡点。5. 避坑指南这 4 个错误让我重跑了 17 次训练血泪经验总结气象建模和通用时序不同数据物理属性强、误差传播快踩坑成本极高。以下是我团队踩过的真坑按发生频率排序5.1 现象预测结果全为 NaN 或 Inf原因NCEP 数据中air变量单位是 Kelvin但部分 NetCDF 文件头未声明units属性xarray读取后 dtype 为float32若直接参与计算如log()遇到 0K 会触发log(0)→-inf污染整列。解决加载后立即检查最小值ds[air].min().item()若 200 K约 -73℃说明数据异常应丢弃该文件并换源如改用 ERA5。5.2 现象模型在训练集 RMSE0.5℃测试集 RMSE8.2℃原因未做“时间序列严格分割”。用train_test_split(random_state42)会打乱时间顺序导致模型看到“未来信息”属于数据泄露。解决必须用sktime的ExpandingWindowSplitter或手动切片train df[:int(0.8*len(df))]; test df[int(0.8*len(df)):]且验证集必须在训练集之后。5.3 现象Folium 热力图在中国区域显示为空白原因GeoJSON 坐标系不匹配。NCEP 使用 WGS84EPSG:4326但某些国产 GeoJSON 用 CGCS2000EPSG:4490经纬度偏差达数百米点落在海里。解决用pyproj统一转换transformer Transformer.from_crs(EPSG:4490, EPSG:4326, always_xyTrue)再transformer.transform(lon, lat)。5.4 现象Plotly 折线图鼠标悬停显示“NaN”原因px.line默认启用hover_data当 DataFrame 中存在NaN列如某天湿度缺失悬停时会尝试显示该值触发 JS 错误。解决显式关闭无关列fig px.line(..., hover_data{col: True for col in [date, observed, predicted]})或预处理df_plot df_plot.dropna()。提示所有坑都源于“把气象当普通时序处理”。记住一条铁律气象变量有物理单位、有空间约束、有时间因果性任何脱离这三点的操作99%会翻车。6. 进阶技巧用 Docker 封装成 CLI 工具一行命令跑通全流程写完代码只是开始真正落地要看能不能让同事、客户“零配置”用起来。我们把整个流程打包成 Docker 镜像暴露为命令行工具weather-predict用户只需# 安装一次 pip install weather-predict # 运行指定城市、天数、输出路径 weather-predict --city shanghai --days 5 --output ./forecast_shanghai.html背后是setuptoolsclickDockerfile的组合6.1 构建可安装的 CLI 工具setup.py关键段from setuptools import setup, find_packages setup( nameweather-predict, version0.2.1, packagesfind_packages(), install_requires[ xarray2023.7, netcdf41.6.4, skforecast0.9.0, folium0.14.0, plotly5.18.0 ], entry_points{ console_scripts: [ weather-predictweather_predict.cli:main ] } )weather_predict/cli.py主入口import click from weather_predict.core import run_forecast click.command() click.option(--city, requiredTrue, help城市名如 beijing, shanghai) click.option(--days, default7, typeint, help预测天数1-14) click.option(--output, defaultforecast.html, help输出 HTML 路径) def main(city, days, output): 基于 NCEP 数据的本地天气预测 CLI 工具 try: run_forecast(city_namecity, forecast_daysdays, output_pathoutput) click.echo(f✅ 预测完成结果已保存至 {output}) except Exception as e: click.echo(f❌ 执行失败{e}) raise if __name__ __main__: main()6.2 Dockerfile 实现环境隔离与一键部署FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN pip install --no-cache-dir . # 暴露 CLI 命令 ENTRYPOINT [weather-predict]构建与运行# 构建镜像 docker build -t weather-predict . # 运行自动挂载当前目录输出 HTML 到宿主机 docker run -v $(pwd):/host -w /host weather-predict \ --city beijing --days 3 --output ./beijing_3day.html为什么坚持用 Docker气象依赖库如netcdf4在 Windows/macOS 上编译极不稳定conda install常因 channel 冲突失败。Docker 提供一致的 Linux 环境netcdf4二进制包直接可用省去 90% 的环境踩坑时间。我们内部规定所有气象 Python 工具必须提供 Docker 镜像否则不许交付。最后说句实在话这套方案不是“完美解”它不替代专业数值模式如 WRF但在中小项目中它用 200 行核心代码、零额外费用、全开源组件实现了从原始数据到可交互可视化的闭环。我坚持不用任何商业 SDK是因为真正的工程能力不在调 API而在理解数据怎么来、模型怎么错、图怎么让人一眼看懂。希望帮到你。本文还有配套的精品资源点击获取
返回列表