Skip to contents

A general (non-OTIS) policy-evaluation entry point for double/debiased machine learning when observations are clustered – e.g. flights within an airspace corridor, students within a school, patients within a hospital. Standard DML standard errors assume independent observations and are anti-conservative under within-cluster correlation of the treatment or the errors. This cross-fits the AIPW (doubly-robust) score and computes a cluster-robust variance from the per-cluster score sums (Liang & Zeger 1986, one-way; Cameron, Gelbach & Miller 2011, up to two-way).

Usage

morie_dml_clustered(
  data,
  treatment,
  outcome,
  covariates,
  cluster = NULL,
  n_folds = 5L,
  seed = 123L,
  eps = 0.02,
  ps = NULL
)

Arguments

data

A data frame.

treatment

Binary treatment column name (0/1 or two-valued).

outcome

Numeric outcome column name.

covariates

Character vector of confounder column names.

cluster

Cluster column name (one-way) or length-2 character vector (two-way). NULL gives the i.i.d. (non-clustered) SE.

n_folds

Cross-fitting folds (default 5).

seed

Integer seed (default 123).

eps

Propensity clip bound in [eps, 1-eps] (default 0.02).

ps

Optional length-nrow(data) vector of externally supplied propensity scores (e.g. from a mixed-effects / cluster-level model); when given it replaces the cross-fitted propensity.

Value

A list of class morie_dml_clustered with ate, se, ci95, z, pval, n, n_clusters, and se_kind.

Details

Nuisances are cross-fitted (Chernozhukov et al. 2018): a ridge-logistic propensity and per-arm ordinary-least-squares outcome regressions, so the AIPW point estimate is Neyman-orthogonal. Only the SE is cluster-aware; the point estimate is the usual AIPW ATE. Add own-implemented mixed-effects propensities via ps if a corridor random effect is needed.

References

Chernozhukov V, et al. (2018). Double/debiased machine learning. The Econometrics Journal 21(1), C1–C68. doi:10.1111/ectj.12097

Cameron AC, Gelbach JB, Miller DL (2011). Robust inference with multiway clustering. JBES 29(2), 238–249. doi:10.1198/jbes.2010.07136

Examples

set.seed(1)
G <- 40L; ng <- 10L; n <- G * ng
g <- rep(seq_len(G), each = ng)
u <- stats::rnorm(G)[g]                       # cluster effect
x <- stats::rnorm(n)
d <- stats::rbinom(n, 1, stats::plogis(0.5 * x + u))
y <- 2 * d + x + u + stats::rnorm(n)          # true ATE = 2
df <- data.frame(y = y, d = d, x = x, corridor = g)
morie_dml_clustered(df, "d", "y", "x", cluster = "corridor")$ate
#> [1] 2.658798