# Next-day bike-demand forecasting

**Business question:** Can a model using historical demand and calendar information improve next-day hourly demand planning?

A histogram gradient-boosting model reduced mean absolute error by **11.5%** against the strongest baseline chosen on validation data. The held-out evaluation covers 2,888 observed hours from September through December 2012. This supports further operational evaluation; no cost savings or real deployment are claimed.

## Results on held-out dates

| Model | MAE | RMSE | WAPE | Bias |
| --- | --- | --- | --- | --- |
| Calendar average | 92.80 | 137.25 | 38.6% | -58.61 |
| Previous day | 79.63 | 133.14 | 33.1% | 1.63 |
| Previous week | 68.09 | 115.81 | 28.3% | 10.90 |
| Gradient boosting | 60.26 | 96.36 | 25.1% | -28.18 |

MAE and RMSE are rentals per observed hour. WAPE is total absolute error divided by total rentals; it is not forecast accuracy. Bias is predicted minus observed demand. The model's negative bias of -28.18 rentals/hour means it systematically underestimates demand in this period. That matters when insufficient capacity is more costly than spare capacity.

![Model comparison](reports/figures/model_comparison.png)

The previous week baseline was selected using earlier validation dates. Both model configuration and baseline selection were fixed before scoring the final period. An exploratory bootstrap resampling whole days estimates a 95% interval of 0.16 to 15.46 rentals/hour for the MAE reduction. The interval ignores dependence between consecutive days and is not a calibrated prediction interval.

## Forecast contract

At midnight on a target date, predict each of that day's 24 hours using finalized counts from earlier days and known calendar information. For a 23:00 prediction, the latest same-hour lag comes from 23:00 on the previous day. This assumes the source system has finalized that count by midnight.

The test simulates successive daily forecast runs with a fixed model. Actual counts from earlier test days become available to later test-day features. It is not a four-month forecast made from one September origin.

![An illustrative test week](reports/figures/forecast_week.png)

This is the first full Monday–Sunday week in the holdout, selected by date rather than model performance. Full predictions are saved so all dates can be inspected. The highest monthly MAE occurs in **2012-10**, at **76.01 rentals/hour**. Monthly and commute-hour errors are available in [diagnostics.csv](outputs/diagnostics.csv).

## How it works

1. Validate 17,379 source observations and reconcile hourly totals to the separate daily source.
2. Reindex to 17,544 calendar hours. Preserve 165 absent targets as unknown; never turn them into zero rentals.
3. Build calendar features, lags at 24/48/168/336 hours, a mean of seven prior same-hour counts, and the previous calendar day's mean. Drop the first 14 days for warm-up. Impute missing features using training data only.
4. Compare two model configurations in three expanding-window validation blocks: March–April, May–June and July–August 2012. Use Poisson loss and disable randomized early stopping.
5. Choose the model and strongest baseline by pooled validation MAE. Fit on 14,167 pre-September observations, then evaluate once on later dates.

Realized target-hour weather is unavailable at forecast time and is excluded. `casual` and `registered` sum to the target and are also excluded. This makes feature availability explicit instead of allowing those columns to reveal the outcome.

![Historical hourly demand](reports/figures/hourly_demand.png)

This historical profile uses pre-test observations only. Working-day and non-working-day patterns help motivate the calendar features; their association does not prove a causal effect.

## Limits and operational next step

The source measures realized system rentals, not unmet demand. There are no station-level inventories, allocation constraints or operating costs, so this study cannot prescribe station rebalancing or calculate profit. Missing target hours are omitted from scoring; unusual unobserved hours may bias the result. The data are historical and should not be used for current operations without retraining and validation on recent observations.

Before an operational pilot, measure data latency, compare against the operator's current forecast, define the relative cost of underprediction, and examine peak-hour service levels. Add weather only if historical forecasts available at the actual prediction time can be obtained. See the [model card](MODEL_CARD.md) for the full contract and limitations.

## Reproduce and inspect

From the repository root:

```powershell
python scripts/download_data.py bikes
python projects/02-bike-demand-forecasting/src/analysis.py
python scripts/build_case_studies.py
```

The report builder requires outputs from both projects. Or open [analysis.ipynb](analysis.ipynb) and run all cells. The notebook trains and evaluates the candidates, saves predictions and displays the figures.

- [Analysis code](src/analysis.py), [model card](MODEL_CARD.md) and [tests](../../tests/test_analysis.py)
- [Validation scores](outputs/validation.csv), [baseline validation scores](outputs/baseline_validation.csv) and [test scores](outputs/test_scores.csv)
- [Every held-out prediction](outputs/test_predictions.csv) and [run metadata](outputs/metrics.json)

The generated model artifact includes its ordered feature list and training cutoff. It remains local and is reproducible; it is not a deployed service.

## Explain it in an interview

Explain why random train/test splits are misleading here, why a row-based shift gives wrong lags when hours are missing, and why using already observed test-day history in a later daily forecast is valid. Discuss why lower average error can still leave an operational problem when forecasts systematically underpredict peaks.

## Source

[UCI Bike Sharing](https://archive.ics.uci.edu/dataset/275/bike+sharing+dataset), Fanaee-T, H. (2013), [DOI: 10.24432/C5W894](https://doi.org/10.24432/C5W894), CC BY 4.0. Capital Bikeshare hourly and daily observations from 2011–2012. Data were transformed into historical features for this study. Download provenance and hashes are in [source_manifest.json](data/source_manifest.json).
