[KDD 2026] Benchmarking Counterfactual Prediction in Epidemic Time Series with Time-Varying Interventions

June 15, 2026 · View on GitHub

This repository contains the machine-learning baselines and evaluation utilities for EpiCF-Bench, the benchmark introduced in:

Wenhao Mu, Facundo Yan, Anik Mumssen, Marisa Eisenberg, and Alexander Rodriguez. 2026. Benchmarking Counterfactual Prediction in Epidemic Time Series with Time-Varying Interventions. In Proceedings of the 32nd ACM SIGKDD Conference on Knowledge Discovery and Data Mining.

EpiCF-Bench evaluates counterfactual prediction for epidemic time series under time-varying interventions. The benchmark is generated from a calibrated differentiable agent-based model (ABM), producing factual and counterfactual epidemic trajectories across 158 U.S. counties.

Resources

The ABM code and benchmark dataset are intentionally maintained outside this repository. This repo is meant to stay lightweight and focused on reproducing the learning-based benchmark experiments.

Current Status

The first cleaned release includes:

  • KDE baseline for single-policy and multi-policy settings
  • Transformer baseline for single-policy and multi-policy settings
  • RNN S-learner baseline for single-policy and multi-policy settings
  • RNN T-learner baseline for single-policy and multi-policy settings
  • Shared dataset loading code for ABM-generated CSV files
  • Preprocessing and postprocessing scripts for benchmark data and model outputs
  • Trajectory, distributional, predictive-interval, calibration, and CATE evaluation utilities

Additional baselines from the paper, including VAE, diffusion, and TECDE, will be added in later updates.

Repository Layout

.
├── experiments/
│   ├── single_policy/
│   │   ├── train_kde.py
│   │   ├── infer_kde.py
│   │   ├── train_transformer.py
│   │   ├── infer_transformer.py
│   │   └── train_st_learner.py
│   └── multi_policy/
│       ├── train_kde.py
│       ├── infer_kde.py
│       ├── train_transformer.py
│       ├── infer_transformer.py
│       └── train_st_learner.py
├── scripts/
│   ├── preprocess_data.py
│   ├── postprocess_predictions.py
│   ├── evaluate_predictions.py
│   ├── evaluate_distribution.py
│   └── evaluate_cate.py
└── src/
    └── epicf_benchmark/
        ├── data.py
        ├── evaluate.py
        ├── kde.py
        ├── models.py
        ├── postprocess.py
        ├── preprocessing.py
        ├── st_learner.py
        └── transformer.py

Setup

python -m venv .venv
source .venv/bin/activate
pip install -e .

If you prefer not to install the package, run scripts with:

export PYTHONPATH="$PWD/src:$PYTHONPATH"

Data

Download the benchmark data from Zenodo and place or symlink the processed CSV folders into the repository root:

data_single_final/
├── y_train.csv
├── a_train.csv
├── x_train.csv
├── y_test.csv
├── a_test.csv
└── x_test.csv

data_multi_final/
├── y_train.csv
├── a_train.csv
├── x_train.csv
├── y_test.csv
├── a_test.csv
└── x_test.csv

The default experiments assume:

  • sequence length: 168
  • number of counties/units: 158
  • covariate channels: 17
  • single-policy treatment channels: 1
  • multi-policy treatment channels: 2

Preprocessing

The preprocessing scripts convert raw ABM output tables into the y/a/x/xa files consumed by the ML baselines.

For the single-policy setting:

python scripts/preprocess_data.py \
  --setting single \
  --input-csv data_single_final/all_county_data_extra_sp.csv \
  --output-dir data_single_final

For the multi-policy setting:

python scripts/preprocess_data.py \
  --setting multi \
  --input-csv data_multi_final/all_county_data_extra_mp.csv \
  --output-dir data_multi_final

Each run also writes outcome_stats.json, which stores the factual-outcome normalization parameters used by postprocessing.

Running Baselines

Single-Policy KDE

python experiments/single_policy/train_kde.py \
  --data-dir data_single_final \
  --output-dir kde_models_single

