首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >模式检验 | FSS评分的两种计算方式

模式检验 | FSS评分的两种计算方式

作者头像
用户11172986
发布2026-04-24 19:23:03
发布2026-04-24 19:23:03
470
举报
文章被收录于专栏:气python风雨气python风雨

模式检验 | FSS评分的两种计算方式

本项目旨在介绍气象预报检验中常用的空间检验指标——Fraction Skill Score (FSS)。本教程通过对比两种主流的 Python 实现方式(pySTEPS 库与 Meteva 库),演示如何科学地计算并可视化降水预报的空间技巧。


前言

传统的“点对点”检验方法(如 RMSE、TS评分)在评估高分辨率数值模式时,经常会遇到“双重惩罚”问题:即预报的降水形态非常准确,仅因位置偏移了几个格点,评分就会大幅下降。

FSS (Fraction Skill Score) 这种邻域验证方法通过比较观测与预报在邻域窗口内发生的频率,允许一定程度的空间位移,从而更客观地评价模式的预报能力。

代码语言:javascript
复制
!pip install --upgrade meteva -i https://pypi.mirrors.ustc.edu.cn/simple/
代码语言:javascript
复制
Looking in indexes: https://pypi.mirrors.ustc.edu.cn/simple/
Requirement already satisfied: meteva in /opt/conda/lib/python3.9/site-packages (1.9.2.6)
Requirement already satisfied: importlib-resources>=5.2.1 in /opt/conda/lib/python3.9/site-packages (from meteva) (5.4.0)
Requirement already satisfied: pandas>=1.0.4 in /opt/conda/lib/python3.9/site-packages (from meteva) (1.2.4)
Requirement already satisfied: matplotlib>=3.2.2 in /opt/conda/lib/python3.9/site-packages (from meteva) (3.4.2)
Requirement already satisfied: netCDF4>=1.4.1 in /opt/conda/lib/python3.9/site-packages (from meteva) (1.5.8)
Requirement already satisfied: numpy>=1.12.1 in /opt/conda/lib/python3.9/site-packages (from meteva) (1.20.3)
Requirement already satisfied: httplib2>=0.12.0 in /opt/conda/lib/python3.9/site-packages (from meteva) (0.20.2)
Requirement already satisfied: scipy>=0.19.0 in /opt/conda/lib/python3.9/site-packages (from meteva) (1.7.2)
Requirement already satisfied: scikit-learn>=0.21.2 in /opt/conda/lib/python3.9/site-packages (from meteva) (0.24.2)
Requirement already satisfied: protobuf>=1.0.0 in /opt/conda/lib/python3.9/site-packages (from meteva) (3.15.8)
Requirement already satisfied: urllib3>=1.21.1 in /opt/conda/lib/python3.9/site-packages (from meteva) (1.26.5)
Requirement already satisfied: pyshp>=2.1.0 in /opt/conda/lib/python3.9/site-packages (from meteva) (2.1.3)
Requirement already satisfied: tables>=3.4.4 in /opt/conda/lib/python3.9/site-packages (from meteva) (3.6.1)
Requirement already satisfied: shapely>=1.8.0 in /opt/conda/lib/python3.9/site-packages (from meteva) (1.8.0)
Requirement already satisfied: pynverse>=0.1.4.6 in /opt/conda/lib/python3.9/site-packages (from meteva) (0.1.4.6)
Requirement already satisfied: xarray>=0.10.0 in /opt/conda/lib/python3.9/site-packages (from meteva) (0.20.1)
Requirement already satisfied: pyparsing!=3.0.0,!=3.0.1,!=3.0.2,!=3.0.3,<4,>=2.4.2 in /opt/conda/lib/python3.9/site-packages (from httplib2>=0.12.0->meteva) (2.4.7)
Requirement already satisfied: zipp>=3.1.0 in /opt/conda/lib/python3.9/site-packages (from importlib-resources>=5.2.1->meteva) (3.4.1)
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=3.2.2->meteva) (0.10.0)
Requirement already satisfied: pillow>=6.2.0 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=3.2.2->meteva) (8.4.0)
Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=3.2.2->meteva) (2.8.1)
Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=3.2.2->meteva) (1.3.1)
Requirement already satisfied: six in /opt/conda/lib/python3.9/site-packages (from cycler>=0.10->matplotlib>=3.2.2->meteva) (1.16.0)
Requirement already satisfied: cftime in /opt/conda/lib/python3.9/site-packages (from netCDF4>=1.4.1->meteva) (1.5.1.1)
Requirement already satisfied: pytz>=2017.3 in /opt/conda/lib/python3.9/site-packages (from pandas>=1.0.4->meteva) (2021.1)
Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/lib/python3.9/site-packages (from scikit-learn>=0.21.2->meteva) (2.1.0)
Requirement already satisfied: joblib>=0.11 in /opt/conda/lib/python3.9/site-packages (from scikit-learn>=0.21.2->meteva) (1.0.1)
Requirement already satisfied: numexpr>=2.6.2 in /opt/conda/lib/python3.9/site-packages (from tables>=3.4.4->meteva) (2.7.3)

