Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions flexmeasures/data/models/forecasting/custom_models/lgbm_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def __init__(
use_past_covariates=False,
use_future_covariates=False,
ensure_positive=False,
seasonal_lag_steps=24,
fallback_lag_steps=24,
training_sample_count=None,
min_samples_per_horizon=2,
Comment on lines +33 to +36
):

if models_params is None:
Expand All @@ -52,6 +56,13 @@ def __init__(
}
else:
self.models_params = models_params
if (
training_sample_count is not None
and training_sample_count - seasonal_lag_steps - (max_forecast_horizon - 1)
< min_samples_per_horizon
):
seasonal_lag_steps = fallback_lag_steps
self.seasonal_lag_steps = seasonal_lag_steps
super().__init__(
max_forecast_horizon=max_forecast_horizon,
probabilistic=probabilistic,
Expand All @@ -70,21 +81,21 @@ def _setup(self) -> None:

# Lag features are dynamically set based on the forecast horizon
lag = (
24
self.seasonal_lag_steps
- ( # temporarily make the adaptation to the sensor resolution; To do: inlude a list of seasonal lags to include, given as pd.timedelta objects
horizon % 24
horizon % self.seasonal_lag_steps
)
) # Adjust to repeat the lag structure every 24 hours
lags = [-1, -lag, -lag - 1]

# Special cases for lags
if (
horizon == 0
or horizon % 24 == 0
or horizon % self.seasonal_lag_steps == 0
or horizon == self.max_forecast_horizon - 1
):
lags = [-1, -24]
elif horizon % 24 == 23:
lags = [-1, -self.seasonal_lag_steps]
elif horizon % self.seasonal_lag_steps == self.seasonal_lag_steps - 1:
lags = [-1, -2]

# lags = list(range(-1, -25, -1)) # todo: consider letting the model figure out which lags are important
Expand Down
6 changes: 5 additions & 1 deletion flexmeasures/data/models/forecasting/pipelines/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pickle
import warnings
import logging
from datetime import datetime
from datetime import datetime, timedelta

from darts import TimeSeries

Expand Down Expand Up @@ -126,6 +126,10 @@ def run(self, counter: int):
use_past_covariates=past_covariates_list is not None,
use_future_covariates=future_covariates_list is not None,
ensure_positive=self.ensure_positive,
seasonal_lag_steps=max(
int(timedelta(days=1) / self.target_sensor.event_resolution), 1
),
Comment on lines +129 to +131
training_sample_count=len(y_train),
)
}

Expand Down
21 changes: 21 additions & 0 deletions flexmeasures/data/tests/test_forecasting_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from marshmallow import ValidationError

from flexmeasures.data.models.forecasting.custom_models.lgbm_model import CustomLGBM
from flexmeasures.data.models.data_sources import DataSource
from flexmeasures.data.models.forecasting.exceptions import NotEnoughDataException
from flexmeasures.data.models.forecasting.pipelines.base import BasePipeline
Expand All @@ -22,6 +23,26 @@
from flexmeasures.data.services.forecasting import handle_forecasting_exception


def test_custom_lgbm_falls_back_when_daily_lag_is_under_sampled():
"""Short histories should keep the old lag pattern instead of failing."""
under_sampled_model = CustomLGBM(
max_forecast_horizon=192,
probabilistic=False,
seasonal_lag_steps=96,
training_sample_count=288,
)
assert under_sampled_model.models[96].lags["target"] == [-24, -1]
assert under_sampled_model.models[-1].lags["target"] == [-24, -1]

sufficiently_sampled_model = CustomLGBM(
max_forecast_horizon=192,
probabilistic=False,
seasonal_lag_steps=96,
training_sample_count=384,
)
assert sufficiently_sampled_model.models[-1].lags["target"] == [-96, -1]


@pytest.mark.parametrize(
["config", "params", "as_job", "expected_error"],
[
Expand Down
Loading