| Type: | Package |
| Title: | Staggered DiD Tools: Event Studies and ATT Aggregation |
| Version: | 0.11.2 |
| Date: | 2026-06-11 |
| Description: | Provides tools for difference-in-differences (DiD) estimation and visualization with staggered adoption. Includes run_es() for event-study curves (dynamic effects by relative time) and calc_att() for aggregated ATT estimation (overall, by cohort, by calendar time). Supports multiple modern estimators: Callaway-Sant'Anna (2021), Sun-Abraham (2021), Borusyak-Jaravel-Spiess (2024), Wooldridge TWM, and Deb et al. FLEX. |
| Depends: | R (≥ 4.1.0) |
| Imports: | dplyr, ggplot2, fixest, broom, tibble, rlang, Rcpp, scales |
| License: | MIT + file LICENSE |
| Encoding: | UTF-8 |
| Suggests: | knitr, rmarkdown, haven, testthat, plotly, tidyr, did, didimputation, modelsummary, tinytable, lpSolveAPI, Rglpk, TruncatedNormal, Matrix, pracma, HonestDiD |
| VignetteBuilder: | knitr |
| URL: | https://github.com/yo5uke/fixes, https://yo5uke.com/fixes/ |
| BugReports: | https://github.com/yo5uke/fixes/issues |
| Config/roxygen2/version: | 8.0.0 |
| Config/roxygen2/markdown: | TRUE |
| LinkingTo: | Rcpp, RcppArmadillo |
| NeedsCompilation: | yes |
| Packaged: | 2026-06-11 13:52:24 UTC; yo5uk |
| Author: | Yosuke Abe [aut, cre] |
| Maintainer: | Yosuke Abe <yosuke.abe0507@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-06-11 15:10:02 UTC |
Autoplot for event-study results
Description
S3 method that plots an es_result (from run_es()).
It forwards arguments to plot_es().
Usage
## S3 method for class 'es_result'
autoplot(object, ci_level = 0.95, type = "ribbon", ...)
Arguments
object |
An |
ci_level |
Confidence level (numeric, e.g., 0.95). Passed to |
type |
Plot type: |
... |
Additional arguments forwarded to |
Value
A ggplot object.
Examples
# res <- run_es(...)
# ggplot2::autoplot(res, ci_level = 0.95, type = "ribbon")
Compute ATT Aggregations for Staggered Adoption Designs
Description
Estimates average treatment effects on the treated (ATT) using either the
Callaway-Sant'Anna (2021) or Borusyak-Jaravel-Spiess (2024) estimator,
aggregated to a single summary effect ("simple"), per-cohort effects
("by_cohort"), or per calendar-time effects ("by_time").
Usage
calc_att(
data,
outcome,
treatment = NULL,
time,
timing,
fe = NULL,
covariates = NULL,
cluster = NULL,
weights = NULL,
interval = 1,
time_transform = FALSE,
unit = NULL,
estimator = c("cs", "bjs"),
aggregation = c("simple", "by_cohort", "by_time"),
control_group = c("nevertreated", "notyettreated"),
anticipation = 0L,
conf.level = 0.95,
vcov = "HC1",
vcov_args = list()
)
Arguments
data |
A data.frame containing panel data. |
outcome |
Unquoted outcome variable (name or expression, e.g., |
treatment |
Unused; reserved for future use. |
time |
Unquoted calendar time variable (numeric). |
timing |
Unquoted column giving each unit's first treatment period
( |
fe |
Ignored (CS and BJS absorb fixed effects internally). |
covariates |
Ignored; reserved for future use. |
cluster |
Ignored; reserved for future use. |
weights |
Ignored; reserved for future use. |
interval |
Numeric time spacing (default |
time_transform |
Logical; if |
unit |
Unquoted unit identifier (required). |
estimator |
Estimation strategy: |
aggregation |
Aggregation type: |
control_group |
For |
anticipation |
For |
conf.level |
Numeric confidence level(s) (default |
vcov |
Ignored (SE is analytical for CS; approximate for BJS). |
vcov_args |
Ignored. |
Details
This function complements run_es(): use run_es() when you want a full
event-study curve (dynamic effects by relative time), and calc_att() when
you want aggregated ATT estimates that collapse the time dimension.
Value
A data.frame of class "att_result" with columns:
groupCohort or calendar time (
NAfor"simple").estimateATT point estimate.
std.errorStandard error.
statistict-statistic (
estimate / std.error).p.valueTwo-sided p-value (normal approximation).
conf_low_XX,conf_high_XXCI bounds for each
conf.level.
Attributes: aggregation, estimator, conf.level, N, N_units,
N_treated, N_nevertreated, control_group (CS only), att_gt (CS
raw ATT(g,t) table), tau_it (BJS unit-time effects table).
Aggregation formulas (CS estimator)
-
simple:
\theta = \sum_g (n_g/n_{treated}) \cdot \overline{ATT(g,\cdot)}where\overline{ATT(g,\cdot)}is the mean over post-treatment periods. -
by_cohort:
\theta(g) = \overline{ATT(g,\cdot)}per cohort. -
by_time:
\theta(t) = \sum_{g \le t} w(g,t) \cdot ATT(g,t)withw(g,t) = n_g / \sum_{g' \le t} n_{g'}.
Standard errors (BJS estimator)
BJS SEs are approximate (naive sample variance of unit-time effects). Cluster-robust SEs for BJS aggregations are planned for a future release.
See Also
run_es() for event-study (dynamic) estimates.
Contamination Weights for TWFE Event-Study Coefficients (Sun-Abraham 2021)
Description
Estimates the contamination weights \omega^{\ell''}_{e,\ell} that
decompose each TWFE event-study coefficient \hat\mu_{\ell''} into a
linear combination of cohort-specific ATTs (CATTs):
\hat\mu_{\ell''} = \sum_{(e,\ell)} \omega^{\ell''}_{e,\ell} \cdot
\widehat{CATT}_{e,\ell}
(Sun and Abraham 2021, Equation 20).
The weights are obtained via the OVB auxiliary regression (Eq. 12): for
each cohort-period CATT cell (e, \ell), regress the cohort-specific
indicator 1\{E_i = e\}\cdot 1\{t-E_i = \ell\} on all cohort-aggregated
relative-time indicators D^{\ell''}_{i,t} and two-way fixed effects.
The resulting regression coefficient on D^{\ell''}_{i,t} is
\omega^{\ell''}_{e,\ell}.
Usage
compute_contamination_weights(
data,
time,
timing,
unit,
fe = NULL,
baseline = -1L
)
Arguments
data |
A data.frame with one row per unit-period (balanced panel). |
time |
Unquoted name of the calendar time variable (numeric). |
timing |
Unquoted name of the first-treatment-period variable;
|
unit |
Unquoted name of the unit identifier. |
fe |
One-sided fixed-effects formula, e.g. |
baseline |
Integer reference (baseline) period excluded from the TWFE
specification (default |
Value
An object of class c("sa_contamination_weights", "data.frame")
with one row per (catt_cohort, catt_period, twfe_period) triple,
and columns:
catt_cohortCohort
e(first treatment period).catt_periodRelative event time
\ellof the CATT.twfe_periodRelative event time
\ell''of the TWFE coefficient being decomposed.weightContamination weight
\omega^{\ell''}_{e,\ell}.is_ownLogical;
TRUEwhencatt_period == twfe_period.
Attributes: baseline, cohorts, cohort_sizes, incl_periods.
Interpretation
-
Own-period cell (
catt_period == twfe_period):\omega^{\ell}_{{e,\ell}}represents the weight the TWFE estimator places onCATT_{e,\ell}. Under the SA IW estimator these equal the cohort-size weightsn_e / \sum n_{e'}. -
Cross-period cell (
catt_period != twfe_period): Any non-zero weight indicates contamination: the TWFE coefficient\hat\mu_{\ell''}also picks up treatment effects from period\ell \ne \ell''. -
Verification: the OVB identity (property iii) holds exactly, so
\hat\mu_{\ell''} = \sum_{(e,\ell)} \omega^{\ell''}_{e,\ell} \cdot \widehat{CATT}_{e,\ell}up to floating-point precision.
References
Sun, L. and Abraham, S. (2021). Estimating dynamic treatment effects in event studies with heterogeneous treatment effects. Journal of Econometrics, 225(2), 175–199.
See Also
plot_contamination_weights(), run_es()
Examples
## Not run:
# Estimate contamination weights
cw <- compute_contamination_weights(
data = panel_data,
time = year,
timing = first_treat,
unit = id,
fe = ~ id + year,
baseline = -1L
)
print(cw)
plot_contamination_weights(cw)
## End(Not run)
Glance at a did_result object
Description
Returns a single-row summary of model-level statistics from a run_did()
result. Delegates to broom::glance.fixest() which provides
nobs, r.squared, adj.r.squared, within.r.squared, AIC, BIC,
and related statistics.
Usage
glance.did_result(x, ...)
Arguments
x |
A |
... |
Additional arguments passed to |
Value
A one-row data frame of model-level statistics.
Examples
## Not run:
res <- run_did(df, outcome = y, treatment = D, fe = ~ id + year)
broom::glance(res)
## End(Not run)
Honest sensitivity analysis for parallel-trends violations
Description
Robust inference and sensitivity analysis for event-study / DiD designs following Rambachan and Roth (2023). Instead of assuming parallel trends holds exactly, it asks how large a violation of parallel trends would have to be before the causal conclusion changes, returning confidence sets for a post-treatment effect under a sequence of restrictions on the possible difference in trends.
Two restriction families are supported:
-
"relative_magnitude"(\Delta^{RM}(\bar M)): post-treatment violations are at most\bar Mtimes the largest pre-treatment violation (Rambachan & Roth 2023, Section 2.4.1). -
"smoothness"(\Delta^{SD}(M)): the difference in trends deviates from linearity by at mostMper period (Section 2.4.3).
Inference uses the Andrews-Roth-Pakes (ARP) conditional moment-inequality test (Section 3.2.1), which is uniformly valid and recommended for general restriction sets.
Usage
honest_sensitivity(
object = NULL,
type = c("relative_magnitude", "smoothness"),
Mvec = NULL,
l_vec = NULL,
alpha = 0.05,
gridPoints = 1000L,
betahat = NULL,
sigma = NULL,
numPrePeriods = NULL,
numPostPeriods = NULL
)
Arguments
object |
An |
type |
Restriction family: |
Mvec |
Numeric vector of restriction parameters. For
|
l_vec |
Numeric weight vector over post-treatment periods defining the
target |
alpha |
Significance level (default |
gridPoints |
Number of grid points for test inversion (default |
betahat |
Optional event-study coefficient vector (pre then post,
excluding the reference period), ordered by relative time. Required when
|
sigma |
Optional covariance matrix of |
numPrePeriods, numPostPeriods |
Optional integer counts; inferred from
|
Value
A data.frame of class "honest_result" with one row per
restriction value plus the original (parallel-trends) confidence interval,
with columns M, lb, ub, method, and type. The breakdown value
(largest restriction at which the robust CI still excludes 0) is stored in
attr(., "breakdown").
References
Rambachan, A. and Roth, J. (2023). A More Credible Approach to Parallel Trends. Review of Economic Studies, 90(5), 2555-2591.
See Also
Plot the ATT(g,t) matrix from a Callaway-Sant'Anna event study
Description
Visualises the full cohort-by-period ATT(g,t) matrix stored in the
att_gt attribute of an es_result object produced by
run_es(estimator = "cs"). Two display styles are available:
-
"heatmap": a tile plot with calendar timeton the x-axis and cohortgon the y-axis, colour-filled by the point estimate. Cells whose pointwise confidence interval excludes zero are marked with a filled dot; cells that are simultaneously significant (when bootstrap data are available) are additionally marked with an open diamond. -
"facet": one panel per cohort showing ATT(g,t) over calendar timetwith a pointwise confidence ribbon, mirroring the style ofplot_es(). A lighter simultaneous CI ribbon is overlaid when bootstrap data are available.
Both types draw a vertical dashed line at t = g (treatment onset)
for each cohort.
Usage
plot_att_gt(
x,
type = c("heatmap", "facet"),
ci_level = 0.95,
zero_line = TRUE,
theme = c("bw", "minimal", "classic"),
color = "#B25D91FF",
fill = "#B25D91FF",
alpha = 0.2
)
## S3 method for class 'att_gt_result'
autoplot(object, ...)
Arguments
x |
An |
type |
|
ci_level |
Confidence level for pointwise intervals (default 0.95). |
zero_line |
Logical; draw a horizontal reference line at zero in the
|
theme |
One of |
color |
Line and point colour used in the |
fill |
Ribbon fill colour in the |
alpha |
Ribbon transparency in the |
object |
An |
... |
Passed to |
Value
A ggplot2::ggplot() object.
Bootstrap annotation
When attr(x, "bootstrap") is present (i.e., run_es() was
called with bootstrap = TRUE), both plot types add simultaneous
inference overlays sourced from the (g,t)-level bootstrap object.
See Also
Examples
## Not run:
cs_result <- run_es(data = mydata, outcome = y, time = year,
timing = g, unit = id, fe = ~id + year,
staggered = TRUE, estimator = "cs")
plot_att_gt(cs_result)
plot_att_gt(cs_result, type = "facet")
## End(Not run)
Plot Contamination Weights as a Tile Heatmap
Description
Creates a ggplot2 tile heatmap of the contamination weights returned by
compute_contamination_weights().
Each cell at position (twfe_period, catt_label) shows the
weight \omega^{\ell''}_{e,\ell}: how much of CATT_{e,\ell}
leaks into the TWFE coefficient \hat\mu_{\ell''}.
-
Diagonal cells (
catt_period == twfe_period): own-period weights (sum across cohorts should be\approx 1). -
Off-diagonal cells: cross-period contamination (ideally close to zero under treatment effect homogeneity).
Usage
plot_contamination_weights(
x,
limit_abs = NULL,
midpoint = 0,
low = "#2166AC",
mid = "white",
high = "#B2182B",
theme = c("bw", "minimal", "classic"),
show_values = FALSE,
value_digits = 2L
)
Arguments
x |
An |
limit_abs |
Numeric; symmetric colour scale limit |
midpoint |
Numeric; midpoint of the diverging colour scale (default 0). |
low |
Colour for negative weights (default |
mid |
Colour for zero weight (default |
high |
Colour for positive weights (default |
theme |
Character; |
show_values |
Logical; overlay weight values in each tile (default
|
value_digits |
Integer; decimal digits when |
Value
A ggplot2::ggplot() object.
See Also
compute_contamination_weights()
Plot event-study results with ribbons or error bars
Description
Plot event-study results with ribbons or error bars
Usage
plot_es(
data,
ci_level = 0.95,
type = "ribbon",
vline_val = 0,
vline_color = "#000",
hline_val = 0,
hline_color = "#000",
linewidth = 1,
pointsize = 2,
alpha = 0.2,
barwidth = 0.2,
color = "#B25D91FF",
fill = "#B25D91FF",
theme_style = "bw",
show_simultaneous = FALSE
)
Arguments
data |
An object of class |
ci_level |
Confidence level to display (e.g., 0.95). |
type |
One of |
vline_val, hline_val |
Numeric locations for vertical/horizontal reference lines (default 0). |
vline_color, hline_color |
Colors for reference lines. |
linewidth, pointsize, alpha, barwidth |
Styling parameters for lines/points/bands/bars. |
color, fill |
Optional, override line/point color and ribbon fill. |
theme_style |
One of |
show_simultaneous |
Logical; if |
Value
A ggplot object.
Examples
# Assuming `res <- run_es(...)`
# p <- plot_es(res, ci_level = 0.95, type = "ribbon")
# print(p)
Interactive event-study plot with hover details
Description
Creates an interactive plotly visualization of event study results with hover-over displays showing coefficients, confidence intervals, and other details.
Usage
plot_es_interactive(
data,
ci_level = 0.95,
vline_val = 0,
hline_val = 0,
vline_color = "#000",
hline_color = "#000",
color = "#B25D91FF",
fill = "#B25D91FF",
alpha = 0.2,
linewidth = 2,
markersize = 8,
show_ribbon = TRUE,
show_simultaneous = FALSE,
height = NULL,
width = NULL
)
Arguments
data |
An object of class |
ci_level |
Confidence level to display (e.g., 0.95). Default is 0.95. |
vline_val |
Numeric location for vertical reference line (default 0). |
hline_val |
Numeric location for horizontal reference line (default 0). |
vline_color |
Color for vertical reference line (default "#000"). |
hline_color |
Color for horizontal reference line (default "#000"). |
color |
Point and line color (default "#B25D91FF"). |
fill |
Ribbon/band fill color (default "#B25D91FF"). |
alpha |
Ribbon transparency (default 0.2). |
linewidth |
Line width (default 2). |
markersize |
Marker size (default 8). |
show_ribbon |
Logical; if TRUE, shows confidence interval as a ribbon band (default TRUE). |
show_simultaneous |
Logical; if |
height |
Plot height in pixels (default NULL for auto). |
width |
Plot width in pixels (default NULL for auto). |
Details
The hover tooltip displays:
Relative time to treatment
Point estimate (coefficient)
Confidence interval bounds
Standard error
P-value
Simultaneous CI bounds (when
show_simultaneous = TRUE)
Value
A plotly object that can be displayed interactively.
Examples
## Not run:
# Assuming res <- run_es(...)
plot_es_interactive(res)
plot_es_interactive(res, ci_level = 0.99, show_ribbon = FALSE)
plot_es_interactive(res, show_simultaneous = TRUE)
## End(Not run)
Plot a honest sensitivity analysis
Description
Visualises the output of honest_sensitivity(): robust confidence intervals
for the target effect under progressively weaker parallel-trends restrictions
(increasing M or \bar M), alongside the original confidence
interval that assumes parallel trends holds exactly. This is the "top-down"
sensitivity plot of Rambachan and Roth (2023).
Usage
plot_honest(x, ...)
## S3 method for class 'honest_result'
autoplot(object, ...)
Arguments
x |
A |
... |
Unused. |
object |
A |
Value
A ggplot object.
See Also
Run a basic two-way fixed-effects DiD model
Description
Estimates a classic difference-in-differences model of the form
outcome ~ D_it | fe using fixest::feols().
There are two ways to supply the treatment indicator:
Option A — pre-built D_it (maximum flexibility):
df$D <- as.integer(df$treated & df$year >= 2006) run_did(df, outcome = y, treatment = D, fe = ~ id + year)
Option B — timing-based construction (convenience; consistent with
run_es() and calc_att()):
run_did(df, outcome = y, treatment = treated, time = year, timing = 2006,
fe = ~ id + year)
Here treatment is a binary group indicator (1 = treated unit, 0 = control),
time is the calendar-time variable, and timing is the scalar treatment
onset period. Internally D_it = treatment * (time >= timing) is constructed
automatically. For staggered-adoption settings use calc_att(); for dynamic
event-study estimates use run_es().
Usage
run_did(
data,
outcome,
treatment,
timing = NULL,
fe = NULL,
unit = NULL,
time = NULL,
covariates = NULL,
cluster = NULL,
weights = NULL,
conf.level = 0.95,
vcov = "HC1",
vcov_args = list()
)
Arguments
data |
A data.frame (panel format). |
outcome |
Unquoted outcome variable or expression (e.g., |
treatment |
Unquoted column name. When |
timing |
Numeric scalar. When provided, |
fe |
One-sided formula specifying fixed effects, e.g. |
unit |
Unquoted unit identifier column (for metadata and |
time |
Unquoted time variable column. Used for (a) |
covariates |
One-sided formula of additional controls, e.g. |
cluster |
Clustering specification: a one-sided formula ( |
weights |
Observation weights (formula or numeric vector). |
conf.level |
Confidence level(s) for CIs. Scalar or vector
(e.g., |
vcov |
VCOV type string passed to |
vcov_args |
Named list of additional arguments forwarded to
|
Value
A did_result object (named list) with elements:
estimatesData frame with the treatment coefficient:
term,estimate,std.error,statistic,p.value, andconf_low_XX/conf_high_XXfor eachconf.levelentry.modelThe underlying
fixestmodel object.
Attributes: call, formula_str, outcome, treatment, timing,
fe, vcov_type, cluster_vars, conf.level, N, N_units,
N_treated, unit, time.
Examples
## Not run:
# Option A: pre-built D_it
df$D <- as.integer(df$treated & df$year >= 2006)
res <- run_did(df, outcome = y, treatment = D, fe = ~ id + year)
# Option B: timing-based construction
res <- run_did(df, outcome = y, treatment = treated, time = year,
timing = 2006, fe = ~ id + year)
# Cluster-robust SE
res <- run_did(df, outcome = y, treatment = D, fe = ~ id + year,
cluster = ~ id)
print(res)
broom::tidy(res)
broom::glance(res)
# modelsummary::modelsummary(res)
## End(Not run)
Event Study Estimation for Panel Data
Description
Runs an event study regression on panel data, supporting both classic (universal timing) and staggered (unit-varying timing via sunab).
The function builds the design (lead/lag factor or sunab), estimates with fixest::feols(), and returns a tidy table with metadata.
Usage
run_es(
data,
outcome,
treatment = NULL,
time,
timing,
fe = NULL,
lead_range = NULL,
lag_range = NULL,
covariates = NULL,
cluster = NULL,
weights = NULL,
baseline = -1L,
interval = 1,
time_transform = FALSE,
unit = NULL,
staggered = FALSE,
method = c("classic", "sunab"),
estimator = c("twfe", "cs", "sa", "bjs", "twm", "flex"),
control_group = c("nevertreated", "notyettreated"),
anticipation = 0L,
conf.level = 0.95,
vcov = "HC1",
vcov_args = list(),
bootstrap = FALSE,
B = 999L,
alpha = 0.05,
boot_seed = NULL,
group = NULL,
trends = FALSE
)
Arguments
data |
A data.frame containing panel data. |
outcome |
Unquoted outcome (name or expression, e.g., |
treatment |
Unquoted treatment indicator (0/1 or logical). Used only when |
time |
Unquoted time variable (numeric or Date). |
timing |
For |
fe |
One-sided fixed-effects formula, e.g., |
lead_range, lag_range |
Integers for pre/post windows. If |
covariates |
One-sided formula of additional controls, e.g., |
cluster |
Cluster specification (one-sided formula like |
weights |
Observation weights (a name/one-sided formula or a numeric vector of length |
baseline |
Integer baseline period (default |
interval |
Numeric spacing of the time variable (default |
time_transform |
Logical; if |
unit |
Unit identifier variable (required when |
staggered |
Logical; if |
method |
Either |
estimator |
Estimation strategy: |
control_group |
For |
anticipation |
For |
conf.level |
Numeric vector of confidence levels (default |
vcov |
VCOV type passed to |
vcov_args |
List of additional arguments forwarded to |
bootstrap |
Logical; if |
B |
Integer number of bootstrap draws (default |
alpha |
Significance level for the simultaneous band (default |
boot_seed |
Integer seed for the bootstrap RNG; |
group |
Unquoted group identifier for |
trends |
Logical; for |
Value
A data.frame of class "es_result" with columns:
-
term,estimate,std.error,statistic,p.value -
conf_low_XX,conf_high_XX(for each requestedconf.level) -
relative_time(integer; 0 = event),is_baseline(logical; marks the reference period)
Attributes include: lead_range, lag_range, baseline, interval, call, model_formula, conf.level,
N, N_units, N_treated, N_nevertreated, fe, vcov_type, cluster_vars, staggered, sunab_used.
Key Features
One-step event study: specify outcome, treatment, time, timing, fixed effects, and (optionally) covariates.
Switch between Classic (factor expansion) and Staggered-SAFE (
method = "sunab").Flexible clustering, weights, and VCOV choices (e.g.,
vcov = "HC1" | "HC3" | "CR2" | "iid" ...).Automatic lead/lag window detection and customizable baseline period.
Returns an
"es_result"object compatible withprint()andautoplot().
Tidy a did_result object
Description
Returns a tidy data frame of model coefficients from a run_did() result.
Delegates to broom::tidy.fixest() on the underlying fixest model so
that all regressors (treatment and covariates) appear in the output — the
format expected by modelsummary::modelsummary().
Usage
## S3 method for class 'did_result'
tidy(x, conf.int = FALSE, conf.level = 0.95, ...)
Arguments
x |
A |
conf.int |
Logical; add |
conf.level |
Confidence level for |
... |
Additional arguments passed to |
Value
A data frame with columns term, estimate, std.error,
statistic, p.value (and optionally conf.low, conf.high).
Examples
## Not run:
res <- run_did(df, outcome = y, treatment = D, fe = ~ id + year)
broom::tidy(res)
broom::tidy(res, conf.int = TRUE)
## End(Not run)