)
天氣模型排名指的是把不同數(shù)值天氣預報模型放到同一批歷史天氣場景里用實際觀測結果檢驗它們的預報誤差最后按誤差大小排出先后。這個做法在氣象服務、能源調(diào)度、農(nóng)業(yè)決策、物流規(guī)劃和量化交易場景里非常有用業(yè)務方不需要關心模型內(nèi)部有多少物理過程只需要知道過去一段時間里哪個模型的預報更接近真實天氣從而決定后續(xù)引用哪條預報源。標題里的 Show HN: Ranking weather models by how their forecasts turned out 對應的正是這樣一個系統(tǒng)把“事后驗證”做成排行榜讓模型質(zhì)量可比較、可追蹤、可復盤。下面會圍繞如何搭建一套最小可用的天氣模型排名系統(tǒng)展開。主線是從數(shù)據(jù)準備、對齊邏輯、評分指標到 Python 實現(xiàn)、異常排查和工程化擴展。所有代碼和數(shù)據(jù)格式都是演示結構用于說明思路真實項目接入任何模型輸出、觀測站點或數(shù)據(jù)文件時需要按實際字段和授權調(diào)整。1. 天氣預報模型排名要解決什么問題1.1 什么是天氣模型排名為什么不能只信模型說明數(shù)值天氣預報模型會用物理方程描述大氣運動再通過超級計算機求解得到未來若干小時甚至十幾天的氣象要素預報。不同模型在物理方案、分辨率、資料同化方式上差別很大所以同一天、同一個地點、同一個變量的預報結果經(jīng)常不一致。使用者最關心的問題是哪個模型更可信天氣模型排名就是給這個問題提供一個可量化的答案。它不評價模型內(nèi)部的物理設計只評價“歷史輸出和真實觀測的差距”。通俗地說一次預報就是一次考試觀測就是標準答案。模型在大量歷史樣本上考出來的平均得分就是它的排名依據(jù)。技術定義可以寫成給定一組模型、一組起報時間、一組站點或格點、一組氣象變量和一組驗證時間逐個比較預報值與觀測值計算統(tǒng)計算法指標再按指標排序。不能只信模型說明原因主要有三點。第一模型宣傳材料通常強調(diào)分辨率、同化系統(tǒng)等能力卻不一定提供與業(yè)務場景匹配的獨立驗證。第二模型在不同地區(qū)、季節(jié)和天氣類型下的表現(xiàn)可能差異很大一個總體分數(shù)不能覆蓋所有場景。第三模型版本會更新排名會變化只有持續(xù)驗證才能跟蹤真實變化。一個榜單系統(tǒng)的價值不是給出一次結論而是讓結論可以被持續(xù)更新和質(zhì)疑。1.2 排名系統(tǒng)的核心鏈路預報、觀測、對齊、評分一套最小可用的天氣模型排名系統(tǒng)核心鏈路只有五步。第一步收集多個模型在過去一段時間的預報。注意“預報”不是一句話而是結構化數(shù)據(jù)至少要包含起報時間、有效時間、站點或經(jīng)緯度、氣象變量和預報值。第二步收集同時段的觀測數(shù)據(jù)。觀測可以是氣象站、網(wǎng)格分析場或衛(wèi)星反演產(chǎn)品但要和預報使用同一套空間和時間口徑。第三步對齊。這一步最容易被忽略卻最影響結果。兩個模型必須比較同一個有效時間、同一個地點、同一個變量的預報才公平。第四步計算誤差指標。比如絕對誤差、均方根誤差、偏差、降水命中率等。第五步按模型聚合指標并生成排行榜再展示給業(yè)務方或下游系統(tǒng)。后面所有代碼和配置都圍繞這條鏈路展開。很多人一開始就把精力放在“把模型跑起來”或“把畫圖做漂亮”上其實排名系統(tǒng)最核心的工作量在前三步數(shù)據(jù)怎么來、字段怎么對應、口徑怎么統(tǒng)一。數(shù)據(jù)和字段錯了后面的指標再專業(yè)也沒有意義。1.3 適用場景與讀者定位這套方案適合以下幾類讀者。第一類是氣象數(shù)據(jù)產(chǎn)品開發(fā)人員需要為業(yè)務方提供模型質(zhì)量看板。第二類是數(shù)據(jù)工程師正在對接氣象預報和觀測數(shù)據(jù)需要設計統(tǒng)一驗證流程。第三類是研究人員想快速驗證新模型或新參數(shù)方案是否優(yōu)于既有模型。第四類是后端開發(fā)人員需要把歷史驗證結果做成排行榜頁面或 API。本文的例子使用 Python 和 pandas不依賴專業(yè)氣象軟件。這樣做的目的是先把排名邏輯講清楚再讓讀者根據(jù)自己的數(shù)據(jù)格式替換加載層。如果你已經(jīng)在使用 xarray、cfgrib 或 GRIB2 文件只需要替換數(shù)據(jù)加載部分評分和排名邏輯可以原樣復用。2. 數(shù)據(jù)準備歷史預報和觀測數(shù)據(jù)如何配對2.1 先統(tǒng)一時間口徑預報發(fā)布時間與有效時間氣象數(shù)據(jù)里至少有三個時間概念起報時間issue_time、預報時效lead_hour和有效時間valid_time。起報時間是模型開始計算的時刻通常是 00 時或 12 時預報時效是預測未來多少個小時后的大氣狀態(tài)有效時間是預報值對應的真實時刻它等于起報時間加上預報時效。排名系統(tǒng)比較的是“對同一個真實時刻的預報誰更準”所以最終必須以 valid_time 作為對齊鍵。如果兩個模型起報時間相同但一個預報 24 小時另一個預報 48 小時它們對應的 valid_time 完全不同不能放在一起比較。同樣如果兩個模型 valid_time 相同但起報時間不同它們利用的初始資料可能不同可以比較但要注意時效差異。下面是一個典型字段示例model,issue_time,valid_time,lead_hour GFS,2025-01-01T00:00:00Z,2025-01-02T00:00:00Z,24 ECMWF,2025-01-01T00:00:00Z,2025-01-02T00:00:00Z,24在讀取數(shù)據(jù)時所有時間字段都應該轉成帶時區(qū)的 UTC 時間。不要使用本地時間字符串做 join因為不同數(shù)據(jù)源可能使用不同時區(qū)容易出現(xiàn)“看起來相同實際差 8 小時”的問題。2.2 先統(tǒng)一空間口徑格點與站點如何對齊天氣預報模型通常輸出網(wǎng)格數(shù)據(jù)比如 0.25 度、0.5 度分辨率的經(jīng)緯度格點。氣象觀測站是離散的點可能落在兩個格點之間。為了比較需要把模型格點插值到觀測站點位置或者把觀測值匹配到網(wǎng)格上。最簡單可靠的做法是使用現(xiàn)成插值工具將模型值插值到站點然后把站點的經(jīng)緯度轉換成唯一的 station_id。在演示項目中為了聚焦排名邏輯可以先把數(shù)據(jù)整理成站點的 long 表。每條記錄都包含 model、valid_time、station_id、variable、value。對齊時forecast 和 observation 都通過 valid_time、station_id、variable 三個字段連接。這樣能避免插值實現(xiàn)干擾主流程。如果數(shù)據(jù)源來自網(wǎng)格加載時要注意經(jīng)緯度表示方式和單位。部分數(shù)據(jù)用整數(shù)經(jīng)緯度部分用浮點經(jīng)緯度有的用 lon 表示 -180 到 180有的用 0 到 360。做空間對齊前需要先統(tǒng)一經(jīng)緯度范圍。2.3 用最小 CSV 結構承載評分數(shù)據(jù)為了能直接運行后面的 Python 代碼這里定義兩個最小 CSV 文件。第一個是歷史預報文件 forecasts.csvmodel,issue_time,valid_time,station_id,variable,value GFS,2025-01-01T00:00:00Z,2025-01-02T00:00:00Z,S001,temperature_2m,4.2 ECMWF,2025-01-01T00:00:00Z,2025-01-02T00:00:00Z,S001,temperature_2m,5.0 ICON,2025-01-01T00:00:00Z,2025-01-02T00:00:00Z,S001,temperature_2m,3.9第二個是觀測文件 observations.csvvalid_time,station_id,variable,value 2025-01-02T00:00:00Z,S001,temperature_2m,3.5這兩個文件都很小但已經(jīng)包含了排名系統(tǒng)需要的所有核心字段。變量名建議統(tǒng)一使用機器可讀的小寫字符例如 temperature_2m、wind_speed_10m、precipitation_24h。如果值有單位最好在表里增加 unit 字段或者先在預處理階段統(tǒng)一單位避免不同模型輸出攝氏度和華氏度造成誤差計算錯誤。注意觀測文件的粒度是“真實時刻的客觀值”而預報文件還需要保留 model 和 issue_time。合并時只保留 valid_time 相同的記錄否則樣本對不齊排名會失真。2.4 數(shù)據(jù)質(zhì)量檢查清單在開始寫評分代碼前建議先對數(shù)據(jù)做一輪質(zhì)量檢查。下面這個清單可以直接復用檢查項檢查方式通過標準時間字段格式打印每個來源的 unique 時間樣例全部使用 ISO8601 且?guī)?Z 時區(qū)valid_time 覆蓋范圍檢查 min/max預報和觀測覆蓋同一時間段station_id 是否一致計算集合差預報和觀測站點集合一致variable 是否一致計算集合差變量名完全匹配是否存在重復記錄按 key 分組統(tǒng)計每個 key 只有一條記錄值是否缺測查看 value 為 NaN 或特殊值缺測數(shù)量低于閾值且被標記這個檢查可以用 pandas 在預處理階段自動完成。不要省略這些步驟因為時間或空間對齊一旦出錯排名結果會出現(xiàn)系統(tǒng)性偏差而且肉眼很難發(fā)現(xiàn)。3. 用 Python 實現(xiàn)最小可運行的模型評分與排名流程3.1 項目結構和依賴在本地目錄創(chuàng)建一個最小項目weather-leaderboard/ ├── data/ │ ├── forecasts.csv │ └── observations.csv ├── leaderboard.py ├── requirements.txt └── run.sh依賴只需要 pandas 和 numpy畫圖可以后面再加。安裝命令pip install pandas numpy matplotlibrequirements.txt 可以寫成pandas2.0 numpy1.24 matplotlib3.7這一步的目的是讓示例可以快速運行。真實項目中如果使用 GRIB2 或 NetCDF需要額外引入 xarray、cfgrib 或 h5netcdf但核心評分邏輯不受影響。3.2 加載和對齊數(shù)據(jù)在 leaderboard.py 中先讀取兩個 CSV 文件import pandas as pd def load_data(forecast_path, observation_path): forecasts pd.read_csv(forecast_path, parse_dates[issue_time, valid_time]) observations pd.read_csv(observation_path, parse_dates[valid_time]) return forecasts, observations然后寫對齊函數(shù)def align_forecast_observation(forecasts, observations): merged pd.merge( forecasts, observations, on[valid_time, station_id, variable], suffixes(_fc, _obs), howinner ) merged[error] merged[value_fc] - merged[value_obs] return merged這里使用內(nèi)連接強制保留預報和觀測都存在的樣本。如果某個模型出現(xiàn)了數(shù)據(jù)缺口它的樣本量會比其他模型小這時排名結果需要謹慎解讀。使用 suffixes 區(qū)分預報值和觀測值error 表示預報減觀測的差值正偏差表示預報偏高。對齊之后需要檢查一下樣本數(shù)量def sample_summary(df): return df.groupby(model).size().reset_index(namesample_size)3.3 計算誤差指標接下來定義三個連續(xù)變量誤差指標平均絕對誤差 MAE、均方根誤差 RMSE、平均偏差 Bias。import numpy as np def mae(obs, fct): obs np.asarray(obs, dtypefloat) fct np.asarray(fct, dtypefloat) return float(np.mean(np.abs(fct - obs))) def rmse(obs, fct): obs np.asarray(obs, dtypefloat) fct np.asarray(fct, dtypefloat) return float(np.sqrt(np.mean((fct - obs) ** 2))) def bias(obs, fct): obs np.asarray(obs, dtypefloat) fct np.asarray(fct, dtypefloat) return float(np.mean(fct - obs))這三個函數(shù)都比較簡單。MAE 直觀反映平均誤差大小RMSE 對大誤差更敏感因為平方項放大了離群值的影響B(tài)ias 表示系統(tǒng)性誤差正值偏暖或偏強負值偏冷或偏弱。排名時通常以 MAE 或 RMSE 為主指標Bias 作為輔助參考。3.4 按模型生成排行榜核心邏輯是按 model 分組對每組樣本計算指標再排序def rank_models(merged, metricmae): records [] for model, group in merged.groupby(model): obs group[value_obs] fct group[value_fc] records.append({ model: model, sample_size: len(group), mae: mae(obs, fct), rmse: rmse(obs, fct), bias: bias(obs, fct), }) result pd.DataFrame(records) # 這里按誤差指標從小到大排名bias 不作為主排名指標 result[rank] result[metric].rank(methodmin, ascendingTrue).astype(int) result result.sort_values([rank, model]).reset_index(dropTrue) return result這段代碼先遍歷每個模型再生成 DataFrame。metric 參數(shù)可以切換排名用的指標默認 mae。rank 列使用 pandas 的 rank 方法相同誤差時并列名次。這里要注意樣本量暫時只作為展示不參與排序。如果兩個模型樣本量差異很大需要在業(yè)務層決定是否過濾。主流程如下if __name__ __main__: forecasts, observations load_data(data/forecasts.csv, data/observations.csv) merged align_forecast_observation(forecasts, observations) if merged.empty: raise SystemExit(沒有匹配到有效樣本請檢查時間、站點、變量字段) table rank_models(merged, metricmae) print(table.to_string(indexFalse))3.5 運行結果示例如果 forecasts.csv 中包含多個站點和多天數(shù)據(jù)運行后可能得到類似這樣的輸出model sample_size mae rmse bias rank ECMWF 2800 1.82 2.41 0.35 1 GFS 2800 2.05 2.78 0.10 2 ICON 2800 2.31 3.02 -0.42 3這個結果的業(yè)務