# 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](https://arxiv.org/abs/2605.03699). 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: - Paper: [Raaschou-Pedersen (2026)](https://arxiv.org/abs/2605.03699) - Repository: ## Installation ``` bash uv pip install git+https://github.com/jsr-p/idid ``` or from PyPi: ``` bash uv pip install idid-py ``` ### Try it out With [uv](https://docs.astral.sh/uv/) run the following: ``` bash 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 ``` python 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 │ └─────┴─────┴─────┴──────────┴─────┴──────────┘ ``` python 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 ``` python 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 ## Documentation For the user guide, see the [documentation](https://jsr-p.github.io/idid/), in particular the [quickstart](https://jsr-p.github.io/idid/quickstart). ## Replication of simulation experiments from paper Run `just replication`. See the [Justfile](https://github.com/jsr-p/idid/blob/main/Justfile) and [scripts/sim_exps.py](https://github.com/jsr-p/idid/blob/main/scripts/sim_exps.py). ## Notes - When `control="never"`, the never-exposed cohort must be coded as `E = 0` (compared to the $E = \infty$ notation in the paper). ## Citation If you use `idid`, please cite [Raaschou-Pedersen (2026)](https://arxiv.org/abs/2605.03699).