环境配置与导入库

本教程依赖 numpy 进行数据处理,matplotlib 进行绘图,并对比使用 pystepsmeteva

代码语言:javascript
复制
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from pysteps.verification.spatialscores import fss # pySTEPS 核心算法
import meteva.method as mem                       # Meteva 核心算法

# 设置随机种子以保证结果可复现
np.random.seed(42)
代码语言:javascript
复制
Pysteps configuration file found at: /opt/conda/lib/python3.9/site-packages/pysteps/pystepsrc

项目概况:模拟数据构建

为了演示计算逻辑,我们构建了一组包含“位置偏差”的模拟降水场。

代码语言:javascript
复制
# 1. 观测场 (Obs):在中心设置一个 20x20 的强降水中心
obs = np.zeros((100, 100))
obs[30:50, 30:50] = 50.0  
obs += np.random.rand(100, 100) * 2  # 添加噪声

# 2. 预报场 (Fcst):将降水中心向右偏移 5 个格点
fcst = np.zeros((100, 100))
fcst[30:50, 35:55] = 50.0  
fcst += np.random.rand(100, 100) * 2

计算

方法一:使用 pySTEPS 逐点计算

pySTEPSfss 函数设计精简,需要通过循环来构建不同阈值和尺度的矩阵。

代码语言:javascript
复制
thresholds = [1.0, 5.0, 10.0]
scales = [1, 3, 5, 9, 15, 21]  # 全窗口大小

fss_pysteps = np.zeros((len(thresholds), len(scales)))

for i, thr in enumerate(thresholds):
    for j, sc in enumerate(scales):
        fss_pysteps[i, j] = fss(fcst, obs, thr, sc)

# 结果展示
print("pySTEPS FSS 矩阵:")
print(fss_pysteps)
代码语言:javascript
复制
pySTEPS FSS 矩阵:
[[0.53525734 0.90795379 0.96137832 0.98442297 0.9927203  0.99553457]
 [0.75       0.78488372 0.81521739 0.86594203 0.90088757 0.92160279]
 [0.75       0.78488372 0.81521739 0.86594203 0.90088757 0.92160279]]

方法二:使用 Meteva 批量计算

Meteva 针对业务流程进行了优化,支持通过 half_window_size_list 批量输出中间结果。

注意: Meteva 使用“半窗口”定义。若要对应 pySTEPS 的 Scale=21,则 Meteva 的 Half_window 应设为 10(公式:)。

代码语言:javascript
复制
grade_list = [1.0, 5.0, 10.0]
half_window_list = [0, 1, 2, 4, 7, 10] # 对应 scale 1, 3, 5, 9, 15, 21

# 计算中间分量
result_3d = mem.fbs_pobfo(obs, fcst, grade_list=grade_list, 
                          half_window_size_list=half_window_list)

# 根据公式计算 FSS: 1 - fbs / (pob + pfo)
pob, pfo, fbs = result_3d[:,:,0], result_3d[:,:,1], result_3d[:,:,2]
fss_meteva = (1 - fbs / (pob + pfo + 1e-30)).T # 转置以匹配 (阈值, 尺度)

