Skip to content

[ENH] Add preprocessing (scalers and normalizers) to tslib datamodule - #2368

Open
echo-xiao wants to merge 14 commits into
sktime:mainfrom
echo-xiao:enh/tslib-preprocessing
Open

[ENH] Add preprocessing (scalers and normalizers) to tslib datamodule#2368
echo-xiao wants to merge 14 commits into
sktime:mainfrom
echo-xiao:enh/tslib-preprocessing

Conversation

@echo-xiao

Copy link
Copy Markdown
Contributor

Reference Issues/PRs

Fixes #2330.

What does this implement/fix? Explain your changes.

TslibDataModule accepted scalers / target_normalizer but never used them
(stored and ignored). This makes them functional, following #2302 and reusing
ScalerAdapter:

  • __init__ wraps them in ScalerAdapter, adds _*_fitted flags and a
    per-series _preprocess_cache.
  • _fit_scalers / _fit_target_normalizer fit on the train split only.
  • _normalize_features / _normalize_target transform per series (no-op until
    fitted; only configured continuous columns).
  • _preprocess_data applies the transforms and caches per series;
    setup("fit") fits on _train_indices.

What should a reviewer concentrate their feedback on?

  1. no deepcopy of scalers
  2. deferring target_scale** to a follow-up
  3. only continuous features are scaled, the models don't consume them yet
  4. target_scale, collate_fn and the models are untouched

Did you add any tests for the change?

Yes — 7 tests in pytorch_forecasting/data/tests/test_tslib_data_module.py:

  • adapter wrapping + flags;
  • fit-on-train (StandardScaler → ~0 mean / ~1 std);
  • target-normalizer fit flag;
  • feature transform (only configured columns);
  • target no-op-until-fitted; _preprocess_data scaling + cache identity;
  • setup("fit") produces scaled samples;
  • a no-scaler backward-compat guard (byte-identical output, no target_scale).

PR checklist

  • The PR title starts with [ENH].
  • Added/modified tests.
  • Used pre-commit hooks (pre-commit run --files on both changed files, clean).

@echo-xiao

echo-xiao commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

hi, @phoeenniixx please review these and let me know if these decisions are right:

  • no deepcopy of scalers
  • deferring target_scale to a follow-up, collate_fn and the models are untouched
  • only continuous features are scaled, the models don't consume them yet

TODO:

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.28571% with 4 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@2c58076). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...forecasting/data/data_module/_tslib_data_module.py 94.28% 4 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2368   +/-   ##
=======================================
  Coverage        ?   90.43%           
=======================================
  Files           ?      203           
  Lines           ?    11090           
  Branches        ?        0           
=======================================
  Hits            ?    10029           
  Misses          ?     1061           
  Partials        ?        0           
Flag Coverage Δ
cpu 90.43% <94.28%> (?)
pytest 90.43% <94.28%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.



def _make_ts(n_series: int = 20, length: int = 40, offset: float = 100.0) -> TimeSeries:
"""合成数据集:连续特征 ``x`` 远离 0(~offset),便于看出标准化;目标 ``y`` 是正弦。"""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please use english docstrings

@phoeenniixx phoeenniixx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice!
I have a few questions:

  • Did you look at the thuml implementation? I think we should try to keep it as close to this package as possible - when it comes to the preprocessing logic as this dm is based on this package
    • Currently it supports all the scalers and normalizers that EncoderDecoderDataModule supports, but is this true for the tslib implementation as well?

Also, please use english docstrings, to maintain uniformity across the documentation.

@phoeenniixx phoeenniixx added enhancement New feature or request module:datasets&dataloaders ptf-v2 Related to `pytorch-forecasting` v2 labels Aug 4, 2026
Address review feedback on sktime#2368: the helper docstring and inline
comments added for the preprocessing tests were written in Chinese.
Rewrite them in English to stay consistent with the rest of the
codebase documentation.
`TslibDataModule` accepted `scalers` and `target_normalizer` but never used
them. This makes them functional, fit once on the training split, mirroring
thuml's data layer.

Scope follows thuml's preprocessing, which hardcodes a single global sklearn
StandardScaler fit on the train segment:

- Only sklearn `StandardScaler` is accepted. Anything else raises
  `NotImplementedError` rather than failing deep inside the scaler stack or,
  in the case of `EncoderNormalizer`, silently producing wrongly scaled data
  (its `fit_per_sequence` semantics are inverted by a global fit).
