Alexander Hess
23391c2fa4
- use the `HorizontalSMAModel` for low demand - use the `TrivialModel` for no demand
143 lines
4.9 KiB
Python
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,
|
|
)
|