可视化

标准的 FSS 评价通常采用“评分随空间尺度变化曲线”。

代码语言:javascript
复制
def plot_fss_curves(scales, fss_matrix, thresholds):
    fig, ax = plt.subplots(figsize=(10, 6), dpi=100)
    markers = ['o', 's', '^']
    
    for i, thr in enumerate(thresholds):
        ax.plot(scales, fss_matrix[i, :], marker=markers[i], label=f'Threshold ≥ {thr} mm')

    # 基准参考线:FSS=0.5
    ax.axhline(y=0.5, color='grey', linestyle='--', alpha=0.8)
    ax.text(scales[-1], 0.52, 'Skillful Threshold (0.5)', color='grey', ha='right')

    ax.set_ylim(0, 1.05)
    ax.set_xticks(scales)
    ax.set_xlabel('Neighborhood Scale (grid points)', fontweight='bold')
    ax.set_ylabel('FSS Score', fontweight='bold')
    ax.set_title('Fraction Skill Score vs. Spatial Scale', fontsize=14)
    ax.legend(loc='lower right')
    ax.grid(True, linestyle=':', alpha=0.6)
    plt.show()

# 执行绘图
plot_fss_curves(scales, fss_pysteps, thresholds)
代码语言:javascript
复制
print("\n" + "="*50)
print(f"{'Thr/Scale':<10} | {'pySTEPS (Scale 21)':<20} | {'Meteva (Half 10)':<20}")
print("-"*50)
for i, thr in enumerate(thresholds):
    print(f"{thr:<10.1f} | {fss_pysteps[i, -1]:<20.4f} | {fss_meteva[i, -1]:<20.4f}")
print("="*50)
代码语言:javascript
复制
==================================================
Thr/Scale  | pySTEPS (Scale 21)   | Meteva (Half 10)    
--------------------------------------------------
1.0        | 0.9955               | 0.9957              
5.0        | 0.9216               | 0.9216              
10.0       | 0.9216               | 0.9216              
==================================================

小结

  1. 算法一致性:通过对比可见,pySTEPSMeteva 在相同物理定义下计算出的 FSS 值是完全一致的。
  2. 参数对应关系
    • pySTEPS 使用全尺度参数 scale
    • Meteva 使用半径参数 half_window_size
    • 使用时务必确保对应关系:。
  3. 结论分析
    • 随着空间尺度增大,FSS 评分显著上升,这说明模式在更大范围内具有捕捉降水趋势的能力。
    • 阈值越高(如强降水),通常 FSS 评分越低,反映了模式对局地强降水位置预报的难度。
    • 曲线与 0.5 基准线 的交点代表了该模式的“有效分辨率”,是论文中分析技巧水平的核心依据。

完整代码

代码语言:javascript
复制
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from pysteps.verification.spatialscores import fss as pysteps_fss
import meteva.method as mem

# ==========================================
# 1. 项目概况与模拟数据构建
# ==========================================
# 固定随机种子确保结果可重复
np.random.seed(42)

# 观测场 (Obs):100x100 区域,中心设置一个 20x20 的 50mm 强降水区
obs = np.zeros((100, 100))
obs[30:50, 30:50] = 50.0
obs += np.random.rand(100, 100) * 2# 添加噪声

# 预报场 (Fcst):将降水中心向右偏移 5 个格点 (模拟位置偏差)
fcst = np.zeros((100, 100))
fcst[30:50, 35:55] = 50.0
fcst += np.random.rand(100, 100) * 2

# 参数设置
thresholds = [1.0, 5.0, 10.0]        # 降水阈值 (mm)
scales = [1, 3, 5, 9, 15, 21]        # 全窗口尺寸 (Scale)

# ==========================================
# 2. 计算:方法一 使用 pySTEPS
# ==========================================
print("正在使用 pySTEPS 计算 FSS...")
fss_pysteps = np.zeros((len(thresholds), len(scales)))

for i, thr in enumerate(thresholds):
    for j, sc in enumerate(scales):
        # pySTEPS 接口: (pred, obs, thr, scale)
        fss_pysteps[i, j] = pysteps_fss(fcst, obs, thr, sc)

