Imports¶
%load_ext autoreload
%autoreload 2
import logging
import pandas as pd
try:
from IPython.display import display
except ImportError:
display = print # type: ignoreimport helpers.hnotebook as hnotebo
import helpers.htutorial as ut
ut.config_notebook()
# Initialize logger.
logging.basicConfig(level=logging.INFO)
_LOG = logging.getLogger(__name__)
hnotebo.init_loggers(_LOG, set_all_loggers_to_print=True)pymc is not installed
arviz is not installed
preliz is not installed
sns is not installed
Python 3.12.13
Linux 3a71c0a7bf16 6.12.67-linuxkit #1 SMP Sun Jan 25 02:26:28 UTC 2026 aarch64 GNU/Linux
Part 1: Loading Data¶
Cell 1.1: Loading the marketing panel data¶
Goal:
- Load a marketing panel dataset used to estimate the effect of an offline campaign via difference-in-differences
The data is in a panel format:
- Each row is a (
date,city) pair - downloads: the outcome to predict
- treated: indicator of whether the city received the intervention
- tau: the (known, ground-truth) treatment effect
dir_name = "L08_data"
!ls $dir_name
out_dir_name = "figures/"mkt_data = pd.read_csv(f"{dir_name}/short_offline_mkt_south.csv").astype(
{"date": "datetime64[ns]"}
)
print("mkt_data.shape=", mkt_data.shape)
display(mkt_data.head())Part 2: Canonical Difference-in-Differences¶
Cell 2.1: Checking the pre/post intervention windows¶
Goal:
- Check the date range covered by each of the 4 (treated, post) groups, to confirm the panel spans both a pre- and a post-intervention period
# Compute pre- and post-intervention period.
pre_post_windows = (
mkt_data.assign(w=lambda d: d["treated"] * d["post"])
.groupby(["w"])
.agg({"date": ["min", "max"]})
)
display(pre_post_windows)Cell 2.2: Computing group-time average outcomes¶
Goal:
- Compute the average outcome for each of the 4 (treated, post) groups, the building block of the difference-in-differences estimator
did_data = mkt_data.groupby(["treated", "post"]).agg(
{"downloads": "mean", "date": "min"}
)
display(did_data)mkt_data= (1632, 7)
Loading...
Cell 2.3: Computing the average treatment effect on the treated¶
Goal:
- Estimate the ATT as the treated group’s actual post-period outcome minus its counterfactual (parallel-trends) outcome
Implementation:
y0_est: the treated group’s pre-period outcome, plus the control group’s pre-to-post changeatt = <treated, post-period outcome> - y0_est
y0_est = (
did_data.loc[1].loc[0, "downloads"] # Treated baseline.
+ did_data.loc[0].diff().loc[1, "downloads"] # Control evolution.
)
att = did_data.loc[1].loc[1, "downloads"] - y0_est
print("att=", att)Loading...
Cell 2.4: Comparing against the known ground-truth effect¶
Goal:
- Compare the estimated ATT against the dataset’s known, ground-truth
tau, to check how close the difference-in-differences estimator got
print("mean tau=", mkt_data.query("post==1").query("treated==1")["tau"].mean())