Skip to content

Enhance task cardinality and introduce exogenous forecasting - #860

Open
Irozuku wants to merge 2 commits into
feat/per-type-task-cardinalityfrom
feat/exogenous-forecasting-task
Open

Enhance task cardinality and introduce exogenous forecasting#860
Irozuku wants to merge 2 commits into
feat/per-type-task-cardinalityfrom
feat/exogenous-forecasting-task

Conversation

@Irozuku

@Irozuku Irozuku commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

ForecastingTask offers a model the date and nothing else, so a series driven by something measurable (a price, a promotion, the temperature) could only be forecast from its own history.

ExogenousForecastingTask takes the same date column with one or more numeric variables beside it, which the per group column contract from the previous branch can now express: Date with cardinality 1, and Float/Integer with cardinality {min: 1, max: "n"}.

It is a separate task rather than a wider ForecastingTask so that each stays honest about what it offers. A model reading only a date would silently drop the variables the user selected, and a model needing explanatory variables cannot be fitted without them.

What the two tasks share moves to TimeSeriesTask.

image

Type of Change

Check all that apply like this [x]:

  • Backend change
  • Frontend change
  • CI / Workflow change
  • Build / Packaging change
  • Bug fix
  • Documentation

Changes (by file)

  • DashAI/back/tasks/time_series_task.py: new. Holds what both forecasting tasks share: PREDICTS_FORWARD_ONLY, sorting the rows by date, reporting no labels, and passing predictions through undecoded. _date_column finds the date column by type rather than by position, since with variables alongside it need not come first. Added _date_column; _sort_by_date, process_predictions and num_labels moved here unchanged from forecasting_task.py.
  • DashAI/back/tasks/exogenous_forecasting_task.py: new. The task itself: metadata, description and display name in the five locales.
  • DashAI/back/tasks/forecasting_task.py: reduced to its metadata and its strings, now subclassing TimeSeriesTask. Metadata restated in the group form, which yields an identical flat view.
  • DashAI/back/initial_components.py: registers ExogenousForecastingTask.
  • DashAI/back/splitters/temporal_holdout.py, DashAI/back/splitters/rolling_origin.py: serve the new task too.
  • DashAI/back/evaluation/forecasting_holdout.py, DashAI/back/evaluation/forecasting_cv.py: same.
  • DashAI/back/metrics/regression_metric.py: same, which carries MAE, RMSE, MAPE and SMAPE across.
  • DashAI/back/optimizers/optuna_optimizer.py: same.
  • tests/back/tasks/test_exogenous_forecasting_task.py: new. Covers one variable and many, the date column arriving last, a date on its own being refused, variables without a date being refused, a second date column, a text variable, two outputs, the rows coming back in date order, and the metadata.
  • tests/back/evaluation/test_forecasting_strategies.py, tests/back/splitters/test_splitter_partitioning.py: compatibility assertions widened, and the shuffling holdout splitter is now asserted to exclude both forecasting tasks.

Testing

  • The task has no models until the next branch, so it appears in the UI with an empty model list on this one.

Notes

The shared base is named TimeSeriesTask, not BaseForecastingTask. ComponentRegistry._get_base_type selects MRO ancestors whose name contains "Base" and refuses a component with more than one, so an intermediate class with Base in its name breaks registration of every task under it. This follows the convention already used for models, where ForecastingModel sits under BaseModel.

Non forecasting splitters and evaluation strategies list their tasks explicitly, so none of them picked up the new task by accident.

A task stated its column contract as one list of allowed types plus one
cardinality for the whole side. That cannot express "one date column and
any number of numeric ones": a single total accepts two dates and no
numbers just as readily.

Tasks now declare a list of groups, each naming interchangeable types and
how many columns of that set it takes, the way BaseGenerativeTask states
its per-type counts. Cardinality may be an exact int, "n", or a
{min, max} range. The older two-key spelling is read as a single group,
so existing tasks and plugins are unchanged, and get_metadata reports
both views so every current consumer keeps reading what it read before.

The column picker banner renders one requirement line per group.
ForecastingTask offers a model the date and nothing else, so a series
that is driven by something measurable, a price, a promotion, the
temperature, could only be forecast from its own history.

ExogenousForecastingTask takes the same date column with one or more
numeric variables beside it, which the per-group column contract can now
express. It is a separate task rather than a wider ForecastingTask so
that each stays honest about what it offers: a model reading only a date
would silently drop the variables the user selected, and a model needing
them cannot be fitted without them.

What the two share, sorting the rows by date and reporting no labels,
moves to TimeSeriesTask. The date column is now found by type rather
than by position, since with variables alongside it need not come first.

The temporal splitters, the forecasting evaluation strategies, the
regression metrics and the Optuna optimizer serve the new task too.
@Irozuku Irozuku added the back Backend work label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

back Backend work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant