A small, end-to-end incrementality sandbox using M5-style simulated retail data.
It simulates treatment/control campaigns and evaluates causal methods against known ground truth.
This repo is intentionally “product-shaped”:
- a reproducible pipeline (
make demo,make pipeline) - method outputs saved as Parquet artifacts
- a Streamlit app to explore results + diagnostics
- a trust checklist (guardrails + placebo tests)
- Campaign simulator (creates treated/control + true effect)
- Methods
- DiD / event study
- Synthetic Control (ridge SCM, optional log1p fit)
- Evaluation vs ground truth (scale-aware)
- Streamlit app to explore results, scorecard, diagnostics, and SCM interpretability (donor weights)
- Method trust checks
- Pre-fit RMSE + stability (CV)
- Placebo tests (fake treatment dates) + placebo lift distribution
app/
Home.py
pages/
01_Documentation.py
02_method_trust_checklist.py
03_Placebo Tests.py
data/
raw/
processed/ # generated artifacts (parquet)
scripts/
demo.sh
smoke.sh
src/
m5lift/
sim/
methods/
eval/
io/
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtRuns the full pipeline + launches the app.
./scripts/demo.shThen open:
make install
make demo
make appmake pipeline \
CAMPAIGN_ID=cmp_003 \
START_DATE=2014-08-01 \
END_DATE=2014-08-28 \
TREAT_FRAC=0.2 \
MAX_UPLIFT=0.15 \
SEED=7 \
DONOR_GRAIN=store_dept \
USE_LOG1P=1 \
ALPHA=50make placebo \
CAMPAIGN_ID=cmp_003 \
DONOR_GRAIN=store_dept \
USE_LOG1P=1 \
ALPHA=50 \
N_PLACEBOS=50 \
MIN_PRE_DAYS=28 \
SEED=7make appmake check./scripts/smoke.shfact_ground_truth.parquetGround truth panel including treatment assignment, true effect (tau), and observed outcomes (y_obs).
-
fact_method_results.parquetOne row per(campaign_id, method)with:att_hat(estimated ATT in units)- SCM diagnostics like
rmse_pre, plus other method diagnostics where available
-
fact_method_eval.parquetScale-aware evaluation table used by the dashboard (e.g., bias vs true ATT).
-
scm_series_<campaign>_<grain>[_log1p].parquetTime series used for SCM plots:y_treated,y0_hat, and lift columns (commonlylift_hat_units)
-
scm_weights_<campaign>_<grain>[_log1p].parquetDonor weights (interpretability / “what built the counterfactual”).
-
scm_placebo_<campaign>_<grain>[_log1p].parquetPlacebo windows + outcomes:placebo_start,placebo_endatt_hat_unitsrmse_pre,cv
- ATT (units): average incremental lift in the campaign window (positive = incremental demand, negative = possible cannibalization or model mismatch)
- Counterfactual (
y0_hat): what SCM estimates would have happened without treatment - Lift series (
lift_hat_units): dailyy_treated - y0_hat
- Assumption: parallel trends between treated and control
- Diagnostic:
pretrend_pSmaller p-values can signal pre-trend differences (violation risk)
- Pre-fit RMSE (
rmse_pre): how well donors match treated pre-period (lower is better) - Stability CV (
cv): variability of daily lift within the campaign (lower is more stable) - Donor weights: sanity check that counterfactual isn’t dominated by strange donors
Why: detect timing artifacts where SCM would “find lift” even when nothing happened.
How it works:
- sample placebo windows before the real campaign window
- run SCM as if treatment started then
- compare real ATT vs placebo ATT distribution
Empirical two-sided p-value:
p = mean(|ATT_placebo| >= |ATT_real|)
Rule of thumb:
p < 0.10→ lift looks rare under placebo timing (more believable)p ≥ 0.10→ lift is common under placebo timing (be cautious)
Where to view:
- Streamlit → Method Trust Checklist / Placebo Tests pages
- Bad pre-fit RMSE → SCM lift is not trustworthy (counterfactual is wrong)
- Short pre-period → unstable SCM weights + weak placebo selection
- Too granular donor grain → can overfit pre-period
- Very small true effects → estimates will look noisy (expected)
make demoruns clean on a fresh clone./scripts/demo.shworks end-to-end./scripts/smoke.shpassesmake checkpasses- Streamlit runs without deprecation warnings (e.g.,
use_container_widthremoved) - README documents: what it is, how to run, and how to interpret outputs
- docs/M5_Causal_Lift_End_to_End_Guide_v2.docx