Comparison with R Packages
This guide compares diff-diff with popular R packages for DiD analysis, helping users familiar with R transition to Python.
Overview
Feature |
diff-diff (Python) |
did (R) |
Other R |
|---|---|---|---|
Basic DiD |
✅ |
✅ |
✅ |
Staggered DiD |
✅ |
✅ |
|
Covariate adjustment |
✅ DR, IPW, Reg |
✅ DR, IPW, Reg |
✅ Varies |
Honest DiD |
✅ |
|
N/A |
Synthetic DiD |
✅ |
|
N/A |
Wild bootstrap |
✅ |
|
N/A |
Package Correspondence
R did Package → diff-diff
The R did package by Callaway and Sant’Anna is the gold standard for
staggered DiD. Here’s how to translate common operations:
Basic estimation:
# R (did package)
library(did)
out <- att_gt(
yname = "Y",
tname = "period",
idname = "id",
gname = "G",
data = data
)
# Python (diff-diff)
from diff_diff import CallawaySantAnna
cs = CallawaySantAnna()
results = cs.fit(
data,
outcome='Y',
time='period',
unit='id',
first_treat='G'
)
With covariates (doubly robust):
# R
out <- att_gt(
yname = "Y", tname = "period",
idname = "id", gname = "G",
xformla = ~ X1 + X2,
est_method = "dr",
data = data
)
# Python
cs = CallawaySantAnna(estimation_method='dr')
results = cs.fit(
data,
outcome='Y',
time='period',
unit='id',
first_treat='G',
covariates=['X1', 'X2']
)
Aggregations:
# R
agg_simple <- aggte(out, type = "simple")
agg_dynamic <- aggte(out, type = "dynamic")
agg_group <- aggte(out, type = "group")
# Python
overall_att = results.att # Simple aggregation
event_study = results.aggregate('event_time') # Dynamic
by_group = results.aggregate('group') # By cohort
R HonestDiD Package → diff-diff
The HonestDiD package implements Rambachan & Roth (2023) sensitivity analysis:
Relative magnitudes (ΔRM):
# R
library(HonestDiD)
delta_rm_results <- createSensitivityResults_relativeMagnitudes(
betahat = beta_hat,
sigma = sigma,
numPrePeriods = 4,
numPostPeriods = 3,
Mbarvec = seq(0, 2, by = 0.5)
)
# Python
from diff_diff import HonestDiD, DeltaRM
honest = HonestDiD(delta=DeltaRM(M_bar=1.0))
results = honest.fit(event_study_results)
# Sensitivity analysis over M grid
sensitivity = honest.sensitivity_analysis(
event_study_results,
M_grid=[0, 0.5, 1.0, 1.5, 2.0]
)
Smoothness restrictions (ΔSD):
# R
delta_sd_results <- createSensitivityResults(
betahat = beta_hat,
sigma = sigma,
numPrePeriods = 4,
numPostPeriods = 3,
Mvec = seq(0, 0.1, by = 0.02)
)
# Python
from diff_diff import HonestDiD, DeltaSD
honest = HonestDiD(delta=DeltaSD(M=0.05))
results = honest.fit(event_study_results)
R synthdid Package → diff-diff
The synthdid package implements Arkhangelsky et al. (2021):
# R
library(synthdid)
setup <- panel.matrices(data, unit = "unit", time = "time",
outcome = "Y", treatment = "treatment")
tau.hat <- synthdid_estimate(setup$Y, setup$N0, setup$T0)
# Python
from diff_diff import SyntheticDiD
sdid = SyntheticDiD()
results = sdid.fit(
data,
outcome='Y',
unit='unit',
time='time',
treated='treatment',
treatment_start=T0
)
Key Differences
Design Philosophy
diff-diff: sklearn-style API with
fit()method, returning rich result objectsR packages: Function-based, returning lists or S3/S4 objects
Inference
diff-diff: Analytical SEs by default, wild bootstrap available
R did: Multiplier bootstrap by default
Fixed Effects
diff-diff:
absorbparameter for high-dimensional FE (within transformation)R fixest:
feolswith|notation for absorbed FE
Output Format
diff-diff results have convenience methods:
results.summary() # Print formatted table
results.to_dict() # Dictionary representation
results.to_dataframe() # pandas DataFrame
Feature Comparison Table
Feature |
diff-diff |
R did |
R HonestDiD |
R synthdid |
|---|---|---|---|---|
Basic 2x2 DiD |
✅ |
✅ |
❌ |
❌ |
TWFE |
✅ |
❌ |
❌ |
❌ |
Staggered DiD (CS) |
✅ |
✅ |
❌ |
❌ |
Covariate adjustment |
✅ |
✅ |
❌ |
❌ |
Doubly robust |
✅ |
✅ |
❌ |
❌ |
Group-time effects |
✅ |
✅ |
❌ |
❌ |
Event study |
✅ |
✅ |
✅ |
❌ |
Synthetic DiD |
✅ |
❌ |
❌ |
✅ |
Honest DiD (ΔRM) |
✅ |
❌ |
✅ |
❌ |
Honest DiD (ΔSD) |
✅ |
❌ |
✅ |
❌ |
Wild bootstrap |
✅ |
❌ |
❌ |
❌ |
Cluster-robust SE |
✅ |
✅ |
❌ |
✅ |
Placebo tests |
✅ |
❌ |
❌ |
✅ |
Parallel trends tests |
✅ |
✅ |
❌ |
❌ |
Bacon decomposition |
✅ |
❌ |
❌ |
❌ |
Migration Tips
Column names: diff-diff uses string column names, similar to R packages
Formula interface: diff-diff supports R-style formulas for basic DiD:
formula='y ~ treated * post'Results access: Use
.att,.se,.ciinstead of$att,$seVisualization:
plot_event_study()produces matplotlib figures similar toggdid()outputMissing data: diff-diff requires complete data; use
balance_panel()ordropna()first