python experiments/single_policy/infer_kde.py \
  --data-dir data_single_final \
  --model-dir kde_models_single \
  --output-dir output_single \
  --split test \
  --prefix kde_cf

Multi-Policy KDE

python experiments/multi_policy/train_kde.py \
  --data-dir data_multi_final \
  --output-dir kde_models_multi

python experiments/multi_policy/infer_kde.py \
  --data-dir data_multi_final \
  --model-dir kde_models_multi \
  --output-dir output_multi \
  --split test \
  --prefix kde_cf

Single-Policy Transformer

python experiments/single_policy/train_transformer.py \
  --data-dir data_single_final \
  --output-dir models_single \
  --epochs 100

python experiments/single_policy/infer_transformer.py \
  --data-dir data_single_final \
  --model-path models_single/transformer.pt \
  --output-dir output_single \
  --split test \
  --prefix transformer_cf

Multi-Policy Transformer

python experiments/multi_policy/train_transformer.py \
  --data-dir data_multi_final \
  --output-dir models_multi \
  --epochs 200

python experiments/multi_policy/infer_transformer.py \
  --data-dir data_multi_final \
  --model-path models_multi/transformer.pt \
  --output-dir output_multi \
  --split test \
  --prefix transformer_cf

Single-Policy RNN S/T Learner

python experiments/single_policy/train_st_learner.py \
  --data-dir data_single_final \
  --model-dir models_single_final \
  --output-dir output_single_final \
  --epochs 200 \
  --seed 1 \
  --run-id 1_new

This writes files such as slearner_rnn_mean_f_1_new.npy, slearner_rnn_mean_cf_1_new.npy, tlearner_rnn_mean_f_1_new.npy, and tlearner_rnn_mean_cf_1_new.npy.

Multi-Policy RNN S/T Learner

python experiments/multi_policy/train_st_learner.py \
  --data-dir data_multi_final \
  --model-dir models_multi_final \
  --output-dir output_multi_final \
  --epochs 200 \
  --seed 3 \
  --run-id 3_new

The multi-policy script trains on y_train_3.csv, a_train_3.csv, and x_train_3.csv, matching the original multi-policy S/T learner setup.

Evaluation

Postprocessing

The postprocessing script inverse-transforms normalized model outputs back to the original case-count scale, following the original postprocess.ipynb workflow.

python scripts/postprocess_predictions.py \
  --input output_single/transformer_cf.csv \
  --stats data_single_final/outcome_stats.json \
  --output-prefix output_single/transformer_cf_inverse \
  --num-units 158

For multi-policy factual predictions that need to be repeated across the three counterfactual policy alternatives, use --tile 3.

Evaluation

For direct point-trajectory prediction metrics:

python scripts/evaluate_predictions.py \
  --y-true path/to/ground_truth.npy \
  --y-pred output_single/transformer_cf.npy \
  --output results/transformer_single_metrics.csv

For sampled predictions and distributional metrics:

python scripts/evaluate_distribution.py \
  --truth data_single_final/y_test_unnorm.csv \
  --pred output_single/cvae_cf_inverse.npy \
  --num-units 158 \
  --samples-per-unit 100 \
  --output results/cvae_single_distribution.csv

For CATE RMSE:

python scripts/evaluate_cate.py \
  --factual-truth data_single_final/y_train_unnorm.csv \
  --counterfactual-truth data_single_final/y_test_unnorm.csv \
  --factual-pred output_single/transformer_f_inverse.npy \
  --counterfactual-pred output_single/transformer_cf_inverse.npy \
  --num-units 158 \
  --output results/transformer_single_cate.csv

Citation

@inproceedings{mu2026epicfbench,
  title = {Benchmarking Counterfactual Prediction in Epidemic Time Series with Time-Varying Interventions},
  author = {Mu, Wenhao and Yan, Facundo and Mumssen, Anik and Eisenberg, Marisa and Rodriguez, Alexander},
  booktitle = {Proceedings of the 32nd ACM SIGKDD Conference on Knowledge Discovery and Data Mining},
  year = {2026}
}