Overview
This vignette walks through the causal-inference surface of MORIE:
ATE, ATT, ATC, augmented IPW (doubly robust), per-unit CATE, group GATE,
and sensitivity analysis (the E-value). Each estimator follows the same
calling convention — data, treatment,
outcome, covariates — so swapping among them
is a matter of changing one function name.
A reproducible synthetic example
library(rmorie)
set.seed(2026)
n <- 800
X1 <- rnorm(n)
X2 <- rnorm(n)
# Confounded treatment assignment
ps <- plogis(0.5 * X1 - 0.3 * X2)
treat <- as.integer(ps > runif(n))
# True ATE = +1.0
y <- 1.0 * treat + 0.7 * X1 - 0.2 * X2 + rnorm(n, sd = 0.5)
df <- data.frame(y = y, treat = treat, X1 = X1, X2 = X2)Single-robust ATE / ATT / ATC
ate <- morie_estimate_ate(df, treatment = "treat", outcome = "y", covariates = c("X1", "X2"))
att <- morie_estimate_att(df, treatment = "treat", outcome = "y", covariates = c("X1", "X2"))
atc <- morie_estimate_atc(df, treatment = "treat", outcome = "y", covariates = c("X1", "X2"))
c(ATE = ate$ate, ATT = att$att, ATC = atc$atc)
#> ATE ATT ATC
#> 1.0083214 1.0190035 0.9966245
c(ATE = ate$se, ATT = att$se, ATC = atc$se)
#> ATE ATT ATC
#> 0.06993854 0.07282375 0.08286423Each result is a list. The point estimate is in a field named after
the estimand (ate, att, atc),
next to se, ci_lower and
ci_upper.
The three quantities estimate slightly different things:
- ATE (average treatment effect): averaged over the full population.
- ATT (treated): averaged over the treated subgroup.
- ATC (controls): averaged over the untreated subgroup.
Under no effect heterogeneity in the covariates, all three converge. Under heterogeneity, they diverge in informative ways.
Doubly robust: augmented IPW
morie_estimate_aipw() is the augmented
inverse-probability-weighting estimator. It is consistent if either the
propensity model or the outcome model is correctly specified — the
doubly-robust guarantee.
aipw <- morie_estimate_aipw(df, treatment = "treat", outcome = "y", covariates = c("X1", "X2"))
aipw$ate
#> [1] 1.010182
aipw$se
#> [1] 0.04052365
c(aipw$ci_lower, aipw$ci_upper)
#> [1] 0.930756 1.089609Per-unit CATE (T-learner / S-learner)
cate <- morie_estimate_cate(df, treatment = "treat", outcome = "y", covariates = c("X1", "X2"),
meta_learner = "t_learner")
head(cate)
#> [1] 1.0534077 0.9977113 1.0363683 1.0293321 0.9726929 0.9714638The result is a numeric vector with one conditional average treatment
effect per row of df. The true effect in this simulation is
constant (+1.0), so the estimates sit close to 1:
Group-level GATE
df$g <- sample(c("A", "B", "C"), nrow(df), replace = TRUE)
gate <- morie_estimate_gate(df, treatment = "treat", outcome = "y",
covariates = c("X1", "X2"),
group_col = "g")
gate
#> group ate se ci_lower ci_upper n
#> 1 B 1.0292613 0.06254227 0.9066784 1.151844 274
#> 2 A 1.0530078 0.07349140 0.9089647 1.197051 265
#> 3 C 0.9384145 0.06603321 0.8089894 1.067840 261The result is one row per group level with ate,
se, and CI.
Sensitivity analysis: the E-value
# Suppose the observed risk ratio is 2.0; how large would an
# unmeasured confounder need to be to explain it away?
evalue <- morie_e_value(rr = 2.0)
evalue
#> $e_value
#> [1] 3.414214
#>
#> $e_value_ci
#> [1] NAThe E-value is the minimum strength (on the risk-ratio scale) that an
unmeasured confounder would need on both treatment and outcome to fully
explain the observed effect. The e_value_ci element is
NA here because no confidence limit was supplied; pass
rr_lower (the lower 95% limit of the risk ratio) to get the
E-value of that limit.
Where to go next
-
morie_mrm_estimate_causal_effect()runs matching, IPW, AIPW and DML on one specification, corrects their p-values for multiple testing, andmorie_mrm_report()renders the table; see thenative-enginesvignette. - The
mrm-otis-walkthroughvignette runs the ten-estimator ensemble ofmorie_otis_analyze_a01_mrm()on OTIS-shaped data. - The
survey-weightedvignette covers sampling, design weights and design effects.
