Add TrivialModel
- the trivial model simply predicts `0` demand for all time steps
This commit is contained in:
parent
015d304306
commit
6fd16f2a6c
3 changed files with 77 additions and 0 deletions
|
@ -32,5 +32,6 @@ A future `planning` sub-package will contain the `*Model`s used to plan the
|
||||||
from urban_meal_delivery.forecasts.models.base import ForecastingModelABC
|
from urban_meal_delivery.forecasts.models.base import ForecastingModelABC
|
||||||
from urban_meal_delivery.forecasts.models.tactical.horizontal import HorizontalETSModel
|
from urban_meal_delivery.forecasts.models.tactical.horizontal import HorizontalETSModel
|
||||||
from urban_meal_delivery.forecasts.models.tactical.horizontal import HorizontalSMAModel
|
from urban_meal_delivery.forecasts.models.tactical.horizontal import HorizontalSMAModel
|
||||||
|
from urban_meal_delivery.forecasts.models.tactical.other import TrivialModel
|
||||||
from urban_meal_delivery.forecasts.models.tactical.realtime import RealtimeARIMAModel
|
from urban_meal_delivery.forecasts.models.tactical.realtime import RealtimeARIMAModel
|
||||||
from urban_meal_delivery.forecasts.models.tactical.vertical import VerticalARIMAModel
|
from urban_meal_delivery.forecasts.models.tactical.vertical import VerticalARIMAModel
|
||||||
|
|
75
src/urban_meal_delivery/forecasts/models/tactical/other.py
Normal file
75
src/urban_meal_delivery/forecasts/models/tactical/other.py
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
"""Forecasting `*Model`s to predict demand for tactical purposes ...
|
||||||
|
|
||||||
|
... that cannot be classified into either "horizontal", "vertical",
|
||||||
|
or "real-time".
|
||||||
|
""" # noqa:RST215
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from urban_meal_delivery import db
|
||||||
|
from urban_meal_delivery.forecasts.models import base
|
||||||
|
|
||||||
|
|
||||||
|
class TrivialModel(base.ForecastingModelABC):
|
||||||
|
"""A trivial model predicting `0` demand.
|
||||||
|
|
||||||
|
No need to distinguish between a "horizontal", "vertical", or
|
||||||
|
"real-time" model here as all give the same prediction for all time steps.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = 'trivial'
|
||||||
|
|
||||||
|
def predict(
|
||||||
|
self, pixel: db.Pixel, predict_at: dt.datetime, train_horizon: int,
|
||||||
|
) -> pd.DataFrame:
|
||||||
|
"""Predict demand for a time step.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pixel: pixel in which the prediction is made
|
||||||
|
predict_at: time step (i.e., "start_at") to make the prediction for
|
||||||
|
train_horizon: weeks of historic data used to predict `predict_at`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
actual order counts (i.e., the "actual" column) and
|
||||||
|
point forecasts (i.e., the "prediction" column);
|
||||||
|
this model does not support confidence intervals;
|
||||||
|
contains one row for the `predict_at` time step
|
||||||
|
|
||||||
|
# noqa:DAR401 RuntimeError
|
||||||
|
"""
|
||||||
|
# Generate the historic order time series mainly to check if a valid
|
||||||
|
# `training_ts` exists (i.e., the demand history is long enough).
|
||||||
|
_, frequency, actuals_ts = self._order_history.make_horizontal_ts(
|
||||||
|
pixel_id=pixel.id, predict_at=predict_at, train_horizon=train_horizon,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Sanity checks.
|
||||||
|
if frequency != 7: # pragma: no cover
|
||||||
|
raise RuntimeError('`frequency` should be `7`')
|
||||||
|
if len(actuals_ts) != 1: # pragma: no cover
|
||||||
|
raise RuntimeError(
|
||||||
|
'the trivial model can only predict one step into the future',
|
||||||
|
)
|
||||||
|
|
||||||
|
# The "prediction" is simply `0.0`.
|
||||||
|
predictions = pd.DataFrame(
|
||||||
|
data={
|
||||||
|
'actual': actuals_ts,
|
||||||
|
'prediction': 0.0,
|
||||||
|
'low80': float('NaN'),
|
||||||
|
'high80': float('NaN'),
|
||||||
|
'low95': float('NaN'),
|
||||||
|
'high95': float('NaN'),
|
||||||
|
},
|
||||||
|
index=actuals_ts.index,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Sanity checks.
|
||||||
|
if predictions['actual'].isnull().any(): # pragma: no cover
|
||||||
|
raise RuntimeError('missing actuals in trivial model')
|
||||||
|
if predict_at not in predictions.index: # pragma: no cover
|
||||||
|
raise RuntimeError('missing prediction for `predict_at`')
|
||||||
|
|
||||||
|
return predictions
|
|
@ -14,6 +14,7 @@ MODELS = (
|
||||||
models.HorizontalSMAModel,
|
models.HorizontalSMAModel,
|
||||||
models.RealtimeARIMAModel,
|
models.RealtimeARIMAModel,
|
||||||
models.VerticalARIMAModel,
|
models.VerticalARIMAModel,
|
||||||
|
models.TrivialModel,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue