Python Comparison#
This guide compares diff-diff with other Python packages for DiD analysis, helping users understand the landscape and choose the right tool.
Overview#
Feature |
diff-diff |
pyfixest |
differences |
CausalPy |
linearmodels |
|---|---|---|---|---|---|
Basic DiD |
✅ |
✅ |
✅ |
✅ |
✅ (manual) |
TWFE |
✅ |
✅ |
❌ |
❌ |
✅ |
Callaway-Sant’Anna |
✅ |
❌ |
✅ |
❌ |
❌ |
Sun-Abraham |
✅ |
✅ |
❌ |
❌ |
❌ |
Synthetic DiD |
✅ |
❌ |
❌ |
✅ (SC only) |
❌ |
Honest DiD |
✅ |
❌ |
❌ |
❌ |
❌ |
Wild bootstrap |
✅ |
✅ |
❌ |
❌ |
❌ |
Package Summaries#
pyfixest#
pyfixest is inspired by R’s
fixest package and excels at fast fixed effects estimation.
Strengths:
Very fast for high-dimensional fixed effects (JIT-compiled)
Sun-Abraham event study estimator
Gardner’s did2s two-stage estimator
Local projections (Dube et al. 2023)
Publication-ready tables
Gaps:
No Callaway-Sant’Anna estimator
No sensitivity analysis (Honest DiD)
No synthetic DiD
differences#
differences provides a pure Callaway-Sant’Anna implementation.
Strengths:
Faithful implementation of Callaway & Sant’Anna (2021)
Multi-valued treatment support
Triple difference estimation
Handles unbalanced panels
Gaps:
No sensitivity analysis
No synthetic DiD
Limited inference options (no wild bootstrap)
CausalPy#
CausalPy takes a Bayesian approach using PyMC for uncertainty quantification.
Strengths:
Bayesian credible intervals
Synthetic control methods
Nice built-in visualizations
Interrupted time series
Gaps:
Limited to simple 2x2 DiD designs
No staggered treatment support
No event study methods
linearmodels#
linearmodels provides panel data econometrics, serving as a building block for DiD.
Strengths:
Solid PanelOLS implementation
Two-way fixed effects
Various standard error options
statsmodels-compatible
Gaps:
No DiD-specific methods (manual implementation required)
No staggered treatment handling
No diagnostics or sensitivity analysis
Why diff-diff?#
diff-diff fills critical gaps in the Python DiD ecosystem:
Only Python library with Honest DiD sensitivity analysis
Reviewers increasingly expect sensitivity analysis showing robustness to parallel trends violations. diff-diff is the only Python package implementing Rambachan & Roth (2023).
Unified toolkit
Combines Callaway-Sant’Anna, Synthetic DiD, TWFE, and Honest DiD in one package with a consistent API.
Built-in diagnostics
Placebo tests, parallel trends tests, Goodman-Bacon decomposition, and sensitivity analysis out of the box.
sklearn-style API
Familiar
fit()interface with rich result objects.
Feature Comparison Table#
Feature |
diff-diff |
pyfixest |
differences |
CausalPy |
linearmodels |
|---|---|---|---|---|---|
Basic 2x2 DiD |
✅ |
✅ |
✅ |
✅ |
✅ |
Two-way fixed effects |
✅ |
✅ |
❌ |
❌ |
✅ |
Staggered DiD (CS) |
✅ |
❌ |
✅ |
❌ |
❌ |
Sun-Abraham estimator |
✅ |
✅ |
❌ |
❌ |
❌ |
Gardner’s did2s |
✅ |
✅ |
❌ |
❌ |
❌ |
Local projections |
❌ |
✅ |
❌ |
❌ |
❌ |
Synthetic DiD |
✅ |
❌ |
❌ |
❌ |
❌ |
Synthetic control |
❌ |
❌ |
❌ |
✅ |
❌ |
Covariate adjustment |
✅ |
✅ |
✅ |
Limited |
✅ |
Doubly robust |
✅ |
❌ |
✅ |
❌ |
❌ |
IPW |
✅ |
❌ |
✅ |
❌ |
❌ |
Group-time effects |
✅ |
❌ |
✅ |
❌ |
❌ |
Event study aggregation |
✅ |
✅ |
✅ |
❌ |
❌ |
Honest DiD (ΔRM) |
✅ |
❌ |
❌ |
❌ |
❌ |
Honest DiD (ΔSD) |
✅ |
❌ |
❌ |
❌ |
❌ |
Sensitivity analysis |
✅ |
❌ |
❌ |
❌ |
❌ |
Wild bootstrap |
✅ |
✅ |
❌ |
❌ |
❌ |
Cluster-robust SE |
✅ |
✅ |
✅ |
❌ |
✅ |
Placebo tests |
✅ |
❌ |
❌ |
❌ |
❌ |
Parallel trends tests |
✅ |
❌ |
❌ |
❌ |
❌ |
Bacon decomposition |
✅ |
❌ |
❌ |
❌ |
❌ |
Event study plots |
✅ |
✅ |
✅ |
✅ |
❌ |
Triple Difference (DDD) |
✅ |
❌ |
✅ |
❌ |
❌ |
TROP |
✅ |
❌ |
❌ |
❌ |
❌ |
Stacked DiD |
✅ |
❌ |
❌ |
❌ |
❌ |
Bacon Decomposition |
✅ |
❌ |
❌ |
❌ |
❌ |
Continuous DiD |
✅ |
❌ |
❌ |
❌ |
❌ |
Efficient DiD |
✅ |
❌ |
❌ |
❌ |
❌ |
Built-in datasets |
✅ |
❌ |
❌ |
❌ |
❌ |
Bayesian inference |
❌ |
❌ |
❌ |
✅ |
❌ |
Code Comparison#
Basic DiD#
# diff-diff
from diff_diff import DifferenceInDifferences
did = DifferenceInDifferences()
results = did.fit(data, outcome='y', treatment='treated', time='post')
print(results.summary())
# pyfixest
import pyfixest as pf
fit = pf.feols("y ~ treated:post | unit + time", data=data)
fit.summary()
# linearmodels
from linearmodels.panel import PanelOLS
mod = PanelOLS.from_formula(
'y ~ treated:post + EntityEffects + TimeEffects',
data=data.set_index(['unit', 'time'])
)
results = mod.fit(cov_type='clustered', cluster_entity=True)
Staggered DiD (Callaway-Sant’Anna)#
# diff-diff
from diff_diff import CallawaySantAnna
cs = CallawaySantAnna(estimation_method='dr')
results = cs.fit(
data,
outcome='y',
unit='unit',
time='time',
first_treat='first_treat',
covariates=['x1', 'x2'],
aggregate='event_study'
)
event_study = results.event_study_effects
# differences
from differences import ATTgt
att = ATTgt(
data=data,
outcome='y',
cohort='first_treat',
time='time',
strata='unit'
)
att.fit()
att.aggregate('event')
Sensitivity Analysis#
# diff-diff (only Python option)
from diff_diff import HonestDiD, plot_sensitivity
honest = HonestDiD(method='relative_magnitude', M=1.0)
results = honest.fit(event_study_results)
# Sensitivity over M grid
sensitivity = honest.sensitivity_analysis(
event_study_results,
M_grid=[0, 0.5, 1.0, 1.5, 2.0]
)
plot_sensitivity(sensitivity)
# No equivalent in other Python packages
When to Use Each Package#
Use diff-diff when:
You need sensitivity analysis (Honest DiD)
You want Callaway-Sant’Anna with doubly robust estimation
You need built-in diagnostics (placebo tests, parallel trends)
You prefer a unified sklearn-style API
Use pyfixest when:
Speed is critical (high-dimensional fixed effects)
You need Sun-Abraham or did2s estimators
You want R’s fixest-style syntax
You need local projections
Use differences when:
You need multi-valued treatment effects
You need triple difference estimation
You want the most faithful CS implementation
Use CausalPy when:
You want Bayesian uncertainty quantification
You’re doing synthetic control (not synthetic DiD)
You prefer PyMC-based modeling
Use linearmodels when:
You’re doing general panel econometrics beyond DiD
You need statsmodels compatibility
You’re implementing DiD manually with full control