print("pySTEPS 计算完成。")

# ==========================================
# 3. 计算:方法二 使用 Meteva
# ==========================================
print("\n正在使用 Meteva 计算 FSS...")
# 对应 pySTEPS 的 scale,计算 Meteva 所需的半窗口 (half_window_size)
# 公式: half = (scale - 1) / 2
half_window_list = [(s - 1) // 2for s in scales]

# 调用 Meteva 中间分量计算函数
# 返回 shape 为 (窗口数, 阈值数, 3)
result_3d = mem.fbs_pobfo(
    obs, 
    fcst, 
    grade_list=thresholds, 
    half_window_size_list=half_window_list
)

# 计算 FSS: 1 - fbs / (pob + pfo)
pob = result_3d[:, :, 0]
pfo = result_3d[:, :, 1]
fbs = result_3d[:, :, 2]
# 转置以便与 pysteps 结果矩阵结构(阈值, 尺度)对齐
fss_meteva = (1 - fbs / (pob + pfo + 1e-30)).T

print("Meteva 计算完成。")

# ==========================================
# 4. 可视化:科研标准绘图
# ==========================================
def plot_fss_comparison(scales, matrix, thresholds, title_suffix=""):
    fig, ax = plt.subplots(figsize=(10, 6), dpi=120)
    
    markers = ['o', 's', '^', 'D']
    colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']
    
    for i, thr in enumerate(thresholds):
        ax.plot(scales, matrix[i, :], 
                marker=markers[i % len(markers)], 
                color=colors[i % len(colors)],
                linewidth=2, markersize=8,
                label=f'Threshold $\geq$ {thr} mm')

    # 基准参考线 FSS = 0.5
    ax.axhline(y=0.5, color='grey', linestyle='--', linewidth=1.5, alpha=0.7)
    ax.text(scales[-1], 0.52, 'Skillful Threshold (0.5)', color='grey', ha='right', fontsize=10)

    # 坐标轴美化
    ax.set_ylim(0, 1.05)
    ax.set_xlim(scales[0], scales[-1])
    ax.set_xticks(scales)
    ax.set_xlabel('Neighborhood Scale (grid points)', fontsize=12, fontweight='bold')
    ax.set_ylabel('FSS Score', fontsize=12, fontweight='bold')
    ax.set_title(f'Fraction Skill Score Analysis {title_suffix}', fontsize=14, pad=15)
    ax.legend(loc='lower right', frameon=True, shadow=True)
    ax.grid(True, linestyle=':', alpha=0.6)
    
    plt.tight_layout()
    plt.show()

# 绘制 pySTEPS 结果图
plot_fss_comparison(scales, fss_pysteps, thresholds, "(via pySTEPS)")

# ==========================================
# 5. 结果打印对比
# ==========================================
print("\n" + "="*50)
print(f"{'Thr/Scale':<10} | {'pySTEPS (Scale 21)':<20} | {'Meteva (Half 10)':<20}")
print("-"*50)
for i, thr in enumerate(thresholds):
    print(f"{thr:<10.1f} | {fss_pysteps[i, -1]:<20.4f} | {fss_meteva[i, -1]:<20.4f}")
print("="*50)
代码语言:javascript
复制
正在使用 pySTEPS 计算 FSS...
pySTEPS 计算完成。

正在使用 Meteva 计算 FSS...
Meteva 计算完成。
代码语言:javascript
复制
==================================================
Thr/Scale  | pySTEPS (Scale 21)   | Meteva (Half 10)    
--------------------------------------------------
1.0        | 0.9955               | 0.9957              
5.0        | 0.9216               | 0.9216              
10.0       | 0.9216               | 0.9216              
==================================================
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 气python风雨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 模式检验 | FSS评分的两种计算方式
    • 前言
    • 环境配置与导入库
    • 项目概况:模拟数据构建
    • 计算
      • 方法一:使用 pySTEPS 逐点计算
      • 方法二:使用 Meteva 批量计算
    • 可视化
    • 小结
    • 完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档