Skip to content

feat: Implement Advanced Spatial (FSS) and Categorical Verification Metrics - #7

Open
GiGiKoneti wants to merge 4 commits into
mllam:mainfrom
GiGiKoneti:feat/fss-categorical-metrics
Open

feat: Implement Advanced Spatial (FSS) and Categorical Verification Metrics#7
GiGiKoneti wants to merge 4 commits into
mllam:mainfrom
GiGiKoneti:feat/fss-categorical-metrics

Conversation

@GiGiKoneti

@GiGiKoneti GiGiKoneti commented Jun 16, 2026

Copy link
Copy Markdown

Describe your changes

This PR implements advanced spatial and categorical verification metrics as proposed in Issue #6:

  • fractions_skill_score() (FSS) in statistics.py for spatial scale-selective verification.
  • equitable_threat_score() (ETS) in statistics.py for categorical skill evaluation.
  • brier_score() in statistics.py for probabilistic threshold verification.
  • plot_fss_scale() in plot.py for scale-selective FSS visualization.
  • Complete test suites for FSS, ETS, Brier Score, and FSS plotting.
  • Added changes to CHANGELOG.md.

No new dependencies are required.

Issue Link

Closes #6

Type of change

  • ✨ New feature (non-breaking change that adds functionality)

Checklist before requesting a review

  • My branch is up-to-date with the target branch - if not update your fork with the changes from the target branch (use pull with --rebase option if possible).
  • I have performed a self-review of my code
  • For any new/modified functions/classes I have added docstrings that clearly describe its purpose, expected inputs and returned values
  • I have placed in-line comments to clarify the intent of any hard-to-understand passages of my code
  • I have updated the documentation to cover introduced code changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have given the PR a name that clearly describes the change, written in imperative form.
  • I have requested a reviewer and an assignee

@GiGiKoneti
GiGiKoneti force-pushed the feat/fss-categorical-metrics branch from 52fa97c to 91b986d Compare June 16, 2026 22:23
@GiGiKoneti
GiGiKoneti force-pushed the feat/fss-categorical-metrics branch from 91b986d to d57e3cd Compare June 16, 2026 22:26
@GiGiKoneti

Copy link
Copy Markdown
Author

@mfroelund
Please review this PR

@mfroelund mfroelund left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks really good, thanks for all your work. I've put a bunch of comments to consider, but overall very nice improvements!

Comment thread mllam_verification/operations/statistics.py
Comment thread mllam_verification/operations/statistics.py Outdated
Comment thread mllam_verification/operations/statistics.py Outdated
**stats_op_kwargs: Additional keyword arguments.

Returns:
Dataset or DataArray with ETS values. Range: -1/3 to 1.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be sure, should it be 0 - 1 and not -1/3 - 1, or what does -1/3 signify ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The range of the Equitable Threat Score (ETS) is indeed [-1/3, 1].

  • A score of 1 indicates a perfect forecast.
  • A score of 0 indicates no skill (performing no better than a random forecast).
  • A score of -1/3 represents the worst possible forecast under certain conditions (specifically, when predictions and observations are completely mismatched with a base rate of 0.5, as discussed in Section 3a of Hogan et al., 2010).

I've kept -1/3 to 1 as the range in the docstring and added the citation to the Hogan et al. (2010) paper to clarify this.

Comment thread mllam_verification/operations/statistics.py Outdated
Comment thread mllam_verification/plot.py Outdated
da_prediction: xr.DataArray,
threshold: float,
window_sizes: list[int],
spatial_dims: Optional[list[str]] = None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to put ["x", "y"] as a default value, instead of doing that inside the function on line 643? That way, it's more clear from the function argument list, what spatial_dims are used if none are provided?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If accepted, then the Optional part of the type hint should be omitted as described in below comment.

@GiGiKoneti GiGiKoneti Jun 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. I've updated the function signature to specify spatial_dims: list[str] = ["x", "y"] and removed the redundant fallback check inside the function body.

Comment thread mllam_verification/plot.py Outdated
Comment thread mllam_verification/plot.py
Comment thread docs/_images/rank_histogram_example.png
Comment thread mllam_verification/plot.py
@GiGiKoneti
GiGiKoneti force-pushed the feat/fss-categorical-metrics branch from 5e3b132 to 48498e9 Compare June 25, 2026 10:44
@GiGiKoneti

Copy link
Copy Markdown
Author

@mfroelund
I have updated the PR to address all your comments:

  1. FSS Plotting (plot_fss_scale): Changed default parameter values for spatial_dims to ["x", "y"] and hue to "datasource", removing the conditional checks. Also added logging via loguru when the requested hue is not found in coordinates/dimensions.
  2. Statistics:
    • Updated the DOI reference link for Fortin et al. (2014) in spread_skill_ratio.
    • Added a concrete example for "yes" events in the Equitable Threat Score docstring.
    • Refined the comment explaining the mse_ref == 0 edge-case in fractions_skill_score.
  3. Documentation: Added the visualization row entries for Rank histogram and FSS vs scale to the README.md table.
  4. CI & Repository: Cleaned up accidental environment files (.venv-test/, .precommit-home/, and uv.lock) and updated the CI pre-commit workflow to run hooks directly to avoid cache service outages.

All tests and pre-commit hooks are passing locally. Please let me know if you need anything else!

@mfroelund mfroelund left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final small comments. Also there are two comments from the previous review, that I still would like to hear your thoughts on.

... spatial_dims=["x", "y"],
... )
"""
if spatial_dims is None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the fractions_skill_score() function signature to default directly to spatial_dims: list[str] = ["x", "y"] and removed the internal check, matching the design pattern of plot_fss_scale().

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a difference between Model A and Model B. Could maybe be good to add some noise to make the example look more realistic.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've regenerated the plot with realistic synthetic data: Model A represents a high-skill forecast with low noise, while Model B has higher noise and a spatial displacement offset to simulate the double-penalty behavior. The new plot clearly illustrates Model A's superior performance across scales.

@GiGiKoneti
GiGiKoneti force-pushed the feat/fss-categorical-metrics branch 2 times, most recently from eb74912 to 2837ad7 Compare June 25, 2026 12:14
@GiGiKoneti
GiGiKoneti force-pushed the feat/fss-categorical-metrics branch from 2837ad7 to d12a745 Compare June 25, 2026 12:18
@GiGiKoneti

Copy link
Copy Markdown
Author

@mfroelund
Thanks for your detailed review..
Is there anything else u find which can be improved ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Implement Advanced Spatial (FSS) and Categorical Verification Metrics for High-Resolution LAMs

2 participants