In staggered difference-in-differences designs the treatment
effect is a vector of cohort-time effects rather than a single number.
Estimating each separately is unbiased but imprecise when some are equal,
while pooling them all is precise but biased under genuine heterogeneity.
This package treats the choice as a partition-selection problem on the
cohort-time cells and provides two estimators for it: a Dirichlet process
mixture fitted by a collapsed Gibbs sampler, whose posterior marginalises
over the unknown partition and reports co-clustering probabilities, and an
'L0'-penalised estimator that returns a single partition and arises as the
fixed-variance maximum a posteriori solution of the same model. Also
provides tests for whether the cohort-time effects carry recoverable
heterogeneity at all, sampler diagnostics including exact enumeration of
the partition posterior for small designs, regularisation paths for both
estimators, and a calibrated data-generating process. All estimators accept
a vector of first-stage cohort-time effects with their joint covariance, so
any heterogeneity-robust first-stage estimator may be used. Methods are
described in Arora and Wagle (2026)
Partial homogeneity in staggered difference-in-differences.
In a staggered DiD design the treatment effect is not a single number but a vector of cohort-time effects (CATTs), one per cohort-time cell. Estimating every one separately is unbiased but inefficient when some are in fact equal; pooling them all into a single TWFE coefficient is efficient but biased whenever the heterogeneity is genuine.
phdid treats the choice between these extremes as a partition-selection
problem on the cells, and implements the two estimators of Arora and Wagle
(2026) that solve it, along with the specification tests and diagnostics needed
to know whether either should be used at all.
# install.packages("remotes")
remotes::install_github("ronwag2005/phdid")
With G treated cohorts and T periods there are K cohort-time cells. The
true effect vector exhibits partial homogeneity if there is a partition of
those cells into m < K groups within which the effects are equal. Under the
true partition, the grouped estimator is both unbiased and strictly more
efficient than the flexible one -- in a balanced design, exactly |C_p| times
more efficient for every cell in a group of size |C_p|.
The partition is unknown, so the package recovers it two ways:
l0_ph() |
bayes_ph() |
|
|---|---|---|
| Returns | one partition | a posterior over partitions |
| Selects by | BIC on the agglomeration path, or a fixed lambda |
Dirichlet Process prior, collapsed Gibbs |
| Intervals | condition on the selected partition | marginalize over the partition |
| Coverage when the partition is uncertain | 0.57-0.62 | 0.79-0.81 |
| Coverage when effects are separated | 0.95 | 0.94 |
The two are not rivals: l0_ph() is the fixed-variance MAP of the same
Bayesian model, differing only in the partition prior. Use l0_ph() when you
want one interpretable grouping to report and bayes_ph() for inference.
library(phdid)
library(did)
data(mpdta, package = "did")
# Any heterogeneity-robust first stage will do.
first <- att_gt(yname = "lemp", tname = "year", idname = "countyreal",
gname = "first.treat", control_group = "notyettreated",
data = mpdta, bstrap = FALSE)
d <- ph_data(first) # keeps post-treatment cells + the exact joint covariance
# 1. Is there heterogeneity to recover at all?
homogeneity_test(d)
#> chi-squared = 25.29 on 6 df, p = 0.000302
#> heterogeneity share: 80%
#> Reading: recoverable heterogeneity.
# 2. One partition to report.
l0_ph(d)
#> Groups (m) : 4 (selected by BIC)
#> Mean variance ratio among pooled cells: 0.50
# 3. Inference that accounts for not knowing the partition.
fit <- bayes_ph(d, alpha = 1, iters = 20000, burn = 2000)
aggregate(fit, "overall")
#> term estimate conf.low conf.high
#> overall -0.0239 -0.0476 -0.0004
coclustering(fit) # which groupings are firm
plot(fit) # the co-clustering heatmap
The covariance is not diagonal. Cohort-time cells share units and share the
unit and time fixed effects, so their estimates are correlated. Using the exact
joint covariance is what makes the reported intervals honest: treating the
cells as independent understates the posterior variance of aggregates by about
3.5x in the paper's design and can misstate individual co-clustering
probabilities by up to 0.39. ph_data() warns when handed a diagonal
covariance, and covariance_check() quantifies the difference on your design.
A partition is only worth reporting when the effects are separated enough to
be recovered. Below that threshold both estimators return groupings that are
quantiles of noise. In the paper's second application the l0 estimator splits
138 event effects into five neat "bands" while a randomization test cannot
reject a single common effect. Run homogeneity_test() first; it says so
plainly rather than letting the output speak for itself.
Estimation -- ph_data() (three entry points: a did::att_gt() fit, a
micro panel, or any (tau, Sigma) pair), l0_ph(), bayes_ph(), ph_fit(),
flex_twfe(), pooled_twfe().
Inference -- aggregate() for overall ATT, event-study, by-cohort and
by-calendar estimands computed per posterior draw; coclustering();
point_partition() (Wade-Ghahramani VI and Binder losses); confint().
Specification testing -- homogeneity_test(), bundling a model-implied
common-effect chi^2 test, an excess-dispersion heterogeneity share, and the
placebo gauge and within-cell randomization test.
Diagnostics -- enumerate_partitions() for the exact posterior on small
designs, ph_rhat() for multi-chain convergence, alpha_sensitivity() for the
prior path, covariance_check() for exact vs. diagonal.
Simulation -- ph_design(), ph_truth(), ph_sample(), sim_study(),
reproducing the paper's Monte Carlo tables.
The methods implemented here are joint work by Parush Arora and Rohan Wagle (Department of Economics, Ashoka University). The simulation and application code that this package generalizes was written by Parush Arora; the package itself is written and maintained by Rohan Wagle. Both are copyright holders under the MIT license.
If you use phdid, please cite both the Ashoka University discussion paper
and the SSRN working paper:
Arora, Parush and Rohan Wagle (2026). "A Bayesian Approach to Partial Homogeneity in Staggered Difference-in-Difference." Ashoka University Economics Discussion Paper 166. Link
Arora, Parush and Rohan Wagle (2026). "A Bayesian Approach to Partial Homogeneity in Staggered Difference-in-Difference" (March 01, 2026). SSRN Working Paper 7207083. SSRN | doi:10.2139/ssrn.7207083
citation("phdid")
Arora, P. and Wagle, R. (2026). A Bayesian Approach to Partial Homogeneity in Staggered Difference-in-Difference. Ashoka University Economics Discussion Paper 166. https://www.ashoka.edu.in/research/a-bayesian-approach-to-partial-homogeneity-in-staggered-difference-in-difference/
Arora, P. and Wagle, R. (2026). A Bayesian Approach to Partial Homogeneity in Staggered Difference-in-Difference (March 01, 2026). SSRN Working Paper 7207083. https://doi.org/10.2139/ssrn.7207083
Callaway, B. and Sant'Anna, P. H. C. (2021). Difference-in-Differences with Multiple Time Periods. Journal of Econometrics 225(2), 200-230.
Neal, R. M. (2000). Markov Chain Sampling Methods for Dirichlet Process Mixture Models. JCGS 9(2), 249-265.
Wade, S. and Ghahramani, Z. (2018). Bayesian Cluster Analysis: Point Estimation and Credible Balls. Bayesian Analysis 13(2), 559-626.
Wooldridge, J. M. (2025). Two-Way Fixed Effects, the Two-Way Mundlak Regression, and Difference-in-Differences Estimators. Empirical Economics 69, 2545-2587.
MIT (c) 2026 Rohan Wagle and Parush Arora. See LICENSE.md.