Quasi-experimental methods: DiD, IV, RDD, and weighting

rmorie’s quasi-experimental stack is fully native: no did, fixest, did2s, didimputation, rdrobust, AER, WeightIt, or CBPS at runtime. Each estimator is cross-validated against its reference package in tests/ and the canonical replication suite (tests/testthat/test-quasiex-replication.R) reproduces the published estimates on the bundled real datasets (LaLonde, Basque Country, Lee 2008 incumbency, CigarettesSW).

library(rmorie)

Difference-in-differences with automatic staggered handling

morie_did() inspects the treatment-timing structure: with a single adoption cohort it runs the native TWFE estimator; with staggered adoption it warns that a naive TWFE would be contaminated by forbidden comparisons (Goodman-Bacon 2021) and switches to the Callaway-Sant’Anna group-time estimator.

df <- expand.grid(id = 1:60, t = 1:8)
df$g <- ifelse(df$id <= 20, 4L, ifelse(df$id <= 40, 6L, NA))
df$y <- rnorm(nrow(df), sd = 0.5) +
  ifelse(!is.na(df$g) & df$t >= df$g, 2, 0)
fit <- suppressWarnings(morie_did(df, "y", "id", "t", "g",
                                  n_bootstrap = 49L))
fit
#> Difference-in-differences -- Callaway-Sant'Anna (2021) group-time ATT, overall (post cells) 
#>   ATT: 2.1464  (SE 0.0647)  95% CI [2.0195, 2.2732]  p = 3.51e-241
#>   Units: 60  Periods: 8

The modern estimators are available directly when you want a specific one: morie_did_sun_abraham() (interaction-weighted event study), morie_did_borusyak() (imputation), and morie_did_did2s() (Gardner two-stage).

es <- morie_did_sun_abraham(df, "y", "id", "t", "g",
                            leads = 2L, lags = 2L)
es
#>   rel_time  estimate std.error  conf.low conf.high n
#> 1       -2 0.3438788 0.1686772 0.0132775 0.6744801 2
#> 2        0 2.2578746 0.1372652 1.9888397 2.5269095 2
#> 3        1 2.1475388 0.1516355 1.8503386 2.4447390 2
#> 4        2 2.0387359 0.1584486 1.7281822 2.3492895 2

Instrumental variables with a weak-instrument gate

morie_iv_2sls() refuses to report a 2SLS point estimate when the first-stage F is below the Staiger-Stock threshold of 10 and returns the identification-robust Anderson-Rubin confidence set instead.

n <- 300
z <- rnorm(n); u <- rnorm(n)
d <- 1.2 * z + 0.5 * u + rnorm(n)
y <- 2 * d + u + rnorm(n)
morie_iv_2sls(data.frame(y, d, z), "y", "d", "z")
#> Two-stage least squares -- 2SLS (native k-class, HC1) with Staiger-Stock gate 
#>   Estimate: 1.9209  (SE 0.0692)  95% CI [1.7846, 2.0571]
#>   First-stage F = 312.93 (Stock-Yogo 10% crit = 16.38)

Regression discontinuity with bundled diagnostics

morie_rdd() returns the bias-corrected estimate together with the McCrary manipulation check and placebo-cutoff estimates – the robustness steps applied papers routinely forget.

x <- runif(600, -1, 1)
y <- 1 + 2 * (x >= 0) + x + rnorm(600, sd = 0.5)
morie_rdd(data.frame(y, x), "y", "x")
#> Regression discontinuity (sharp), bandwidth = 1.2320
#>   Estimate: 1.9330  (SE 0.1417)  95% CI [1.6552, 2.2108]  p = 2.41e-42
#>   Manipulation check (McCrary): stat = -0.40, p = 0.693

Propensity-score weighting

The morie_weight_* family produces a morie_weight object the rest of the package composes with: logistic scores, entropy balancing, CBPS, overlap weights, stabilization, trimming, a native SuperLearner stack, and balance diagnostics.

dw <- data.frame(t = rbinom(300, 1, 0.4),
                 x1 = rnorm(300), x2 = rnorm(300))
w <- morie_weight_ps(dw, "t", c("x1", "x2"), estimand = "ATT")
w
#> morie_weight: logistic propensity (estimand ATT)
#>   n = 300  ESS = 280.6  weight range [0.530, 1.000]
head(morie_weight_diagnostic(w, dw, "t", c("x1", "x2")))
#>   covariate mean_treated mean_control           smd      abs_smd variance_ratio
#> 1        x1  -0.06944165  -0.06866672 -0.0008164644 0.0008164644      1.0499323
#> 2        x2  -0.13495195  -0.13655584  0.0015225926 0.0015225926      0.8981806
#>      ks_stat ks_p_value
#> 1 0.08054711  0.7526660
#> 2 0.07408815  0.8357571

One pipeline end to end

The composition test (tests/testthat/test-composition-pipeline.R) chains DAG construction, backdoor identification, matching, weighting, estimation, refutation, and the MRM publication table on the bundled LaLonde data – one class system across every module.