- `target_normalizer` now defaults to `None` instead of `"auto"`, and
  `"auto"` resolves to no normalization. The inverse transform
  (`target_scale`) is not implemented yet (sktime#2359), and all three models on
  this data module guard on `"target_scale" in x`, so normalizing by default
  would leave every prediction in normalized space with no way back.

Making the fit boundary hold required two further changes:

- `_ensure_split` computes the train/val/test split once and caches it,
  matching what sktime#2302 did for `EncoderDecoderTimeSeriesDataModule`. `setup`
  runs once per stage, so the unconditional `torch.randperm` handed
  `trainer.test()` a different split than `trainer.fit()` had used, moving
  series the scalers were fit on into the test set.
- The fit calls moved out of the `stage == "fit"` branch, so a standalone
  `setup("test")` no longer passes raw values through unscaled.

Also drops the per-series preprocessing cache added earlier on this branch:
it is unrelated to scaling and belongs with the base class work in sktime#2313.

Drive-by: `test_multivariate_target` asserted `y.shape[-1] == 2`, but sktime#1960
changed multi-target `__getitem__` to return one tensor per target. The
assertion is updated to match. This file is not collected by CI, which is
why the failure went unnoticed.
`testpaths` listed only `tests/` and `pytorch_forecasting/tests/`, so
`pytorch_forecasting/data/tests/` was never collected. Its tests have
therefore not run in CI at all, which is why the codecov patch report shows
0% for `test_tslib_data_module.py` and why a stale assertion in
`test_multivariate_target` went unnoticed for months.

Adding the directory collects 34 further tests (1165 -> 1199) and they all
pass, so this does not turn CI red.
`test_tslib_data_module.py` lived in `pytorch_forecasting/data/tests/`,
which is not listed in `testpaths`, so it had never run in CI. That is why
codecov reported 0% patch coverage for it, and why a stale assertion in
`test_multivariate_target` (left behind by sktime#1960) went unnoticed.

Move it to `tests/test_data/`, where every other data layer test already
lives -- `test_data_module.py`, `test_d1.py`, `test_encoders.py`,
`test_samplers.py`, `test_timeseries.py` -- and where sktime#2302 put the
`EncoderDecoderTimeSeriesDataModule` scaler tests this PR mirrors. This also
reverts the `testpaths` entry added in the previous commit, which is no
longer needed.

The 34 tests are now collected and all pass.
@echo-xiao

Copy link
Copy Markdown
Contributor Author

Nice! I have a few questions:

  • Did you look at the thuml implementation? I think we should try to keep it as close to this package as possible - when it comes to the preprocessing logic as this dm is based on this package

    • Currently it supports all the scalers and normalizers that EncoderDecoderDataModule supports, but is this true for the tslib implementation as well?

Also, please use english docstrings, to maintain uniformity across the documentation.

In thuml's data layer, it's preprocessing is:

  • one hardcoded sklearn standardscaler. no other way to pass a custom scaler
  • features and target share the one scaler, there is no seperate target_normalizer
  • it is fit strictly on the train segment.
  • inverse_transform is exposed on the datasets. metrics are computed in the original scale.
  • per-window normalisation exists but lives in the model, not in the data layer. the normalizer in usa.py is per-sample and classification-only.

tslib does not support EncoderDecoderDataModule supports.

  • list/tuple of normalizers
  • multivariate target + one normalizer
  • groupnormalizer
  • encodernormalizer

what this pr does:

  • scalers are now really fits on the training split only. the split is computed once and cached, scalers are fit once, and fitting happens for every stage. previously setup() redrew the split on each call, so trainer.test() saw a split the scalers had already been fit on.
  • only sklearn standardscaler is accepted. anything else raises not implemented error.
  • target normalization is off by default.
  • the tests now run in CI

`TslibDataModule` now rejects normalizers other than sklearn's
`StandardScaler`, so the tutorial's `target_normalizer=TorchNormalizer()`
raises `NotImplementedError` and fails the notebook CI job.

Switch the tutorial to `StandardScaler` and drop the now unused
`TorchNormalizer` import.
@review-notebook-app

Copy link
Copy Markdown

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

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

Labels

enhancement New feature or request module:datasets&dataloaders ptf-v2 Related to `pytorch-forecasting` v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENH] Add preprocessing to tslib datamodule

2 participants