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").
Arguments
- formula
A model formula
outcome ~ treatment | fixed effects(seeevent_study()). The fixed effects only serve to name the unit: both estimators absorb unit and time effects themselves, and neither takes covariates.- data
A data.frame containing panel data.
formulaanddatamay come in either order.- time
Unquoted calendar time variable (numeric).
- timing
Treatment timing: an unquoted column giving each unit's first treatment period (
NA= never treated), or a single treatment date. Omit it when the formula holds a D column. A timing column must have the same value on every row of a unit; otherwiseatt()stops with an error naming the offending units.- unit
Unquoted unit identifier; taken from the fixed effects when they are
timeplus one other column.- estimator
Estimation strategy:
"cs"(Callaway-Sant'Anna 2021, default) or"bjs"(Borusyak-Jaravel-Spiess 2024).- aggregation
Aggregation type:
"simple"(overall ATT, default),"by_cohort"(one ATT per treatment cohort), or"by_time"(one ATT per calendar time period).- control_group
For
estimator = "cs": comparison group,"nevertreated"(default) or"notyettreated".- anticipation
For
estimator = "cs": number of anticipation periods before treatment (non-negative integer, default0L).- conf_level
Numeric confidence level(s) (default
0.95). Multiple levels are supported, e.g.,c(0.90, 0.95).- interval
Numeric time spacing (default
1; informational only).- time_transform
Logical; if
TRUE, creates consecutive integer time within unit viadplyr::dense_rank(). A timing column or value is then read as an index; a D column needs no adjustment.- outcome
The unquoted outcome of the older interface. Still works in fixes 1.x, with a deprecation warning that shows the equivalent formula call; fixes 2.0.0 is planned to accept only
formula. Name it when you use it: the second argument is nowdata.
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, unit_var, time_var, control_group
(CS only), att_gt (CS raw ATT(g,t) table), tau_it (BJS unit-time
effects table).
Details
This function complements event_study(): use event_study() when you
want a full event-study curve (dynamic effects by relative time), and
att() when you want aggregated ATT estimates that collapse the time
dimension. Both take the same formula, and treatment is given in the same
three ways (see event_study(), Specifying treatment):
att(y ~ D | id + year, data = df, time = year) # D column
att(y ~ 1 | id + year, data = df, time = year, timing = g) # timing column
att(y ~ treated | id + year, data = df, time = year, timing = 2005)The result has broom::tidy() and broom::glance() methods, so it can be
passed to modelsummary::modelsummary() (rendered with tinytable) to build
a publication table — including stacking several estimators (e.g. did()
TWFE, CS, and BJS) side by side in one table.
att() supersedes calc_att() as of fixes 1.0.0.
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)\) with \(w(g,t) = n_g / \sum_{g' \le t} n_{g'}\).
Rows with a missing or non-finite outcome, time, or unit are dropped and
the panel is balanced by dropping units not observed in every period, as
did::att_gt() does by default; both steps warn, and N, N_units,
N_treated and N_nevertreated describe the balanced sample.
Standard errors
For "cs", the SE of each aggregate is computed from its unit-level
influence function, which carries the covariance across the ATT(g,t)
it averages and the estimation of the cohort weights (the same SEs as
did::aggte()).
For "bjs", the SE is the conservative cluster-robust SE of Borusyak et
al. (2024, Theorem 3), clustered by unit, including the uncertainty from
the imputation regression. It matches didimputation::did_imputation().
See also
event_study() for event-study (dynamic) estimates, and
calc_att() for the deprecated verb-style predecessor.