Skip to contents

Estimates a classic difference-in-differences model of the form outcome ~ D + covariates | fixed effects on the package's internal fixed-effects OLS engine (numerically equivalent to fixest::feols()). The formula is the one used by event_study() and att(): swap the function to get effects by event time or aggregated staggered ATTs.

did() supersedes run_did() as of fixes 1.0.0.

Treatment is given in one of three ways, as in event_study():

# D: 1 for treated units in post-treatment periods
did(y ~ D | id + year, data = df)

# each unit's first treated period (NA = never treated)
did(y ~ 1 | id + year, data = df, time = year, timing = g)

# a treated-group dummy and one treatment date
did(y ~ treated | id + year, data = df, time = year, timing = 2006)

With timing, D = treatment * (time >= timing) is built internally. For staggered-adoption settings see att(); for dynamic effects see event_study().

Usage

did(
  formula,
  data,
  time = NULL,
  timing = NULL,
  unit = NULL,
  cluster = NULL,
  weights = NULL,
  conf_level = 0.95,
  vcov = "HC1",
  vcov_args = list(),
  outcome,
  treatment,
  fe = NULL,
  covariates = NULL
)

Arguments

formula

A model formula outcome ~ treatment + covariates | fixed effects. The left-hand side may be an expression (e.g. log(y)); without | the model has no fixed effects. The first right-hand-side term is D without timing, the treated-group dummy with a single timing date, and absent with a timing column (see Description). All variables must be columns of data.

data

A data.frame (panel format). formula and data may come in either order.

time

Unquoted time variable. Required with timing; with plain fixed effects such as | id + year it also lets did() report the unit (see event_study(), Panel identifiers).

timing

Treatment timing: an unquoted column giving each unit's first treatment period (NA = never treated), or a single numeric treatment date. Default NULL: the formula holds D.

unit

Unquoted unit identifier column (for metadata). Taken from the fixed effects when possible.

cluster

Clustering specification: a one-sided formula (~ id), a single character column name, or a numeric vector of length nrow(data). When cluster is specified and vcov is the default "HC1", cluster-robust standard errors are used automatically.

weights

Observation weights (formula or numeric vector).

conf_level

Confidence level(s) for CIs. Scalar or vector (e.g., c(0.90, 0.95)). Default 0.95.

vcov

VCOV type string passed to fixest::vcov(). Default "HC1". Ignored in favour of cluster-robust SE when cluster is supplied and vcov is left at its default "HC1".

vcov_args

Named list of additional arguments forwarded to fixest::vcov().

outcome, treatment, fe, covariates

[Deprecated] The model arguments of the older interface: the unquoted outcome, the treatment column (D, or the treated-group dummy when timing is given), a one-sided fixed-effects formula (NULL with both unit and time gives ~ unit + time), and a one-sided covariate formula. They still work 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 them when you use them: the second argument is now data.

Value

A did_result object (named list) with elements:

estimates

Data frame with the treatment coefficient: term, estimate, std.error, statistic, p.value, and conf_low_XX/conf_high_XX for each conf.level entry.

coeftable

Data frame with all kept coefficients (treatment and covariates): term, estimate, std.error, statistic, p.value.

vcov

Full coefficient covariance matrix.

fit_stats

Model-level statistics (nobs, R-squareds, sigma, logLik, AIC, BIC) used by broom::glance().

df.t

Degrees of freedom of the t inference.

Attributes: call, formula_str, outcome, treatment, timing, fe, vcov_type, cluster_vars, conf.level, N, N_units, N_treated, unit, time (and timing_var with a timing column). N, N_units, and N_treated describe the estimation sample (after rows with missing values are dropped), matching broom::glance(), not nrow(data).

See also

event_study() for dynamic effects, att() for staggered aggregation, and run_did() for the deprecated verb-style predecessor.

Examples

if (FALSE) { # \dontrun{
res <- did(y ~ D | id + year, data = df)

# Built from a treated-group dummy and the treatment date
res <- did(y ~ treated | id + year, data = df, time = year, timing = 2006)

# Covariates and cluster-robust SE
res <- did(y ~ D + x1 | id + year, data = df, cluster = ~ id)

print(res)
broom::tidy(res)
broom::glance(res)
# modelsummary::modelsummary(res)
} # }