urban-meal-delivery/tests/forecasts/timify/test_avg_daily_demand.py
Alexander Hess 23391c2fa4
Adjust OrderHistory.choose_tactical_model() heuristic
- use the `HorizontalSMAModel` for low demand
- use the `TrivialModel` for no demand
2021-02-02 15:20:02 +01:00

143 lines
4.9 KiB
Python

"""Tests for the `OrderHistory.avg_daily_demand()` and ...
`OrderHistory.choose_tactical_model()` methods.
We test both methods together as they take the same input and are really
two parts of the same conceptual step.
"""
import pytest
from tests import config as test_config
from urban_meal_delivery.forecasts import models
class TestAverageDailyDemand:
"""Tests for the `OrderHistory.avg_daily_demand()` method."""
def test_avg_daily_demand_with_constant_demand(
self, order_history, good_pixel_id, predict_at,
):
"""The average daily demand must be the number of time steps ...
... if the demand is `1` at each time step.
Note: The `order_history` fixture assumes `12` time steps per day as it
uses `LONG_TIME_STEP=60` as the length of a time step.
"""
result = order_history.avg_daily_demand(
pixel_id=good_pixel_id,
predict_day=predict_at.date(),
train_horizon=test_config.LONG_TRAIN_HORIZON,
)
assert result == 12.0
def test_avg_daily_demand_with_no_demand(
self, order_history, good_pixel_id, predict_at,
):
"""Without demand, the average daily demand must be `0.0`."""
order_history._data.loc[:, 'n_orders'] = 0
result = order_history.avg_daily_demand(
pixel_id=good_pixel_id,
predict_day=predict_at.date(),
train_horizon=test_config.LONG_TRAIN_HORIZON,
)
assert result == 0.0
class TestChooseTacticalModel:
"""Tests for the `OrderHistory.choose_tactical_model()` method."""
def test_best_model_with_high_demand(
self, order_history, good_pixel_id, predict_at,
):
"""With high demand, the average daily demand is `.>= 25.0`."""
# With 12 time steps per day, the ADD becomes `36.0`.
order_history._data.loc[:, 'n_orders'] = 3
result = order_history.choose_tactical_model(
pixel_id=good_pixel_id,
predict_day=predict_at.date(),
train_horizon=test_config.LONG_TRAIN_HORIZON,
)
assert isinstance(result, models.HorizontalETSModel)
def test_best_model_with_medium_demand(
self, order_history, good_pixel_id, predict_at,
):
"""With medium demand, the average daily demand is `>= 10.0` and `< 25.0`."""
# With 12 time steps per day, the ADD becomes `24.0`.
order_history._data.loc[:, 'n_orders'] = 2
result = order_history.choose_tactical_model(
pixel_id=good_pixel_id,
predict_day=predict_at.date(),
train_horizon=test_config.LONG_TRAIN_HORIZON,
)
assert isinstance(result, models.HorizontalETSModel)
def test_best_model_with_low_demand(
self, order_history, good_pixel_id, predict_at,
):
"""With low demand, the average daily demand is `>= 2.5` and `< 10.0`."""
# With 12 time steps per day, the ADD becomes `12.0` ...
data = order_history._data
data.loc[:, 'n_orders'] = 1
# ... and we set three additional time steps per day to `0`.
data.loc[ # noqa:ECE001
# all `Pixel`s, all `Order`s in time steps starting at 11 am
(slice(None), slice(data.index.levels[1][0], None, 12)),
'n_orders',
] = 0
data.loc[ # noqa:ECE001
# all `Pixel`s, all `Order`s in time steps starting at 12 am
(slice(None), slice(data.index.levels[1][1], None, 12)),
'n_orders',
] = 0
data.loc[ # noqa:ECE001
# all `Pixel`s, all `Order`s in time steps starting at 1 pm
(slice(None), slice(data.index.levels[1][2], None, 12)),
'n_orders',
] = 0
result = order_history.choose_tactical_model(
pixel_id=good_pixel_id,
predict_day=predict_at.date(),
train_horizon=test_config.LONG_TRAIN_HORIZON,
)
assert isinstance(result, models.HorizontalSMAModel)
def test_best_model_with_no_demand(
self, order_history, good_pixel_id, predict_at,
):
"""Without demand, the average daily demand is `< 2.5`."""
order_history._data.loc[:, 'n_orders'] = 0
result = order_history.choose_tactical_model(
pixel_id=good_pixel_id,
predict_day=predict_at.date(),
train_horizon=test_config.LONG_TRAIN_HORIZON,
)
assert isinstance(result, models.TrivialModel)
def test_best_model_for_unknown_train_horizon(
self, order_history, good_pixel_id, predict_at, # noqa:RST215
):
"""For `train_horizon`s not included in the rule-based system ...
... the method raises a `RuntimeError`.
"""
with pytest.raises(RuntimeError, match='no rule'):
order_history.choose_tactical_model(
pixel_id=good_pixel_id,
predict_day=predict_at.date(),
train_horizon=test_config.SHORT_TRAIN_HORIZON,
)