For a fitted object from fetwfe(), etwfe(), or betwfe(), computes the
pooled event-time treatment-effect estimates tau_E(e), defined as
cohort-weighted averages of the cell-level treatment-effect estimates at
each post-treatment event time e = t - g (where t is calendar time and
g is the cohort's first-treated calendar time). Weights are
sample-cohort-size weights (matching did::aggte(type = "dynamic")
convention).
Standard errors combine two terms, mirroring the package's existing
overall-ATT SE machinery: var_1(e) from regression-coefficient noise
(computed via the same gram_inv machinery the package uses for cohort
SEs, or the cluster-robust sandwich under se_type = "cluster"), and
var_2(e) from cohort-probability noise (analog of the existing
getSecondVarTermOLS / getSecondVarTermDataApp machinery, with the
multinomial Jacobian restricted to cohorts valid at event time e).
Combined as sqrt(var_1 + var_2) by default (asymptotically exact
under paper Theorem te.asym.norm.thm(\(c'\)) / Assumption (\(\Psi\)-IF), which
the package's default cohort-sample-proportions estimator satisfies);
the conservative Cauchy-Schwarz bound sqrt(var_1 + var_2 + 2 sqrt(var_1 * var_2)) is available via se_type = "conservative"
(for users with non-(\(\Psi\)-IF) propensity-score estimators). When
indep_counts was supplied at fit time, the tight formula applies
regardless of se_type (two-sample regime, Theorem (b)).
Arguments
- x
A fitted object of class
"fetwfe","etwfe", or"betwfe".- alpha
(Optional) Significance level for confidence intervals. Defaults to
x$alpha(the alpha used at fit time).- ci_type
(Optional) Character; one of
"simultaneous"or"pointwise", orNULL(default). Controls whether the returnedci_low/ci_highare the event-study-family simultaneous (family-wise, uniform) band or the per-event-time pointwise Wald intervals.NULLinherits the fit's storedci_type(so a fit made with the defaultci_type = "simultaneous"yields simultaneous event-study bands, whichprint/summary/plotthen display); for objects fitted before version 1.16.0 (noci_typeslot)NULLfalls back to"pointwise". Thesecolumn is identical under both settings; the interval bounds, their critical-value multiplier, and thep_valuediffer (under"simultaneous"thep_valueis the single-step max-T multiplicity- adjusted dual of the band, under"pointwise"the per-effect Wald p-value; #200). Degenerate event times (no valid contributing cohorts) carryNAbounds andNAp_valueunder both settings.High-dimensional (
p >= NT): the"simultaneous"band here is the analytic post-selection band on the selected support (orNAbounds on agls = FALSEfit), not the uniformly-valid desparsified band. For the valid high-dimensional event-study band callsimultaneousCIs()withfamily = "event_study",method = "bootstrap"(Theoremdebiased.highdim.joint.thm, validated near-nominally in Faletto 2025). Such a call emits a warning; see Details.
Value
A data frame with class c("eventStudy", "data.frame") and
columns:
- event_time
Integer; event time
e = t - g, ranging from 0 toT - 2.- n_cohorts
Integer; number of cohorts contributing to the pooled estimate at event time
e.- estimate
Numeric; the pooled event-time ATT estimate.
- se
Numeric; combined standard error.
- ci_low
Numeric; lower bound of the (1 - alpha) Wald CI.
- ci_high
Numeric; upper bound of the (1 - alpha) Wald CI.
- p_value
Numeric; follows the fit's
ci_type. Under"pointwise", the two-sided Wald p-value (2 * pnorm(-|estimate / se|)); under"simultaneous"(the default), the single-step max-T multiplicity-adjusted (family-wise) p-value matching the simultaneous band (#200).NAwhenseis0orNA.
The returned frame also carries the logical attribute
attr(., "band_applied") (#460), TRUE when its ci_low / ci_high ARE
the event-study-family simultaneous band and FALSE when they are the
pointwise Wald bounds — because the fit asked for pointwise intervals,
because standard errors were unavailable on the selected support, or
because the band construction degraded. It is not the same
question as the fit's ci_type, which records what was requested, nor as
the fit's catt_band_applied slot, which answers it for the cohort family:
the two families are built from different contrast matrices and fail
independently, so one can be applied while the other is not. It is an
attribute rather than a slot because no slot could hold it. This band is
recomputed per call with that call's arguments, so one fit answers
differently for different calls: on a fit carrying
catt_band_applied = TRUE, attr(eventStudy(fit), "band_applied") is
TRUE while attr(eventStudy(fit, ci_type = "pointwise"), "band_applied")
is FALSE. Like the slot, it records only that the band was applied: TRUE does not imply the
bounds are wider than the pointwise ones, and does not imply every row is
informative — degenerate event times are masked to NA while the band is
still reported as applied.
Only post-treatment event times (e >= 0) are included; pre-treatment
placebo periods would require an extended regression specification and
are out of scope for this initial release.
Details
High-dimensional (p >= NT) bands warn. When the returned
"simultaneous" bounds are the p >= NT post-selection fallback rather than
a uniformly-valid band – which is every high-dimensional fit that reaches
the band at all, since this accessor always takes the analytic route – the
call emits a warning naming the remedy (#433). A high-dimensional fit under
ci_type = "pointwise", or one whose standard errors are unavailable, never
builds a simultaneous band and so does not warn. A #308 coverage study
measures the fallback band at roughly
0.09 against 0.95 nominal, even when the selected support is low-dimensional.
The remedy is simultaneousCIs() with family = "event_study" and
method = "bootstrap" on a fetwfe() fit; no valid high-dimensional band
exists for the other estimators.
The warning is the same classed condition simultaneousCIs() signals,
"fetwfe_highdim_postselection_band", so it can be caught or muffled
precisely:
withCallingHandlers(
eventStudy(fit),
fetwfe_highdim_postselection_band = function(w) invokeRestart("muffleWarning")
)print(), summary(), and plot() call this function internally and muffle
that condition, so they do not repeat it on every render; print() and
summary() render a two-line caveat under their CATT preview instead, and
plot() renders none.
See also
cohortStudy() for the parallel per-cohort accessor;
cohortTimeATTs() for the fully disaggregated per-(cohort, time) accessor.
Examples
if (FALSE) { # \dontrun{
coefs <- genCoefs(G = 3, T = 6, d = 2, density = 0.5, eff_size = 2)
dat <- simulateData(coefs, N = 120, sig_eps_sq = 1, sig_eps_c_sq = 0.5, seed = 123)
res <- fetwfeWithSimulatedData(dat)
eventStudy(res)
} # }