Repository navigation
Conversation
Add `excess_volatility` (scaled excess volatility, sEV) and `forecast_percentage_change` (symmetric forecast percentage change, sFPC) to `utilsforecast.losses`. Both compare the forecasts that consecutive cross-validation cutoffs make for the same target date, and work with pandas and polars. - Forecasts are paired by (next cutoff, same date), so any `step_size` smaller than the horizon works; non-overlapping windows raise. - Like the other losses, both return one row per series and cutoff. A row scores the revisions made at that cutoff; the first cutoff is null. - sEV rows share a per-series scale, so averaging a series' rows over cutoffs gives its sEV over the whole cross-validation. Also add a forecast volatility tutorial using StatsForecast and NeuralForecast cross-validation output.
- Treat NaN like null when choosing the pairs to score, so a polars NaN forecast drops only its own pairs instead of turning whole cutoffs NaN. - Escape `\sum` in the `excess_volatility` docstring and rewrap the `forecast_percentage_change` docstring. - Add both metrics to the losses API reference. - Show how to include the median in sEV in the tutorial.
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
|
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
As a multi-horizon forecasting system operates over time, it generates multiple overlapping forecasts for the same future target date, one from each new forecast creation date (FCD) as more data becomes available. This sequence of updates is a forecast revision, and how consistent (or erratic) those revisions are is defined as forecast volatility. This PR adds two metrics that measure forecast volatility, introduced in [1]: scaled Excess Volatility (sEV) and Symmetric Forecast Percentage Change (sFPC).
They were first proposed for NeuralForecast in Nixtla/neuralforecast#1641, and moved here since
neuralforecast.losses.numpyis being deprecated.[1] Willa Potosnak, Malcolm Wolff, Mengfei Cao, Ruijun Ma, Tatiana Konstantinova, Dmitry Efimov, Michael W. Mahoney, Boris Oreshkin, Kin G. Olivares. Forking-Sequences: Statistically and Computationally Efficient Multi-Horizon Forecasting with Reduced Volatility. Transactions on Machine Learning Research (2026).
What this adds
Two losses in
utilsforecast.losses, working on pandas and polars cross-validation output like the existing ones:forecast_percentage_change(sFPC) measures the relative size of revisions, bounded in [0, 200].excess_volatility(sEV) measures harmful revisions: the revision cost, as the quantile loss between the older and newer forecasts, minus the accuracy improvement it produced, scaled by|y|:EV = QL(ŷ_update, ŷ_before) − (QL(y, ŷ_before) − QL(y, ŷ_update))Both pair each forecast with the forecast from the next cutoff of the same series for the same target date, so they work with any
step_sizesmaller than the horizon and raise a clear error when windows do not overlap.Usage
Output
Like the other losses, both return one row per series and cutoff. A row scores the revisions made at that cutoff, so the first cutoff of each series is null.
sEV divides every cutoff of a series by the same scale rather than by that cutoff's own sum of |y|, so that averaging a series' rows over cutoffs gives exactly the sEV of [1] for that series. The trade-off is that adding or removing a cutoff changes the other cutoffs' values.
Docs
nbs/docs/tutorials/forecast_volatility.ipynb, listed under Tutorials. It motivates forecast volatility, then applies the metrics to StatsForecast and NeuralForecastcross_validationoutput.docs/losses.html.md).Tests
31 cases in
tests/test_losses.py, covering the new code fully: hand-computed values for both metrics, the EV ≥ 0 property with asymmetric quantiles, pandas/polars parity,step_size > 1, per-cutoff output matchingmae's layout, rows averaging to the overall sEV, NaN/null forecasts, series with fewer or missing cutoffs, and the error cases.Notes for reviewers