Skip to content

fix: preserve target scale in MFLES fallback - #1239

Open
Dresden (DresdenGman) wants to merge 1 commit into
Nixtla:mainfrom
DresdenGman:fix/mfles-naive-original-scale
Open

Dresden (DresdenGman) wants to merge 1 commit into
Nixtla:mainfrom
DresdenGman:fix/mfles-naive-original-scale

Conversation

@DresdenGman

Copy link
Copy Markdown
Contributor

Summary

Keep the original target scale in MFLES's existing short/constant-series fallback. The fallback currently runs after normalization/log transformation, overwrites the saved additive transform parameters, and returns transformed fitted values without applying the inverse transform.

For example, on main at 99f2408:

import numpy as np
from statsforecast.models import MFLES

model = MFLES().fit(np.full(10, 7.0))
print(model.predict(3)["mean"])                 # currently [0., 0., 0.]
print(model.predict_in_sample()["fitted"])    # currently ten zeros

model = MFLES().fit(np.array([5.0, 7.0]))
print(model.predict(3)["mean"])                # currently [1., 1., 1.]

The existing fallback is intended to repeat the last observation, so these cases should return 7 on the original target scale. With multiplicative=True, a short history such as [5, 7] also returns log-scale fitted values even though its forecasts are inverse-transformed.

Changes

  • Preserve the mean and standard deviation saved before normalization, so predict applies the correct inverse transform.
  • Inverse-transform the fallback's fitted values, as the non-fallback path already does.
  • Keep the fallback trigger and ordinary decomposition/optimization paths unchanged.
  • Add offline regression tests for additive/log-scale cases, positive/negative/zero/fractional constants, one-to-three-point histories, float32/float64, both MFLES/AutoMFLES public wrappers, and fitting a fallback series after a model with exogenous components.

This is independent of #911's forward/update design and #1215's multi-seasonal cycle fix. It does not add a forward method or change seasonal-cycle construction.

Validation

Python 3.12.12, NumPy 2.5.3, SciPy 1.18.1, pandas 2.3.3 and scikit-learn 1.9.1; StatsForecast built from 99f2408.

  • New regression file against the earlier unmodified base d31d8ab: 39 failed, 7 passed. The affected upstream code is unchanged at 99f2408, where the minimal examples above still reproduce.
  • After rebasing the patch onto 99f2408: 48 passed across the new regression file and the existing MFLES tests.
  • Eight non-fallback controls (seasonal/nonseasonal × additive/log × with/without exogenous features): fitted values and forecasts exactly match unmodified main.
  • Ruff check and whitespace validation pass.
python -m pytest -o addopts='' -q tests/test_mfles_naive.py tests/test_mfles.py
ruff check tests/test_mfles_naive.py python/statsforecast/mfles.py
git diff --check

The targeted command disables the full-suite coverage gate; this is not a full-suite coverage result. The existing demand-data-download integration tests were not run. Explicit log transforms of nonpositive data and seasonal decomposition output for degenerate series are outside this patch's scope.

@codspeed

codspeed Bot commented Sep 20, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 38 untouched benchmarks


Comparing DresdenGman:fix/mfles-naive-original-scale (99fd875) with main (99f2408)

Open in CodSpeed

This branch has not been deployed

No deployments
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.

1 participant