Skip to contents

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

att(
  formula,
  data,
  time,
  timing = NULL,
  unit = NULL,
  estimator = c("cs", "bjs"),
  aggregation = c("simple", "by_cohort", "by_time"),
  control_group = c("nevertreated", "notyettreated"),
  anticipation = 0L,
  conf_level = 0.95,
  interval = 1,
  time_transform = FALSE,
  outcome
)

Arguments

formula

A model formula outcome ~ treatment | fixed effects (see event_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. formula and data may 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; otherwise att() stops with an error naming the offending units.

unit

Unquoted unit identifier; taken from the fixed effects when they are time plus 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, default 0L).

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 via dplyr::dense_rank(). A timing column or value is then read as an index; a D column needs no adjustment.

outcome

[Deprecated] 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 now data.

Value

A data.frame of class "att_result" with columns:

group

Cohort or calendar time (NA for "simple").

estimate

ATT point estimate.

std.error

Standard error.

statistic

t-statistic (estimate / std.error).

p.value

Two-sided p-value (normal approximation).

conf_low_XX, conf_high_XX

CI 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.

Examples

if (FALSE) { # \dontrun{
att(y ~ 1 | id + year, data = df, time = year, timing = g)
att(y ~ D | id + year, data = df, time = year,
    estimator = "bjs", aggregation = "by_cohort")
} # }