Skip to content

Commit 1a08f32

Browse files
authored
[ENH] Add TimeSeries datatype as per new API (#2410)
This PR adds the new Add `TimeSeries` datatype and update datamodule with the new design. See #2407, #2398 - `TimeSeries` is now a standarised datatype. Updated the tests and renamed the file to `test_timeseries_v2` as this is no more a D1 layer! - Also, we now have `metadata` as a dataclass, instead of the `dict` to make it more user friendly. Updated tests also updates some old ones (and removes some tests like `init` checks - which are not needed i think?) This PR is aimed at `v2-dev` instead of `main`, so that we dont break current user code until this design is complete and stable. See #2407 for details. So I had to update the branch target in the workflows for the tests to run (which might fail due to the changes) ## Stack - `TimeSeries` - #2410 **<- This PR** - `Datamodule` - #2423 - `Forecaster` - #2434 ## Note The tutorials will fail for sure, will update the notebook in one of the branchs stacked over this PR. Still need to add the `Forecaster` and tests - will add the notebook and docs changes on the upcoming branches as the vignette is incomplete without `forecaster` which is not in this PR. ## Changes made - Changed the test file of `TimeSeries` from `test_d1` tro `test_timeseries_v2` - updates to `TimeSeries` - Now uses `TimeSeriesMetadata` as metadata inplace of a `dict` - Not a `Dataset` anymore - Added some auto-detection of cols - `target` is the last column of `data`. - `time` is the first column not already used as `group`, `target`, `static` or `weight`. If no such column exists, `ValueError` is raised - pass `time` explicitly. - `num` and `cat` are the dtype split over all columns that are not `group`, `time` or `weight` - data validation - `to_pandas` to create a df - `from_tensors` to create a `TimeSeries` class for predicitons - which lack a lot of metadata, so need special handling otherwise will interfere with the input data validation
1 parent 67c354f commit 1a08f32

8 files changed

Lines changed: 477 additions & 50 deletions

File tree

‎.github/workflows/test.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ name: Test
55

66
on:
77
push:
8-
branches: [main]
8+
branches: [v2-dev]
99
pull_request:
10-
branches: [main]
10+
branches: [v2-dev]
1111

1212
concurrency:
1313
group: ${{ github.workflow }}-${{ github.ref }}

‎pytorch_forecasting/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
GroupNormalizer,
1010
MultiNormalizer,
1111
NaNLabelEncoder,
12+
TimeSeries,
1213
TimeSeriesDataSet,
14+
TimeSeriesMetadata,
1315
)
1416
from pytorch_forecasting.metrics import (
1517
MAE,
@@ -66,7 +68,9 @@
6668
from pytorch_forecasting.utils._maint._show_versions import show_versions
6769

6870
__all__ = [
71+
"TimeSeries",
6972
"TimeSeriesDataSet",
73+
"TimeSeriesMetadata",
7074
"GroupNormalizer",
7175
"EncoderNormalizer",
7276
"NaNLabelEncoder",

‎pytorch_forecasting/data/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
into model-ready PyTorch datasets.
77
"""
88

9+
from pytorch_forecasting.data._metadata import TimeSeriesMetadata
910
from pytorch_forecasting.data.encoders import (
1011
EncoderNormalizer,
1112
GroupNormalizer,
@@ -19,6 +20,7 @@
1920
__all__ = [
2021
"TimeSeriesDataSet",
2122
"TimeSeries",
23+
"TimeSeriesMetadata",
2224
"NaNLabelEncoder",
2325
"GroupNormalizer",
2426
"TorchNormalizer",
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Typed schemas of the data layer."""
2+
3+
from dataclasses import dataclass, fields
4+
5+
6+
class _DictLike:
7+
"""Mapping protocol for the metadata dataclasses."""
8+
9+
def __getitem__(self, key):
10+
if key not in self:
11+
raise KeyError(key)
12+
return getattr(self, key)
13+
14+
def get(self, key, default=None):
15+
return getattr(self, key) if key in self else default
16+
17+
def keys(self):
18+
return [f.name for f in fields(self)]
19+
20+
def __contains__(self, key):
21+
return key in self.keys()
22+
23+
def __iter__(self):
24+
return iter(self.keys())
25+
26+
27+
@dataclass(frozen=True)
28+
class TimeSeriesMetadata(_DictLike):
29+
"""Schema of a :class:`~pytorch_forecasting.data.TimeSeries`.
30+
31+
Parameters
32+
----------
33+
cols : dict
34+
``{"y": [...], "x": [...], "st": [...]}``, names of the target, feature
35+
and static columns. List order is the column order of the corresponding
36+
tensor dimension, and must not be reordered.
37+
col_type : dict
38+
maps column name to ``"F"`` (numerical) or ``"C"`` (categorical).
39+
col_known : dict
40+
maps column name to ``"K"`` (known in the future) or ``"U"`` (unknown).
41+
is_prediction : bool, default=False
42+
whether the described object holds predictions rather than input data.
43+
"""
44+
45+
cols: dict[str, list[str]]
46+
col_type: dict[str, str]
47+
col_known: dict[str, str]
48+
is_prediction: bool = False
49+
50+
def __post_init__(self):
51+
"""Validation checks.
52+
53+
Raises
54+
------
55+
ValueError
56+
If ``cols`` does not have exactly the keys ``"y"``, ``"x"``, ``"st"``.
57+
"""
58+
missing = {"y", "x", "st"} - set(self.cols)
59+
if missing:
60+
raise ValueError(
61+
f"`cols` is missing required keys: {sorted(missing)}. It must "
62+
'map "y", "x" and "st" to lists of column names.'
63+
)

0 commit comments

Comments
 (0)