Trajectory-aware pipeline forecasting cumulative COVID-19 deaths for ~180 countries across a multi-week horizon. The pipeline does not apply one strategy to all — it first classifies each country by its death trajectory, then routes it to a matched forecasting approach.
Forecast cumulative COVID-19 deaths for approximately 180 countries across a multi-week horizon. Data came from Zindi and Johns Hopkins CSSE time series. The challenge was that no single forecasting strategy works for all countries simultaneously — a decelerating country in Europe behaves completely differently from a surging country with 7 days of data.
An additional complication: irregular reporting. Many countries missed reporting for several consecutive days, then submitted catch-up corrections that created artificial spikes. Standard time series models treat those spikes as real signal, producing unstable forecasts.
Instead of fitting one model globally, the pipeline first assigns each country a trajectory class, then routes it to the forecasting strategy most appropriate for that trajectory.
For each country, compute the rate-of-change in daily deaths across two time windows: 6-day and 10-day. This produces a per-country slope signal.
# Vectorized elasticity: rate of change in daily deaths
jh[, elas := {
prev <- shift(death_dif, 1L, fill = 0)
denom <- fifelse(prev == 0, prev + 1, prev)
(death_dif - prev) / denom
}]
Each country is assigned one of four classes based on its elasticity signal and death volume.
Each forecast runs on multiple data versions — original, spike-smoothed (threshold 0.5), and more aggressively smoothed (threshold 0.33). The best version is selected per country based on validation MAE.
Irregular reporting (missed days followed by catch-up corrections) creates artificial spikes that distort time series models. Two thresholds (0.5 and 0.33) were applied to redistribute those spikes before fitting. The best-smoothed version was selected per country via validation — not applied uniformly.
For REST and NEAR_ZERO countries, models were evaluated on a held-out 5–7 day window within the training data. The model with the lowest RMSE on that validation window was used — not the best CV score across the full series. This prevents overfitting to a globally optimal but locally incorrect model.
For DOWN countries, the pipeline selects the model with the minimum cumulative prediction rather than best fit. This encodes an epidemiological prior: a decelerating trend should not suddenly reverse in the forecast. A forecast that is slightly too low is far less damaging than one that artificially inflates projections for a country already past its peak.
Three transformation strategies were tested per country: raw values, log transformation (to stabilize variance), and diff(log(x)) combined (to handle both variance and trend). The right transformation depends on the trajectory class and smoothing version — so the selection is done in the inner loop, not as a global choice.
├── 00_config.R # Libraries, dates, helper functions ├── 01_data_loading.R # Load Zindi + Johns Hopkins data ├── 02_elasticity_analysis.R # Compute elasticity, classify countries ├── 03_data_preprocessing.R # Negative value cleaning, spike smoothing ├── 04_time_series_models.R # ETS, ARIMA, TBATS model functions ├── 05_forecast_down.R # DOWN countries (damped, conservative) ├── 06_forecast_up.R # UP countries (undamped, median) ├── 07_forecast_near_zero.R # NEAR_ZERO countries (validated) ├── 08_forecast_rest.R # REST countries (validation-based) ├── 09_merge_and_submit.R # Merge all forecasts, generate CSV └── MAIN.R # Full pipeline orchestration
The main insight of this project is that heterogeneous forecasting — routing different series to different strategies — consistently outperforms applying a single best model globally. The time spent on trajectory classification and routing logic produced more improvement than any hyperparameter tuning would have.
Design insight: When forecasting many series at once, the first modeling decision is not "which algorithm?" — it is "are all these series actually the same kind of problem?" Classification before forecasting is often the highest-leverage step.