Examples
See also
Tutorials for narrative step-by-step workflows. User Guide for installation and parameter guidance.
Each example below demonstrates a specific scenario using the built-in datasets. All outputs are produced with fixed random seeds for reproducibility.
Built-in Datasets
The package ships two example datasets for immediate use:
from diddesign.data import data
# Malesky et al. (2014): Vietnam communes, repeated cross-section
malesky = data("malesky2014")
print(malesky.shape) # (6269, 42)
# Paglayan (2019): US states teacher bargaining, staggered adoption panel
paglayan = data("paglayan2019")
print(paglayan.shape) # (2058, 9)
RCS Double DID (Malesky 2014)
A repeated cross-section workflow: estimate Double DID, run a lag-1 placebo diagnostic, and inspect the component estimates and GMM weights.
from diddesign.data import data
from diddesign import did, did_check, fit
df = data("malesky2014")
# Pre-treatment diagnostic
check = did_check(
data=df, outcome="pro4", treatment="treatment",
time="year", post="post_treat", data_type="rcs",
id_cluster="id_district", lag=[1], n_boot=50, random_seed=1234,
)
print(check.to_summary_frame())
lag estimate_raw std_error_raw eqci95_lb_std eqci95_ub_std
1 -0.00337 0.041026 -0.163403 0.163403
# Treatment effect estimation
result = did(
df, outcome="pro4", treatment="treatment",
time="year", post="post_treat", data_type="rcs",
id_cluster="id_district", n_boot=200, random_seed=1234,
)
print(result.to_dataframe())
estimator lead estimate std_error ci_lo ci_hi weight
Double-DID 0 0.076596 0.046146 -0.013849 0.167041 NaN
DID 0 0.079314 0.057338 -0.033066 0.191694 1.806658
sDID 0 0.082684 0.089100 -0.091949 0.257317 -0.806658
print(result.to_weights_frame())
lead w_did w_sdid double_did_available
0 1.806658 -0.806658 True
# Plotting rows with diagnostic overlay
fit_rows = fit(result, check_fit=check, as_frame=True)
print(fit_rows)
Staggered Adoption (Paglayan 2019)
A panel staggered-adoption workflow: diagnose multiple pre-treatment lags, then estimate lead-specific SA-Double-DID effects.
import numpy as np
from diddesign.data import data
from diddesign import did, did_check, fit
df = data("paglayan2019")
df["log_expenditure"] = np.log(df["pupil_expenditure"] + 1.0)
# Multi-lag pre-treatment diagnostics
check = did_check(
data=df, outcome="log_expenditure", treatment="treatment",
time="year", unit_id="state", design="sa",
lag=[1, 2, 3], thres=1, n_boot=50, random_seed=1234,
)
print(check.to_summary_frame())
lag estimate_raw std_error_raw eqci95_lb_std eqci95_ub_std
1 -0.002669 0.009736 -0.117499 0.117499
2 -0.012447 0.007841 -0.151357 0.151357
3 0.002269 0.011331 -0.121691 0.121691
# SA-Double-DID estimation
result = did(
df, outcome="log_expenditure", treatment="treatment",
time="year", unit_id="state", design="sa",
thres=1, n_boot=200, random_seed=1234,
)
print(result.to_dataframe())
estimator lead estimate std_error ci_lo ci_hi weight
SA-Double-DID 0 0.011401 0.012157 -0.011430 0.033800 NaN
SA-DID 0 0.010984 0.012247 -0.011420 0.034097 0.843723
SA-sDID 0 0.013653 0.014537 -0.014634 0.042717 0.156277
print(result.to_weights_frame())
lead w_did w_sdid double_did_available
0 0.843723 0.156277 True
# Plotting rows (one per lead)
fit_rows = fit(result, as_frame=True)
print(fit_rows)
source estimator lag lead time_to_treat estimate std_error ci90_lb ci90_ub
fit SA-Double-DID None 0 0 0.011401 0.012157 -0.008596 0.031398
K-DID with J-test (Paglayan 2019)
When three or more pre-treatment periods are available, K-DID exploits higher-order moment conditions. The J-test adaptively removes components whose identifying assumptions appear violated:
result_k = did(
df, outcome="log_expenditure", treatment="treatment",
time="year", unit_id="state", design="sa",
kmax=3, jtest=True, thres=1,
n_boot=200, random_seed=1234,
)
print(result_k.to_dataframe())
estimator lead estimate std_error ci_lo ci_hi weight
SA-K-DID 0 0.011685 0.012156 -0.011180 0.034105 None
SA-DID 0 0.010984 0.012247 -0.011420 0.034097 None
SA-sDID 0 0.013653 0.014537 -0.014634 0.042717 None
SA-kDID-3 0 0.003875 0.023613 -0.040192 0.052995 None
The SA-kDID-3 component exploits three pre-treatment differences. Its larger standard error reflects the additional noise from earlier pre-treatment periods. The SA-K-DID combined estimate aggregates all valid components.
RCS with Covariates
Adding continuous covariates to the Malesky RCS analysis:
from diddesign.data import data
from diddesign import did
df = data("malesky2014")
result = did(
df, outcome="pro4", treatment="treatment",
time="year", post="post_treat", data_type="rcs",
id_cluster="id_district",
covariates=["lnarea", "lnpopden"],
n_boot=50, random_seed=1234,
)
print(result.to_dataframe())
estimator lead estimate std_error ci_lo ci_hi weight
Double-DID 0 0.083374 0.042437 0.000199 0.166549 NaN
DID 0 0.086286 0.054630 -0.020786 0.193359 1.864198
sDID 0 0.089656 0.085487 -0.077895 0.257207 -0.864198
Formula Interface
The formula interface provides a concise alternative to specifying column names
individually. Covariates appear after the | separator, joined by +:
from diddesign.data import data
from diddesign import did
df = data("malesky2014")
result = did(
df, formula="pro4 ~ treatment + post_treat | lnarea + lnpopden",
time="year", data_type="rcs",
id_cluster="id_district",
n_boot=50, random_seed=1234,
)
print(result.to_dataframe())
estimator lead estimate std_error ci_lo ci_hi weight
Double-DID 0 0.083374 0.042437 0.000199 0.166549 NaN
DID 0 0.086286 0.054630 -0.020786 0.193359 1.864198
sDID 0 0.089656 0.085487 -0.077895 0.257207 -0.864198
For categorical covariates, use the factor() wrapper:
result = did(
df, formula="pro4 ~ treatment + post_treat | lnarea + factor(city)",
time="year", data_type="rcs",
id_cluster="id_district",
n_boot=50, random_seed=1234,
)
Visualization
Render figures from fitted results (requires diddesign[plot]):
from diddesign import plot_estimates, plot_diagnostics, plot_trends
# Event-study plot with placebo overlay
plot_estimates(result, check_fit=check,
title="Double DID Estimates", save="estimates.png", show=False)
# Multi-panel diagnostic figure
plot_diagnostics(check, result=result,
title="Pre-treatment Diagnostics", save="diagnostics.png",
show=False)
# Pre-treatment trend comparison
plot_trends(check, title="Trend Comparison", save="trends.png", show=False)
LaTeX Table Export
Generate publication-ready tables directly from result objects:
from diddesign.data import data
from diddesign import did
df = data("malesky2014")
result = did(
df, outcome="pro4", treatment="treatment",
time="year", post="post_treat", data_type="rcs",
id_cluster="id_district", n_boot=200, random_seed=1234,
)
print(result.to_latex(caption="Recentralization Effect"))
\begin{table}[htbp]
\centering
\caption{Recentralization Effect}
\begin{tabular}{llrrrr}
\hline\hline
Estimator & Lead & Estimate & Std. Error & CI Low & CI High \\
\hline
Double-DID & 0 & 0.0766$^{*}$ & 0.0461 & -0.0138 & 0.1670 \\
DID & 0 & 0.0793 & 0.0573 & -0.0331 & 0.1917 \\
sDID & 0 & 0.0827 & 0.0891 & -0.0919 & 0.2573 \\
\hline\hline
\multicolumn{6}{l}{\footnotesize Note: $^{*}$p$<$0.10, $^{**}$p$<$0.05, $^{***}$p$<$0.01} \\
\end{tabular}
\end{table}
Running Repository Examples
The repository ships standalone example scripts (for repository contributors):
python3 examples/malesky_rcs_workflow.py
python3 examples/paglayan_sa_workflow.py
These scripts print compact JSON summaries containing component estimates, GMM weights, diagnostic rows, and plotting rows.
Dataset Provenance
For full dataset provenance, cleaning rules, and package-copy paths, see the
root data/ registry at data/README.md.