idid: instrumented difference-in-differences¶
idid implements instrumented difference-in-differences (IDiD)
estimators for cohort-time local average treatment effects on the
treated, \(LATT(e,t)\). The package covers both balanced panel data and
repeated cross-sections, and includes the doubly robust (DR) and double
machine learning (DML) estimators developed in the
paper. It also provides tools for
aggregating cohort-time effects into event-study and summary parameters,
together with plotting utilities for inspecting the aggregated effects.
Links:
Documentation: https://jsr-p.github.io/idid/
Paper: Raaschou-Pedersen (2026)
Repository: https://github.com/jsr-p/idid
Installation¶
uv pip install git+https://github.com/jsr-p/idid
or from PyPi:
uv pip install idid-py
Try it out¶
With 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.
Minimal Example¶
1import idid
2
3data = idid.sim_stag_panel(n=10_000, T=5, E_cohorts=[0, 2, 3, 4, 5])
4print(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 │
└─────┴─────┴─────┴──────────┴─────┴──────────┘
1res = idid.estimate(
2 data,
3 cohort="E",
4 time="t",
5 outcome="Y_t",
6 treatment="D_t",
7 unit="id",
8 covariates=["X"],
9 control="never",
10 method="dr",
11 balanced=True,
12 verbose=False,
13)
14res.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
1dynamic = idid.agg_latt(res, method="dynamic")
2dynamic.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
Documentation¶
For the user guide, see the documentation, in particular the quickstart.
Replication of simulation experiments from paper¶
Run just replication.
See the Justfile and scripts/sim_exps.py.
Notes¶
When
control="never", the never-exposed cohort must be coded asE = 0(compared to the \(E = \infty\) notation in the paper).
Citation¶
If you use idid, please cite Raaschou-Pedersen
(2026).