Metadata-Version: 2.4
Name: idid-py
Version: 0.1.0
Summary: idid: instrumented difference-in-differences
Project-URL: Homepage, https://github.com/jsr-p/idid
Project-URL: Documentation, https://jsr-p.github.io/idid
Project-URL: Repository, https://github.com/jsr-p/idid
Author-email: jsr-p <jsr-p@sodas.ku.dk>
License-Expression: MIT
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.12
Requires-Dist: matplotlib>=3.10.0
Requires-Dist: polars>=1.21.0
Requires-Dist: pyarrow>=19.0.0
Requires-Dist: scikit-learn>=1.8.0
Requires-Dist: seaborn>=0.13.2
Requires-Dist: statsmodels>=0.14.6
Requires-Dist: tabulate>=0.9.0
Requires-Dist: tqdm>=4.67.1
Description-Content-Type: text/markdown

# 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 {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.

## 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](Justfile) and
[scripts/sim_exps.py](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).
