1- from typing import TYPE_CHECKING , List , Union
2-
31from DashAI .back .core .utils import MultilingualString
4- from DashAI .back .tasks .base_task import BaseTask
2+ from DashAI .back .tasks .time_series_task import TimeSeriesTask
53from DashAI .back .types .value_types import Date , Float , Integer
64
7- if TYPE_CHECKING :
8- from datasets import DatasetDict
9- from numpy import ndarray
10-
11- from DashAI .back .dataloaders .classes .dashai_dataset import DashAIDataset
12-
135
14- class ForecastingTask (BaseTask ):
6+ class ForecastingTask (TimeSeriesTask ):
157 """Task for predicting the future values of a single time series.
168
179 The input is one ``Date`` column and the output is one numeric column: the
@@ -20,7 +12,9 @@ class ForecastingTask(BaseTask):
2012
2113 That restriction is the point. Models that take a date and nothing more,
2214 such as ARIMA or exponential smoothing, are a different family from models
23- that also take explanatory variables.
15+ that also take explanatory variables. Those belong to
16+ :class:`ExogenousForecastingTask`, which takes the same date column with
17+ any number of numeric variables beside it.
2418
2519 Two routes lead to a forecast in DashAI, and this is only one of them. The
2620 other is ``TimeSeriesWindowConverter``, which reshapes the same data into
@@ -29,8 +23,6 @@ class ForecastingTask(BaseTask):
2923 directly and cannot be expressed that way.
3024 """
3125
32- PREDICTS_FORWARD_ONLY : bool = True
33-
3426 DESCRIPTION : str = MultilingualString (
3527 en = (
3628 "Predict the future values of a time series from its own history. "
@@ -66,121 +58,6 @@ class ForecastingTask(BaseTask):
6658 )
6759
6860 metadata : dict = {
69- "inputs_types" : [Date ],
70- "outputs_types" : [Float , Integer ],
71- "inputs_cardinality" : 1 ,
72- "outputs_cardinality" : 1 ,
61+ "inputs" : [{"types" : [Date ], "cardinality" : 1 }],
62+ "outputs" : [{"types" : [Float , Integer ], "cardinality" : 1 }],
7363 }
74-
75- def prepare_for_task (
76- self ,
77- dataset : Union ["DatasetDict" , "DashAIDataset" ],
78- input_columns : List [str ],
79- output_columns : List [str ],
80- ) -> "DashAIDataset" :
81- """Convert the dataset to a DashAIDataset and validate its types.
82-
83- Parameters
84- ----------
85- dataset : DatasetDict or DashAIDataset
86- Dataset to prepare.
87- input_columns : list of str
88- The single date column.
89- output_columns : list of str
90- The single numeric column holding the series.
91-
92- Returns
93- -------
94- DashAIDataset
95- Dataset with validated types, in date order.
96- """
97- prepared = super ().prepare_for_task (dataset , input_columns , output_columns )
98- return self ._sort_by_date (prepared , input_columns [0 ])
99-
100- @staticmethod
101- def _sort_by_date (dataset : "DashAIDataset" , date_column : str ) -> "DashAIDataset" :
102- """Put the rows in date order.
103-
104- Everything downstream reads row order as time order and none of it
105- checks: the temporal splitter carves its partitions by position, and
106- the models hand their values to statsmodels in the order they arrive.
107- A file that is not sorted by its date column therefore produces
108- partitions that are not periods of time and a model fitted on a
109- scrambled series, with nothing reporting a problem.
110-
111- This is the task's job rather than the splitter's. The splitter is
112- handed the selected input columns, which on the windowed route through
113- ``TimeSeriesWindowConverter`` are lag columns with no date among them.
114-
115- Sorting reads the format the column declares. Text order only matches
116- time order for ISO layouts: as text, "01/02/2020" precedes
117- "31/01/2020" while following it in time.
118-
119- Parameters
120- ----------
121- dataset : DashAIDataset
122- The validated dataset.
123- date_column : str
124- The single input column, which the task has already checked is a
125- ``Date``.
126-
127- Returns
128- -------
129- DashAIDataset
130- The same rows and types, ordered by date.
131- """
132- from DashAI .back .dataloaders .classes .dashai_dataset import to_dashai_dataset
133- from DashAI .back .types .date_utils import DEFAULT_DATE_FORMAT , parse_date_column
134-
135- date_format = (
136- getattr (dataset .types [date_column ], "format" , None ) or DEFAULT_DATE_FORMAT
137- )
138- frame = dataset .to_pandas ()
139- order = parse_date_column (frame [date_column ], date_format ).sort_values ().index
140-
141- if list (order ) == list (frame .index ):
142- return dataset
143-
144- return to_dashai_dataset (
145- frame .loc [order ].reset_index (drop = True ), types = dict (dataset .types )
146- )
147-
148- def process_predictions (
149- self , dataset : "DashAIDataset" , predictions : "ndarray" , output_column : str
150- ):
151- """Return the forecast values unchanged.
152-
153- Parameters
154- ----------
155- dataset : DashAIDataset
156- Dataset used for training.
157- predictions : np.ndarray
158- Predictions from the model.
159- output_column : str
160- Output column.
161-
162- Returns
163- -------
164- np.ndarray
165- The predictions as they were produced. A forecast is already a
166- number on the scale of the series, so there is nothing to decode.
167- """
168- return predictions
169-
170- def num_labels (self , dataset : "DashAIDataset" , output_column : str ) -> int | None :
171- """Report that this task has no labels.
172-
173- Parameters
174- ----------
175- dataset : DashAIDataset
176- Dataset used for training.
177- output_column : str
178- Output column.
179-
180- Returns
181- -------
182- int | None
183- Always ``None``: the output is continuous, so there is no class
184- count for a model to size itself against.
185- """
186- return None
0 commit comments