Skip to contents

This guide uses the Sequeira (2016) application from Sant’Anna and Xu (2026) to show how external researchers should use compdid in a new empirical project. It is not a numerical replication guide for Table 4. The package now uses more guarded defaults than the historical paper scripts: generalized propensity score rows are normalized by default, local multinomial-logit fits use more stable numerical routines, and cross-fitted nuisance estimation is available as the main front door. Those choices can move the application estimates relative to the paper-final archived output.

If the goal is to reproduce the published paper numbers exactly, use the separate vignette:

vignette("sequeira-application-replication", package = "compdid")

Data

The package ships the application data so users can work through the example without cloning the original replication repository.

library(compdid)

data("sequeira_bribes", package = "compdid")
dim(sequeira_bribes)

The outcome variables are:

  • bp: whether a bribe was paid.
  • lba: log transformed bribe amount.
  • lba_value: log transformed bribe amount relative to shipment value.
  • lba_tonnage: log transformed bribe amount relative to shipment tonnage.

The treatment indicator is tariff_change_2008, the post-period indicator is post_2008, and clustered inference in the paper uses hc_4digits.

Prepare The Analysis Data

The application needs a few deterministic transformations. The key point for new projects is to write preprocessing explicitly and keep the analysis sample fixed before fitting models.

prepare_sequeira <- function(data) {
  data$lba_value[2783L] <- 0
  data$monitor <- data$monitor - 1
  data$psi <- 2 - data$psi
  data$ltonnage <- log(data$tonnage + 1)

  vars <- c(
    "bp", "lba", "lba_value", "lba_tonnage",
    "post_2008", "tariff_change_2008", "hc_4digits",
    "lvalue_tonnage", "ltonnage", "lvalue_shipment_metical",
    "tariff2007",
    "differentiated", "agri", "perishable", "dfs", "day_w_arrival",
    "monitor", "psi", "rsa", "term"
  )
  data[complete.cases(data[, vars]), vars]
}

app <- prepare_sequeira(sequeira_bribes)

For most new empirical work, start with comp_did() and the default DML nuisance path. This avoids high-dimensional local-polynomial smoothing and keeps the workflow close to the package’s best-tested interface.

xvars <- c(
  "lvalue_tonnage", "tariff2007",
  "differentiated", "agri", "perishable", "dfs", "day_w_arrival",
  "monitor", "psi", "rsa", "term"
)
xform <- reformulate(xvars)

fit_bp <- comp_did(
  yname = "bp",
  tname = "post_2008",
  dname = "tariff_change_2008",
  xformula = xform,
  data = app,
  nuisance_method = "dml",
  K = 5,
  stationary = TRUE,
  stationarity_test = TRUE,
  boot = FALSE,
  inffunc = TRUE,
  seed = 1234
)

summary(fit_bp)

The non-stationary ATT is the compositional-change-robust target. The stationary fit and stationarity test are useful diagnostics when the researcher wants to compare the no-compositional-change restriction against the more robust target.

Clustered Inference

The paper clusters at the four-digit HS-code level. With inffunc = TRUE, use the fitted influence functions and drdid_cluster_bootstrap().

clustered_bp <- drdid_cluster_bootstrap(
  nonstationary_fit = fit_bp$att,
  stationary_fit = fit_bp$stationary,
  group = app$hc_4digits,
  nboot = 9999,
  seed = 1234,
  keep_boots = FALSE
)

clustered_bp

Multiple Outcomes

A clean applied workflow makes the outcome-specific choices explicit. The example below mirrors the paper’s use of different scale variables across outcomes, while still using the modern DML front door.

outcome_specs <- list(
  bp = c("lvalue_tonnage", "tariff2007"),
  lba = c("lvalue_tonnage", "tariff2007"),
  lba_value = c("ltonnage", "tariff2007"),
  lba_tonnage = c("lvalue_shipment_metical", "tariff2007")
)

discrete_x <- c(
  "differentiated", "agri", "perishable", "dfs", "day_w_arrival",
  "monitor", "psi", "rsa", "term"
)

run_outcome <- function(y, continuous_x) {
  xform <- reformulate(c(continuous_x, discrete_x))
  fit <- comp_did(
    yname = y,
    tname = "post_2008",
    dname = "tariff_change_2008",
    xformula = xform,
    data = app,
    nuisance_method = "dml",
    K = 5,
    stationary = TRUE,
    stationarity_test = TRUE,
    boot = FALSE,
    inffunc = TRUE,
    seed = 1234
  )
  boot <- drdid_cluster_bootstrap(
    nonstationary_fit = fit$att,
    stationary_fit = fit$stationary,
    group = app$hc_4digits,
    nboot = 9999,
    seed = 1234,
    keep_boots = FALSE
  )
  list(fit = fit, cluster = boot)
}

fits <- Map(run_outcome, names(outcome_specs), outcome_specs)

Local-Polynomial Nuisances

Local-polynomial nuisances can be useful in low-dimensional problems where the mixed continuous/discrete kernel structure is substantively important. They are also more expensive and more sensitive to bandwidth choices. If you use them, declare continuous and unordered covariates explicitly.

fit_bp_lp <- comp_did(
  yname = "bp",
  tname = "post_2008",
  dname = "tariff_change_2008",
  data = app,
  continuous = c("lvalue_tonnage", "tariff2007"),
  unordered = discrete_x,
  nuisance_method = "loo",
  stationary = TRUE,
  stationarity_test = TRUE,
  boot = FALSE,
  inffunc = TRUE,
  list_control_ps = list(
    n_start = 3,
    ps_min = 1e-2,
    local_mode = "fast",
    normalize_ps = TRUE
  ),
  list_control_or = list(
    n_start = 2,
    bw_constrained = TRUE
  )
)

The default recommendation is to keep normalize_ps = TRUE. Setting it to FALSE is an advanced replication or sensitivity exercise, not a best-practice default for new work.

Reporting

For a new application, report:

  • the estimand, especially whether the non-stationary compositional-change robust ATT is the primary estimand;
  • the nuisance method and learners or bandwidth controls;
  • the cluster variable and number of bootstrap draws;
  • seeds, package version, and any saved nuisance fitted values used for replication.

Do not expect this best-practice workflow to reproduce Table 4 numerically. That is intentional: the package’s current defaults target reliable use by new researchers. Numerical reproduction of the paper-final application is documented separately.