Skip to content

Add probabilistic ensemble evaluation metrics (CRPS, spread-skill ratio, rank histogram) - #4

Open
GiGiKoneti wants to merge 10 commits into
mllam:mainfrom
GiGiKoneti:feat/ensemble-evaluation-metrics
Open

Add probabilistic ensemble evaluation metrics (CRPS, spread-skill ratio, rank histogram)#4
GiGiKoneti wants to merge 10 commits into
mllam:mainfrom
GiGiKoneti:feat/ensemble-evaluation-metrics

Conversation

@GiGiKoneti

Copy link
Copy Markdown

Describe your changes

mllam-verification currently only covers deterministic metrics
(rmse, mae). This PR adds the probabilistic ensemble evaluation
suite proposed in issue #3 and approved by @mfroelund.

Three new functions following the exact compute_pipeline_statistic
architecture already established in statistics.py and plot.py:

statistics.py

  • crps() — wraps scores.probability.crps_for_ensemble via
    compute_pipeline_statistic, mirroring how rmse wraps
    scores.continuous.rmse. Uses the fair (unbiased) estimator
    (Zamo & Naveau 2018) which correctly accounts for finite ensemble
    size. Accepts a configurable ensemble_member_dim parameter.
  • spread_skill_ratio() — computes ensemble spread divided by RMSE
    of the ensemble mean. A perfectly calibrated ensemble has SSR = 1.0.
    Values below 1.0 indicate underdispersion.

plot.py

  • plot_rank_histogram() — wraps scores.plotdata.rank_histogram to
    produce a Talagrand diagram. Includes a dashed reference line
    marking perfect calibration.

tests/unit/conftest.py

  • Added da_ensemble_prediction_2d_utc fixture — a 10-member ensemble
    DataArray built from the existing deterministic prediction fixture
    by adding Gaussian noise per member.

No new dependancies introduced. All three functions use the existing
scores>=1.2.0 dependency already pinned in pyproject.toml.

All 34 tests pass. Pre-commit hooks (isort, black, flake8,
mypy) pass cleanly at the 90-char line length.

Note: no CHANGELOG file exists in the repository currently.

Issue Link

Closes #3

Type of change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📖 Documentation (Addition or improvements to documentation)

Checklist before requesting a review

  • My branch is up-to-date with the target branch
  • 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 (context).
  • I have requested a reviewer and an assignee (assignee is responsible for merging)

Checklist for reviewers

Each PR comes with its own improvements and flaws. The reviewer should check the following:

  • the code is readable
  • the code is well tested
  • the code is documented (including return types and parameters)
  • the code is easy to maintain

Author checklist after completed review

  • I have added a line to the CHANGELOG describing this change, in a section
    reflecting type of change (add section where missing):
    • added: when you have added new functionality
    • changed: when default behaviour of the code has been changed
    • fixes: when your contribution fixes a bug

Checklist for assignee

  • PR is up to date with the base branch
  • the tests pass
  • author has added an entry to the changelog (and designated the change as added, changed or fixed)
  • Once the PR is ready to be merged, squash commits and merge the PR.

…semble evaluation

Adds probabilistic ensemble evaluation metrics to mllam-verification,
as proposed in issue mllam#3 and approved by @mfroelund.

New functions in statistics.py:
- crps(): wraps scores.probability.crps_for_ensemble via
  compute_pipeline_statistic, following the same pattern as rmse()
  and mae(). Uses the fair (unbiased) estimator. Accepts any
  ensemble_member_dim name.
- spread_skill_ratio(): computes ensemble spread / RMSE of ensemble
  mean. SSR = 1.0 indicates perfect calibration. SSR < 1.0 indicates
  underdispersion.

New function in plot.py:
- plot_rank_histogram(): wraps scores.plotdata.rank_histogram to
  produce a Talagrand diagram. Includes a reference line for perfect
  calibration.

New test fixture in conftest.py:
- da_ensemble_prediction_2d_utc: 10-member ensemble DataArray built
  from the existing deterministic prediction fixture.

All functions follow the existing architecture exactly:
compute_pipeline_statistic backbone, Google-style docstrings,
cell_methods CF-convention attribute, 90-char line length.

No new dependencies introduced. All functions use the existing
scores>=1.2.0 dependency already pinned in pyproject.toml.

Closes mllam#3
@GiGiKoneti

Copy link
Copy Markdown
Author

@mfroelund.. PR ready for your review when you get a chance

@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.

