nehemiah/src/urban_meal_delivery/forecasts/models/tactical/other.py

75 lines
2.6 KiB
Python
Raw Normal View History

"""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