idid implements instrumented difference-in-differences (IDiD)
estimators for cohort-time local average treatment effects on the
treated,
Links:
- Documentation: https://jsr-p.github.io/idid/
- Paper: Raaschou-Pedersen (2026)
- Repository: https://github.com/jsr-p/idid
uv pip install git+https://github.com/jsr-p/ididor from PyPi:
uv pip install idid-pyWith uv run the following:
uv run --with "git+https://github.com/jsr-p/idid" python -i -c '
import idid
import polars as pl
res = idid.estimate(
data=(data := idid.sim_stag_panel(n=10_000, T=5, E_cohorts=[0, 2, 3, 4, 5])),
cohort="E",
time="t",
outcome="Y_t",
treatment="D_t",
unit="id",
covariates=["X"],
control="never",
method="dr",
balanced=True,
verbose=False,
)
print(res)
res.summary()
'and inspect the results in the interactive shell.
import idid
data = idid.sim_stag_panel(n=10_000, T=5, E_cohorts=[0, 2, 3, 4, 5])
print(data.head())shape: (5, 6)
┌─────┬─────┬─────┬──────────┬─────┬──────────┐
│ id ┆ E ┆ t ┆ X ┆ D_t ┆ Y_t │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ f64 ┆ i64 ┆ f64 │
╞═════╪═════╪═════╪══════════╪═════╪══════════╡
│ 0 ┆ 2 ┆ 1 ┆ 0.496714 ┆ 1 ┆ 1.429874 │
│ 0 ┆ 2 ┆ 2 ┆ 0.496714 ┆ 1 ┆ 1.89475 │
│ 0 ┆ 2 ┆ 3 ┆ 0.496714 ┆ 0 ┆ 0.573911 │
│ 0 ┆ 2 ┆ 4 ┆ 0.496714 ┆ 0 ┆ 2.3326 │
│ 0 ┆ 2 ┆ 5 ┆ 0.496714 ┆ 1 ┆ 4.496302 │
└─────┴─────┴─────┴──────────┴─────┴──────────┘
res = idid.estimate(
data,
cohort="E",
time="t",
outcome="Y_t",
treatment="D_t",
unit="id",
covariates=["X"],
control="never",
method="dr",
balanced=True,
verbose=False,
)
res.summary()Cohort-Time Local Average Treatment Effects on the Treated:
E t AET(e, t) LATT(e, t) Std. Error [95% Pointwise. Conf. Band]
2 2 0.1950 1.2059 0.2793 0.6586 1.7533 *
2 3 0.1918 0.9700 0.2806 0.4201 1.5200 *
2 4 0.2445 1.0540 0.2245 0.6141 1.4940 *
2 5 0.2147 1.3310 0.2582 0.8249 1.8371 *
3 3 0.2117 0.5478 0.2551 0.0478 1.0478 *
3 4 0.2447 0.7353 0.2188 0.3063 1.1642 *
3 5 0.2260 0.7074 0.2401 0.2368 1.1781 *
4 4 0.2656 0.9291 0.2026 0.5320 1.3261 *
4 5 0.2661 1.0409 0.2047 0.6397 1.4420 *
5 5 0.2261 0.8852 0.2399 0.4151 1.3554 *
---
Signif. codes: `*' confidence band does not cover 0
Control group: Never treated
Estimation Method: Doubly Robust
dynamic = idid.agg_latt(res, method="dynamic")
dynamic.summary()Overall summary of ATT's based on event-study/dynamic aggregation:
LATT Std. Error [95% Conf. Band]
1.0047 0.1281 0.7537 1.2557 *
Dynamic effects:
Event time Estimate Std. Error [95% Pointwise Conf. Band]
0 0.8868 0.0946 0.7013 1.0723 *
1 0.9157 0.1215 0.6775 1.1539 *
2 0.8853 0.1631 0.5656 1.2051 *
3 1.3310 0.2582 0.8249 1.8371 *
---
Signif. codes: `*' confidence band does not cover 0
Control group: Never treated
Estimation Method: Doubly Robust
For the user guide, see the documentation, in particular the quickstart.
Run just replication.
See the Justfile and scripts/sim_exps.py.
- When
control="never", the never-exposed cohort must be coded asE = 0(compared to the$E = \infty$ notation in the paper).
If you use idid, please cite Raaschou-Pedersen
(2026).