Hi! Sorry for the delay. I've made the review, and it generally looks great! I've just added a few minor comments/suggestions.

Comment thread mllam_verification/operations/statistics.py Outdated
Comment thread mllam_verification/operations/statistics.py Outdated
Comment thread tests/unit/test_plot.py Outdated
Comment thread tests/unit/test_plot.py
…ove imports

- Remove unused groupby parameter from crps() and spread_skill_ratio()
- Swap argument order to (ds_reference, ds_prediction) matching plot
  calling convention and mae() signature
- Add preserve_dims support to spread_skill_ratio() for hovmoller plots
- Move matplotlib/plot_rank_histogram imports to top of test_plot.py
- Add crps and spread_skill_ratio to timeseries/hovmoller test
  parametrizations
- Add da_ensemble_prediction_2d_elapsed fixture for elapsed-time tests
- Update test_statistics.py to match new argument order

All 38 tests pass. Pre-commit hooks (isort, black, flake8, mypy) clean.
@GiGiKoneti

Copy link
Copy Markdown
Author

I think now it should be okay @mfroelund .

@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.

Thanks for your edits. Just one last small addition :)

Comment thread tests/unit/test_plot.py
@GiGiKoneti

Copy link
Copy Markdown
Author

Hey @mfroelund!

Just wrapped your last bit of feedback on the "unexpected input" tests.

I've added crps and spread_skill_ratio to both test suites, implemented full groupby support for them in statistics.py (they now work just like rmse()/mae()), and verified all 45 tests are passing cleanly.

Should be good to go for final review now. Thanks for all the guidance!

@GiGiKoneti

Copy link
Copy Markdown
Author

Another thing @mfroelund Based on my work so far, here are a few ideas that could really level up the repo:

  1. Refactor plot.py complexity - Those # noqa: C901 functions are getting unwieldy. Breaking them into helper functions (_prepare_ensemble_data, _apply_time_ops, etc.) would make testing and maintenance way easier.

  2. Add CRPSS (skill score) - Since we already have CRPS, adding CRPSS would be super valuable. It's basically 1 - (CRPS_model / CRPS_reference) and way more interpretable for comparing models.

  3. Ensemble fan plots - A plot_ensemble_timeseries_fan() showing mean + percentile bands would be the perfect complement to rank histograms. Pretty standard in weather forecasting viz.

  4. Better docs - The docs/ folder is pretty empty rn. A Sphinx/MkDocs site with an example gallery would make the project way more accessible.

  5. CHANGELOG.md - Following the "Keep a Changelog" format would help track changes better, esp as the project grows.

Lmk what you think 🙂
Why don't we have channel for this repository in slack ? I hope it will be necessary in near future as mllam is going through architectural shift.

@mfroelund

Copy link
Copy Markdown
Collaborator

Hey @mfroelund!

Just wrapped your last bit of feedback on the "unexpected input" tests.

I've added crps and spread_skill_ratio to both test suites, implemented full groupby support for them in statistics.py (they now work just like rmse()/mae()), and verified all 45 tests are passing cleanly.

Should be good to go for final review now. Thanks for all the guidance!

Thanks for the final adjustments, looks great! Just realized, that we don't have a changelog in this repo. Do you mind adding one in this PR?

@GiGiKoneti

GiGiKoneti commented Apr 23, 2026

Copy link
Copy Markdown
Author

@mfroelund I've updated the CHANGELOG.md and ensured it follows the same style as the neural-lam repository, including PR links and contributor handles. Ready for review!
Later I can update the README.md file once this PR is merged.

@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 good!

@mfroelund

Copy link
Copy Markdown
Collaborator

I'm ready to merge, when linting passes:)

@GiGiKoneti

Copy link
Copy Markdown
Author

I'm ready to merge, when linting passes:)

there's an ongoing GitHub Actions outage failing the cache download right now..

@GiGiKoneti

Copy link
Copy Markdown
Author

@mfroelund
Can u approve to start linting checks please

@GiGiKoneti

Copy link
Copy Markdown
Author

@mfroelund
Do you have any idea why we are having this GitHub Actions service outage ?

@GiGiKoneti
GiGiKoneti requested a review from mfroelund June 24, 2026 19:43
@mfroelund

Copy link
Copy Markdown
Collaborator

@mfroelund Do you have any idea why we are having this GitHub Actions service outage ?

No I have no idea, but now it seems to be resolved!

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.

Add probabilistic evaluation metrics (CRPS, rank histograms) via existing scores dependency

2 participants