What is ModernDiD?
ModernDiD is a Python library for difference-in-differences (DiD), designed for applied researchers, economists, and data scientists who estimate the effects of policies and treatments.
A two-way fixed effects regression, long the default way to run DiD in applied settings, can give misleading answers when treatment effects differ across groups or change over time. ModernDiD implements the estimators that recent research developed to avoid that problem.
Every estimator reports analytical or bootstrap standard errors for the effects it estimates. Most can also cluster those errors by a variable such as state. The staggered, triple-difference, continuous-treatment, and nonparametric IV estimators can build simultaneous confidence bands too. Estimation runs in parallel threads on one machine and supported estimators also run on NVIDIA GPUs for large panels. Staggered DiD also runs on Spark clusters for panels too large for one machine.
ModernDiD is under active development so expect sharp edges. Please help by trying it out, reporting bugs, and telling us what you think. If you're new to DiD, the introduction covers the ideas behind the methods before any code.
Installation
ModernDiD requires Python 3.12 or later. In a project managed by uv, add it as a dependency.
uv add moderndid
With pip, install it into your environment instead.
pip install moderndid
Extras add optional dependencies and work the same way with either tool.
uv add "moderndid[plots]" # Core estimators and plotting
uv add "moderndid[all]" # All estimator extras and plots
uv add "moderndid[didcont,plots]" # Choose individual extras
uv add "moderndid[gpu]" # CUDA 12 GPU dependencies
uv add "moderndid[spark]" # PySpark for staggered DiD on Spark
The all extra includes didml and leaves out gpu and spark. The
installation guide
covers every extra, GPU setup, and troubleshooting. The development version
installs straight from GitHub.
uv add "moderndid[all] @ git+https://github.com/jordandeklerk/moderndid.git"
Features
ModernDiD covers the main research designs in the modern DiD literature.
- Staggered DiD:
att_gtandaggte, based on Callaway and Sant'Anna (2021) - Two-period DiD:
drdid,ipwdid, andordid, based on Sant'Anna and Zhao (2020) - Triple differences:
dddandagg_ddd, based on Ortiz-Villavicencio and Sant'Anna (2025) - Continuous treatment:
cont_did, based on Callaway, Goodman-Bacon, and Sant'Anna (2025) - Intertemporal treatment:
did_multiplegt, based on de Chaisemartin and D'Haultfoeuille (2024) - Dynamic covariate balancing:
dyn_balancing, based on Viviano and Bradic (2026) - Machine learning DiD:
didml, based on Hatamyar, Kreif, Rocha, and Huber (2023) - Extended TWFE:
etwfeandemfx, based on Wooldridge (2023, 2025) - Sensitivity analysis:
honest_did, based on Rambachan and Roth (2023) - Nonparametric IV:
npiv, based on Chen, Christensen, and Kankanala (2024)
Every estimator also comes with the tools the rest of an analysis needs.
- Estimators accept any Arrow-compatible DataFrame, such as polars, pandas, pyarrow, or a DuckDB result.
- Inference covers analytical and bootstrap standard errors, plus clustering and simultaneous confidence bands where the estimator supports them.
- Plots return plotnine
ggplotobjects that take any further plotnine layer or theme. - Results work directly with maketables to build LaTeX, HTML, Word, and Typst tables.
- Parallel threads speed up estimation on one machine.
Quickstart
The example below estimates how state minimum wage increases that took effect in different years affected teen employment. The staggered adoption example works through the same question at a deeper level.
import moderndid as did
# County teen employment and state minimum wage increases from 2003 to 2007
data = did.load_mpdta()
# An average effect for each treatment cohort and year
result = did.att_gt(
data=data,
yname="lemp",
tname="year",
idname="countyreal",
gname="first.treat",
)
# An event study by time relative to treatment, plotted with the plots extra
event_study = did.aggte(result, type="dynamic")
did.plot_event_study(event_study)
Large panels
The quickstart's call moves to an NVIDIA GPU or a Spark cluster with a single change.
NVIDIA GPUs
With the gpu extra, passing backend="cupy" to att_gt, ddd, or
cont_did runs its supported numerical work on the GPU. Since moving arrays to
the GPU takes time too, a small panel such as the county data can fit faster on
the CPU. The
GPU guide
shows how to check your CUDA setup and time a fit.
# The quickstart's fit with its numerical work on the GPU
result = did.att_gt(
data=data,
yname="lemp",
tname="year",
idname="countyreal",
gname="first.treat",
backend="cupy",
)
Spark clusters
Passed a Spark DataFrame, att_gt fits it on the cluster without gathering its
rows on one machine. That suits a panel that already sits in a Spark table or
holds more units than one machine can keep in memory. Since Databricks runtimes
already include PySpark, you need the spark extra only where you run Spark
yourself. The
Spark guide
walks through a fit and the options a Spark fit supports. The
Spark setup guide
shows how to install ModernDiD on Databricks, a cluster you manage, or your own
machine.
from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()
# The county data stands in for a large panel stored in a Spark table
data = spark.createDataFrame(did.load_mpdta().to_pandas())
# The same calls as in the quickstart
result = did.att_gt(
data=data,
yname="lemp",
tname="year",
idname="countyreal",
gname="first.treat",
)
event_study = did.aggte(result, type="dynamic")
Documentation
- User Guide: installation, a first analysis, data preparation, results, and computation.
- Examples: analyses for each treatment design.
- API Reference: function signatures, parameters, and result objects.
- Background: the assumptions, identification, and inference behind each estimator.
- Comparisons with R: each estimator's results beside those of the R package for the same method.
- FAQ: answers to common questions about data, estimators, and results.
- Development: setup, workflow, testing, releases, architecture, new estimators, debugging, and benchmarking.
- Changelog: changes between versions.
Acknowledgements
ModernDiD builds on methods and implementations developed by researchers across the DiD literature. The acknowledgements list the papers and software behind each estimator.
Metadata
Release files for moderndid 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| moderndid-0.4.0.tar.gz | 1.6 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| moderndid-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.4 MB
Release files / moderndid-0.4.0.tar.gz
| Download URL | moderndid-0.4.0.tar.gz |
|---|---|
| Size | 1.6 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
03f663f1f2b3b9a3430fbc6f0b7e1b5e3a6216382f14f7a38bf05f51e9f1336c
|
|
BLAKE2b-256 checksum How to use checksums |
6d6abca0ebac277bda92905d14964fb93c88f071e647440fa8522a96ebc7ab7f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / moderndid-0.4.0-py3-none-any.whl
| Download URL | moderndid-0.4.0-py3-none-any.whl |
|---|---|
| Size | 1.8 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a8c12d6a5b79595c82865abc0e135ed82ffa2fd834e6446f0a9c93a9b48bb7f7
|
|
BLAKE2b-256 checksum How to use checksums |
b455ab2d21e8b89e4dd6f6ec8f74dcb1bbb7a728e0b94fa369ba64663e4164ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for moderndid 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for moderndid-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.