Merge branch 'tactical-forecasting' into main
This commit is contained in:
commit
f5ced933d6
9 changed files with 3965 additions and 12 deletions
34
README.md
34
README.md
|
@ -16,19 +16,39 @@ that iteratively build on each other.
|
||||||
### Data Cleaning
|
### Data Cleaning
|
||||||
|
|
||||||
The UDP provided its raw data as a PostgreSQL dump.
|
The UDP provided its raw data as a PostgreSQL dump.
|
||||||
This [notebook](https://nbviewer.jupyter.org/github/webartifex/urban-meal-delivery/blob/develop/research/clean_data.ipynb)
|
This [notebook](https://nbviewer.jupyter.org/github/webartifex/urban-meal-delivery/blob/main/research/01_clean_data.ipynb)
|
||||||
cleans the data extensively
|
cleans the data extensively
|
||||||
and maps them onto the [ORM models](https://github.com/webartifex/urban-meal-delivery/tree/develop/src/urban_meal_delivery/db)
|
and maps them onto the [ORM models](https://github.com/webartifex/urban-meal-delivery/tree/main/src/urban_meal_delivery/db)
|
||||||
defined in the `urban-meal-delivery` package
|
defined in the `urban-meal-delivery` package
|
||||||
that is developed in the [src/](https://github.com/webartifex/urban-meal-delivery/tree/develop/src) folder
|
that is developed in the [src/](https://github.com/webartifex/urban-meal-delivery/tree/main/src) folder
|
||||||
and contains all source code to drive the analyses.
|
and contains all source code to drive the analyses.
|
||||||
|
|
||||||
Due to a non-disclosure agreement with the UDP,
|
Due to a non-disclosure agreement with the UDP,
|
||||||
neither the raw nor the cleaned data are published as of now.
|
neither the raw nor the cleaned data are published as of now.
|
||||||
However, previews of the data can be seen throughout the [research/](https://github.com/webartifex/urban-meal-delivery/tree/develop/research) folder.
|
However, previews of the data can be seen throughout the [research/](https://github.com/webartifex/urban-meal-delivery/tree/main/research) folder.
|
||||||
|
|
||||||
|
|
||||||
### Real-time Demand Forecasting
|
### Tactical Demand Forecasting
|
||||||
|
|
||||||
|
Before any optimizations of the UDP's operations are done,
|
||||||
|
a **demand forecasting** system for *tactical* purposes is implemented.
|
||||||
|
To achieve that, the cities first undergo a **gridification** step
|
||||||
|
where each *pickup* location is assigned into a pixel on a "checker board"-like grid.
|
||||||
|
The main part of the source code that implements that is in this [file](https://github.com/webartifex/urban-meal-delivery/blob/main/src/urban_meal_delivery/db/grids.py#L60).
|
||||||
|
Visualizations of the various grids can be found in the [visualizations/](https://github.com/webartifex/urban-meal-delivery/tree/main/research/visualizations) folder
|
||||||
|
and in this [notebook](https://nbviewer.jupyter.org/github/webartifex/urban-meal-delivery/blob/main/research/03_grid_visualizations.ipynb).
|
||||||
|
|
||||||
|
Then, demand is aggregated on a per-pixel level
|
||||||
|
and different kinds of order time series are generated.
|
||||||
|
The latter are the input to different kinds of forecasting `*Model`s.
|
||||||
|
They all have in common that they predict demand into the *short-term* future (e.g., one hour)
|
||||||
|
and are thus used for tactical purposes, in particular predictive routing (cf., next section).
|
||||||
|
The details of how this works can be found in the first academic paper
|
||||||
|
published in the context of this research project
|
||||||
|
and titled "*Real-time Demand Forecasting for an Urban Delivery Platform*"
|
||||||
|
(cf., the [repository](https://github.com/webartifex/urban-meal-delivery-demand-forecasting) with the LaTeX files).
|
||||||
|
All demand forecasting related code is in the [forecasts/](https://github.com/webartifex/urban-meal-delivery/tree/main/src/urban_meal_delivery/forecasts) sub-package.
|
||||||
|
|
||||||
|
|
||||||
### Predictive Routing
|
### Predictive Routing
|
||||||
|
|
||||||
|
@ -51,11 +71,11 @@ and
|
||||||
`poetry install --extras research`
|
`poetry install --extras research`
|
||||||
|
|
||||||
The `--extras` option is necessary as the non-develop dependencies
|
The `--extras` option is necessary as the non-develop dependencies
|
||||||
are structured in the [pyproject.toml](https://github.com/webartifex/urban-meal-delivery/blob/develop/pyproject.toml) file
|
are structured in the [pyproject.toml](https://github.com/webartifex/urban-meal-delivery/blob/main/pyproject.toml) file
|
||||||
into dependencies related to only the `urban-meal-delivery` source code package
|
into dependencies related to only the `urban-meal-delivery` source code package
|
||||||
and dependencies used to run the [Jupyter](https://jupyter.org/) environment
|
and dependencies used to run the [Jupyter](https://jupyter.org/) environment
|
||||||
with the analyses.
|
with the analyses.
|
||||||
|
|
||||||
Contributions are welcome.
|
Contributions are welcome.
|
||||||
Use the [issues](https://github.com/webartifex/urban-meal-delivery/issues) tab.
|
Use the [issues](https://github.com/webartifex/urban-meal-delivery/issues) tab.
|
||||||
The project is licensed under the [MIT license](https://github.com/webartifex/urban-meal-delivery/blob/develop/LICENSE.txt).
|
The project is licensed under the [MIT license](https://github.com/webartifex/urban-meal-delivery/blob/main/LICENSE.txt).
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"- numeric columns are checked for plausibility\n",
|
"- numeric columns are checked for plausibility\n",
|
||||||
"- foreign key relationships are strictly enforced\n",
|
"- foreign key relationships are strictly enforced\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The structure of the data can be viewed at the [ORM layer](https://github.com/webartifex/urban-meal-delivery/tree/develop/src/urban_meal_delivery/db) in the package."
|
"The structure of the data can be viewed at the [ORM layer](https://github.com/webartifex/urban-meal-delivery/tree/main/src/urban_meal_delivery/db) in the package."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
227
research/04_visualizing_restaurants.ipynb
Normal file
227
research/04_visualizing_restaurants.ipynb
Normal file
File diff suppressed because one or more lines are too long
230
research/05_visualizing_customers.ipynb
Normal file
230
research/05_visualizing_customers.ipynb
Normal file
File diff suppressed because one or more lines are too long
1531
research/06_tactical_demand_forecasting.ipynb
Normal file
1531
research/06_tactical_demand_forecasting.ipynb
Normal file
File diff suppressed because it is too large
Load diff
1940
research/07_visualizing_demand_forecasting.ipynb
Normal file
1940
research/07_visualizing_demand_forecasting.ipynb
Normal file
File diff suppressed because one or more lines are too long
|
@ -49,9 +49,9 @@ class Config:
|
||||||
TIME_STEPS = [60]
|
TIME_STEPS = [60]
|
||||||
|
|
||||||
# Training horizons (in full weeks) used to train the forecasting models.
|
# Training horizons (in full weeks) used to train the forecasting models.
|
||||||
# For now, we only use 8 weeks as that was the best performing in
|
# For now, we only use 7 and 8 weeks as that was the best performing in
|
||||||
# a previous study (note:4f79e8fa).
|
# a previous study (note:4f79e8fa).
|
||||||
TRAIN_HORIZONS = [8]
|
TRAIN_HORIZONS = [7, 8]
|
||||||
|
|
||||||
# The demand forecasting methods used in the simulations.
|
# The demand forecasting methods used in the simulations.
|
||||||
FORECASTING_METHODS = ['hets', 'rtarima']
|
FORECASTING_METHODS = ['hets', 'rtarima']
|
||||||
|
|
|
@ -105,7 +105,12 @@ def tactical_heuristic( # noqa:C901,WPS213,WPS216,WPS231
|
||||||
# Continue with forecasting on the day the last prediction was made ...
|
# Continue with forecasting on the day the last prediction was made ...
|
||||||
last_predict_at = ( # noqa:ECE001
|
last_predict_at = ( # noqa:ECE001
|
||||||
db.session.query(func.max(db.Forecast.start_at))
|
db.session.query(func.max(db.Forecast.start_at))
|
||||||
|
.join(db.Pixel, db.Forecast.pixel_id == db.Pixel.id)
|
||||||
|
.join(db.Grid, db.Pixel.grid_id == db.Grid.id)
|
||||||
.filter(db.Forecast.pixel == pixel)
|
.filter(db.Forecast.pixel == pixel)
|
||||||
|
.filter(db.Grid.side_length == side_length)
|
||||||
|
.filter(db.Forecast.time_step == time_step)
|
||||||
|
.filter(db.Forecast.train_horizon == train_horizon)
|
||||||
.first()
|
.first()
|
||||||
)[0]
|
)[0]
|
||||||
# ... or start `train_horizon` weeks after the first `Order`
|
# ... or start `train_horizon` weeks after the first `Order`
|
||||||
|
|
|
@ -542,9 +542,9 @@ class OrderHistory:
|
||||||
pixel_id=pixel_id, predict_day=predict_day, train_horizon=train_horizon,
|
pixel_id=pixel_id, predict_day=predict_day, train_horizon=train_horizon,
|
||||||
)
|
)
|
||||||
|
|
||||||
# For now, we only make forecasts with 8 weeks
|
# For now, we only make forecasts with 7 and 8 weeks
|
||||||
# as the training horizon (note:4f79e8fa).
|
# as the training horizon (note:4f79e8fa).
|
||||||
if train_horizon == 8:
|
if train_horizon == 7 or train_horizon == 8:
|
||||||
if add >= 25: # = "high demand"
|
if add >= 25: # = "high demand"
|
||||||
return models.HorizontalETSModel(order_history=self)
|
return models.HorizontalETSModel(order_history=self)
|
||||||
elif add >= 10: # = "medium demand"
|
elif add >= 10: # = "medium demand"
|
||||||
|
|
Loading…
Reference in a new issue