| Title: | Mediation Analysis with Multiple Imputation for Missing Data |
|---|---|
| Description: | Mediation analysis with incomplete data. Fits the mediator and outcome regressions on each dataset imputed with 'mice' (through 'medfit'), pools them with Rubin's rules, and infers the indirect effect with a Monte Carlo confidence interval ('RMediation') or a D4-stacked model-based constrained optimization (MBCO) likelihood-ratio test. Also provides an inverse probability weighting estimator, treatment-by-mediator interaction models, and a delta-adjustment sensitivity analysis for departures from missing at random. Structural equation models, including a latent mediator, are fit with 'lavaan'. |
| Authors: | Davood Tofighi [aut, cre, cph] (ORCID: <https://orcid.org/0000-0001-8523-7776>) |
| Maintainer: | Davood Tofighi <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.10.0 |
| Built: | 2026-10-14 22:33:55 UTC |
| Source: | https://github.com/Data-Wise/missingmed |
Computes inference for the indirect (mediated) effect from a fitted missingmed pipeline, dispatching to one of two engines:
infer(object, ...)infer(object, ...)
object |
An MDMediationFit (supports both |
... |
Method arguments: |
type = "mc" — Monte-Carlo / distribution-of-the-product confidence
interval via RMediation::ci_mediation_data() applied to the pooled
named medfit::MediationData. When the outcome model has a
treatment-by-mediator interaction (Y ~ X * M + ...), the indirect effect
is , which depends on the treatment level ;
set treatment_level to choose it. The interval then comes from
RMediation::ci() on the pooled estimates and pooled covariance of
.
With a non-identity link (family_m or family_y binomial, poisson, ...)
the interval is on that link scale, and a warning of class
missingmed_link_scale_warning says so once per session.
type = "mbco" — D4-stacked MBCO likelihood-ratio test of
, computed from the per-imputation datasets (MBCO does not
commute with Rubin's rules; see per_imputation_list()). The engine is
mbco_d4(); see there for ariv, the branch diagnostics and the cost.
Every imputation is refit with an unweighted stats::glm(), so weights,
offset, subset and na.action in fit_args are an error here; use
type = "mc" for such fits.
At least two imputations are required. It also works for
engine = "lavaan" fits (including a latent mediator), with lavaan doing
the refits: the tested paths are the structural regressions mediator ~ treatment and outcome ~ mediator. Only estimator = "ML" (the default)
is supported; MLR, MLM, WLSMV, group, ordered, sampling.weights
and method = "ipw" are refused, naming the option. A refit that did not
converge stops the test, naming the dataset, the branch and the model.
For "mc", the list returned by RMediation::ci_mediation_data()
(CI, Estimate, SE, MC.Error); for a model with an X:M term, the
same elements plus Estimand, the formula the interval is for.
For "mbco", an MbcoMIResult: the named numeric c(D4, p, r4, nu, d_S)
(index it with r["p"] or r[["p"]]) with the branch diagnostics as
properties.
run(), pool(), per_imputation_list(), mbco_d4()
set.seed(1) n <- 200 d <- data.frame(X = rnorm(n), C = rnorm(n)) d$M <- 0.4 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 30)] <- NA d$Y[sample(n, 30)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) fit <- run(set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" )) r <- infer(fit, type = "mbco", ariv = "fixed") r r[["p"]]set.seed(1) n <- 200 d <- data.frame(X = rnorm(n), C = rnorm(n)) d$M <- 0.4 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 30)] <- NA d$Y[sample(n, 30)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) fit <- run(set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" )) r <- infer(fit, type = "mbco", ariv = "fixed") r r[["p"]]
Tests with the model-based constrained optimization (MBCO)
likelihood-ratio statistic, pooled across multiply imputed datasets with the
D4 rule (Chan & Meng, 2022; Grund, Lüdtke & Robitzsch, 2023). This is the
engine behind infer(<MDMediationFit>, type = "mbco"), exported so that
other packages can call it on a plain list of completed datasets.
mbco_d4( implist, formula_y, formula_m, family_y = stats::gaussian(), family_m = stats::gaussian(), treatment, mediator, ariv = c("fixed", "own"), model = NULL, outcome = NULL, fit_args = list() )mbco_d4( implist, formula_y, formula_m, family_y = stats::gaussian(), family_m = stats::gaussian(), treatment, mediator, ariv = c("fixed", "own"), model = NULL, outcome = NULL, fit_args = list() )
implist |
A list of at least two completed data frames, e.g.
|
formula_y, formula_m
|
Outcome and mediator model formulas. The
response of |
family_y, family_m
|
|
treatment, mediator
|
Names of the treatment and mediator variables. |
ariv |
|
model, outcome, fit_args
|
For a lavaan SEM, in place of |
The MBCO constraint is a branch union, ,
and nulling a path drops every term that carries it (for example both M
and X:M from Y ~ X * M). The D4 statistic is
, referred to , where
is the statistic on the stacked data divided by and
is the relative increase in variance estimated from the
per-imputation statistics, and
(Chan and Meng 2022, eq. 2.15 of arXiv:1711.08822; the same reference distribution as
mitml::testModels(method = "D4", ariv = "positive"), since is
floored at 0). Chan and Meng derive this df for the own-branch statistic at
a regular null. For the fixed-branch default, and at the nonregular null
, it is a working reference distribution whose calibration
rests on simulation (see vignette("mbco-mi"), Operating characteristics).
ariv chooses how those statistics are formed:
"fixed" (default, calibrated): each imputation's statistic is computed on the branch
(a = 0 or b = 0) that the stacked constrained fit selected.
Imputations that disagree on the winning branch then cannot pull
down, and every imputation uses the stacked fit's k. An error
is raised if that branch's constraint removes a different number of
parameters in some imputation than in the stacked data (for example, a
level of a factor that interacts with the treatment or mediator is absent
from one imputation). k is a difference of design-matrix ranks, so a
sparse level of a main-effect factor does not trigger it.
"own": each imputation's statistic is computed on its own winning branch
(the standard Chan & Meng ). On full-rank designs its D4 and
r4 equal missingmed 0.4.0's; nu and p differ slightly, because the
denominator degrees of freedom now follow Chan & Meng (see NEWS). k is
a rank difference rather than a column count, so a design with aliased
columns gets a smaller k. It errors when the winning branches remove
different numbers of parameters, since there is then no single k.
It can be liberal. In simulation (1000 replications per cell, 20
imputations, MAR missingness in the mediator, 5% level) it rejected a true
null up to 8.3% of the time for a plain Gaussian model with 40% missing
and n = 200, and up to 13.8% for binary and count outcomes at 40%
missing, where "fixed" stayed at or below 5.7% on the same data. Use
"fixed" unless you need to reproduce an earlier analysis.
Cost. Every imputation is fit three times (the full model and both
single-path nulls), plus the same three fits on the stacked data. The
ariv = "fixed" statistic alone needs only the null on the stacked branch;
the second null per imputation is what the branch diagnostics
branch_mix and p_branch_a require.
Convergence. A stats::glm() refit that did not converge, or whose
log-likelihood is not finite, stops the test with an error naming the
dataset (an imputation or the stacked data), the branch (full, a = 0 or
b = 0) and the model: its likelihood is not a maximum, so the statistic
would not be a likelihood ratio. A fit that converged but carries glm's
"fitted probabilities numerically 0 or 1" warning is not refused (its
likelihood is finite and the warning reaches you unchanged), but the
chi-square-type reference may be poor for such near-separated data.
At least two imputations are required. For a single complete dataset, use a
complete-data MBCO test such as RMediation::mbco().
An MbcoMIResult: the named numeric c(D4, p, r4, nu, d_S) with
the branch diagnostics as properties.
Chan, K. W., & Meng, X.-L. (2022). Multiple improvements of multiple imputation likelihood ratio tests. Statistica Sinica.
Grund, S., Lüdtke, O., & Robitzsch, A. (2023). Pooling methods for likelihood ratio tests in multiply imputed data sets. Psychological Methods, 28(5), 1207–1221. doi:10.1037/met0000556
set.seed(1) implist <- lapply(1:3, function(i) { n <- 200 X <- rnorm(n) M <- 0.4 * X + rnorm(n) data.frame(X = X, M = M, Y = 0.3 * M + rnorm(n)) }) mbco_d4(implist, Y ~ X + M, M ~ X, treatment = "X", mediator = "M", ariv = "fixed" ) # The same test for a lavaan SEM, from a model string mbco_d4(implist, model = "M ~ X\nY ~ M + X", treatment = "X", mediator = "M", outcome = "Y" )set.seed(1) implist <- lapply(1:3, function(i) { n <- 200 X <- rnorm(n) M <- 0.4 * X + rnorm(n) data.frame(X = X, M = M, Y = 0.3 * M + rnorm(n)) }) mbco_d4(implist, Y ~ X + M, M ~ X, treatment = "X", mediator = "M", ariv = "fixed" ) # The same test for a lavaan SEM, from a model string mbco_d4(implist, model = "M ~ X\nY ~ M + X", treatment = "X", mediator = "M", outcome = "Y" )
The value of infer(<MDMediationFit>, type = "mbco") and mbco_d4(), for the
glm and the lavaan (maximum likelihood) engines alike. It is an S7 object
whose parent is class_double: its data is the named numeric
vector c(D4, p, r4, nu, d_S), and the properties carry how it was computed
and the branch diagnostics.
MbcoMIResult( .data = numeric(0), ariv = character(0), k = integer(0), m = integer(0), stacked_branch = character(0), branch_mix = logical(0), p_branch_a = integer(0), scaling = NA_real_ )MbcoMIResult( .data = numeric(0), ariv = character(0), k = integer(0), m = integer(0), stacked_branch = character(0), branch_mix = logical(0), p_branch_a = integer(0), scaling = NA_real_ )
.data |
Named numeric |
ariv |
|
k |
Number of parameters the constraint removes (numerator df). |
m |
Number of imputations pooled. |
stacked_branch |
|
branch_mix |
Logical; |
p_branch_a |
Share of imputations whose own winning branch is |
scaling |
|
D4: the pooled statistic, referred to .
p: its p-value.
r4: the relative increase in variance, clamped at 0.
nu: the denominator degrees of freedom (Inf when r4 is 0).
d_S: the MBCO statistic on the stacked data divided by the number of
imputations.
x["p"], x[["p"]] and is.numeric(x) work as they did on the plain
vector returned by missingmed 0.4.0 and earlier, and S7::S7_data(x)
returns that vector. Two things differ: identical(x, old_vector) is
FALSE, and x$p is an error.
An MbcoMIResult S7 object.
r <- MbcoMIResult(c(D4 = 4.7, p = 0.048, r4 = 1.16, nu = 13.8, d_S = 10.2), ariv = "fixed", k = 1, m = 5, stacked_branch = "b", branch_mix = TRUE, p_branch_a = 0.4 ) r[["p"]] r@stacked_branchr <- MbcoMIResult(c(D4 = 4.7, p = 0.048, r4 = 1.16, nu = 13.8, d_S = 10.2), ariv = "fixed", k = 1, m = 5, stacked_branch = "b", branch_mix = TRUE, p_branch_a = 0.4 ) r[["p"]] r@stacked_branch
An S7 class holding multiply imputed data together with a medfit-style
mediation specification (outcome/mediator formulas + roles). It is the entry
point of the missingmed S7 pipeline
(set_md_mediation() -> run() -> pool() -> infer()) and the S7
successor of the removed S4 SemImputedData class.
MDMediationData( data = NULL, formula_y = NULL, formula_m = NULL, treatment = character(0), mediator = character(0), engine = "glm", family_y = NULL, family_m = NULL, method = "mi", mechanism = "mar", weight_formula = NULL, weight_stabilize = TRUE, weight_trim = 1, se_type = "sandwich", conf_int = FALSE, conf_level = 0.95, n_imputations = integer(0), model = character(0), outcome = character(0), fit_args = list(), original_data = data.frame() )MDMediationData( data = NULL, formula_y = NULL, formula_m = NULL, treatment = character(0), mediator = character(0), engine = "glm", family_y = NULL, family_m = NULL, method = "mi", mechanism = "mar", weight_formula = NULL, weight_stabilize = TRUE, weight_trim = 1, se_type = "sandwich", conf_int = FALSE, conf_level = 0.95, n_imputations = integer(0), model = character(0), outcome = character(0), fit_args = list(), original_data = data.frame() )
data |
For |
formula_y |
Outcome model formula (e.g. |
formula_m |
Mediator model formula (e.g. |
treatment |
Name of the treatment/exposure variable. |
mediator |
Name of the mediator variable. |
engine |
medfit fitting engine, e.g. |
family_y, family_m
|
|
method |
Estimator axis: |
mechanism |
Assumed missing-data mechanism: |
weight_formula |
(IPW) Missingness model specification: |
weight_stabilize |
(IPW) Logical; if |
weight_trim |
(IPW) Upper quantile at which to cap weights (e.g. |
se_type |
(IPW) Variance estimator passed to |
conf_int |
Logical; whether downstream output carries confidence
intervals. Defaults to |
conf_level |
Numeric in (0, 1); confidence level. Defaults to |
n_imputations |
Number of imputations (MI) or |
model |
For |
outcome |
For |
fit_args |
For |
original_data |
The original data (pre-imputation for MI; the supplied frame for IPW). |
Fitting is delegated to medfit::fit_mediation() (one call per imputation),
so the per-imputation fits carry named path coefficients (a, b,
c_prime) ready for RMediation inference. The estimator (method) and the
model are orthogonal axes: method selects the missing-data estimator
("mi"/"ipw"); the formulas/engine select the model.
An MDMediationData S7 object.
set_md_mediation(), medfit::fit_mediation()
An S7 class holding the result of fitting a mediation model across all
imputations. Its defining feature is per_imputation: a list of named
medfit::MediationData objects, one per imputation. This list is what the
MBCO-MI path consumes, because MBCO does not commute with Rubin's rules
(D4-stacked MBCO needs the per-imputation fits, not the pooled estimate).
MDMediationFit( per_imputation = list(), m = integer(0), engine = "glm", conf_int = FALSE, conf_level = 0.95, weights = NULL, source = NULL )MDMediationFit( per_imputation = list(), m = integer(0), engine = "glm", conf_int = FALSE, conf_level = 0.95, weights = NULL, source = NULL )
per_imputation |
A list of named medfit::MediationData objects (length |
m |
Integer number of imputations. |
engine |
medfit fitting engine used (e.g. |
conf_int |
Logical; whether output carries confidence intervals. |
conf_level |
Numeric in (0, 1); confidence level. |
weights |
(IPW) Full-length numeric IPW weight vector ( |
source |
The originating MDMediationData (retained so MBCO can refit constrained/unconstrained models against the imputed data). |
It is the S7 successor of the removed S4 SemResults class.
An MDMediationFit S7 object.
An S7 class holding the Rubin's-rules pooled mediation result. Its defining
feature is pooled: a single named medfit::MediationData built from the
pooled estimates and total variance-covariance, valid as input to
RMediation::ci_mediation_data() / RMediation::medci() (path coefficients
resolve by name). It is the S7 successor of the removed S4 PooledSEMResults class.
MDMediationResult( pooled = NULL, tidy_table = data.frame(), cov_total = NULL, cov_between = NULL, cov_within = NULL, m = integer(0), engine = "glm", conf_int = FALSE, conf_level = 0.95 )MDMediationResult( pooled = NULL, tidy_table = data.frame(), cov_total = NULL, cov_between = NULL, cov_within = NULL, m = integer(0), engine = "glm", conf_int = FALSE, conf_level = 0.95 )
pooled |
A named medfit::MediationData carrying the pooled estimates
and total vcov (path labels |
tidy_table |
A data frame of pooled estimates, one row per coefficient:
|
cov_total, cov_between, cov_within
|
The Rubin's-rules total, between-, and within-imputation covariance matrices. |
m |
Integer number of imputations pooled. |
engine |
medfit fitting engine used (e.g. |
conf_int |
Logical; whether the tidy table carries per-coefficient
|
conf_level |
Numeric in (0, 1); confidence level. |
An MDMediationResult S7 object.
An S7 class holding the result of sensitivity_mnar(): one inference result
per rung of a delta grid, plus the grid itself and the realized marginal
sensitivity parameters.
MDSensitivityResult( rungs = list(), failed = character(0), grid = data.frame(), msp = numeric(), target = character(0), type = "mc", level = 0.95, seed = integer(0), seed_source = "argument", method_target = NA_character_, mechanism_used = NA_character_, scale = NA_character_, source = NULL )MDSensitivityResult( rungs = list(), failed = character(0), grid = data.frame(), msp = numeric(), target = character(0), type = "mc", level = 0.95, seed = integer(0), seed_source = "argument", method_target = NA_character_, mechanism_used = NA_character_, scale = NA_character_, source = NULL )
rungs |
List of inference results, one per row of |
failed |
Character vector, one per rung: the error message of a failed
rung and |
grid |
Data frame of delta values; one column per target, one row per rung. |
msp |
Numeric vector of realized marginal sensitivity parameters, one per
rung, computed on the first target ( |
target |
Character; the shifted variable(s). |
type |
Inference type used for each rung ( |
level |
Numeric in (0, 1); the confidence level the rungs were built
at. Needed by |
seed |
Integer seed pinned across rungs. |
seed_source |
|
method_target |
The |
mechanism_used |
How the delta was applied, one entry per target:
|
scale |
The delta scale, one entry per target: |
source |
The originating MDMediationData. |
An MDSensitivityResult S7 object.
@grid holds conditional sensitivity parameters (CSPs) – the values the
user supplied. @msp holds the marginal sensitivity parameter actually
realized at each rung: the mean difference between imputed and observed values
of the target. They are not the same quantity and can differ substantially;
see vignette("technical") and sensitivity_mnar().
Returns the number of imputations m stored in a mice::mids object.
n_imp(x)n_imp(x)
x |
A mice::mids object. |
An integer: the number of imputations.
imp <- mice::mice(mice::nhanes, m = 3, printFlag = FALSE, seed = 1) n_imp(imp)imp <- mice::mice(mice::nhanes, m = 3, printFlag = FALSE, seed = 1) n_imp(imp)
Number of imputations
n_imputations(object, ...)n_imputations(object, ...)
object |
An MDMediationFit or MDMediationResult object. |
... |
Unused. |
Integer count of imputations.
set.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" ) fit <- run(md) n_imputations(fit) n_imputations(pool(fit))set.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" ) fit <- run(md) n_imputations(fit) n_imputations(pool(fit))
Returns the list of per-imputation named medfit::MediationData objects
held in an MDMediationFit, together with the number of imputations m.
per_imputation_list(object, ...)per_imputation_list(object, ...)
object |
An MDMediationFit object. |
... |
Unused. |
This accessor exists because MBCO does not commute with Rubin's rules:
D4-stacked MBCO needs the per-imputation fits, not the pooled estimate. The
list it returns is the shape consumed by infer()(type = "mbco") and by an
external RMediation::mbco() MI entry point (missingmed issue #2).
A list with components per_imputation (a length-m list of named
medfit::MediationData) and m (the number of imputations).
set.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" ) fit <- run(md) pl <- per_imputation_list(fit) pl$m # The a path in each imputation, before pooling vapply(pl$per_imputation, function(x) x@a_path, numeric(1))set.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" ) fit <- run(md) pl <- per_imputation_list(fit) pl$m # The a path in each imputation, before pooling vapply(pl$per_imputation, function(x) x@a_path, numeric(1))
Applies Rubin's (1987) rules to the list of per-imputation named
medfit::MediationData objects in an MDMediationFit, producing a single
pooled named medfit::MediationData (the pooled slot of the returned
MDMediationResult). Because the estimates and variance-covariance carry the
mediation path names (a, b, c_prime, ...), the pooled object is valid
input to RMediation::ci_mediation_data() / RMediation::medci().
For a model with a treatment-by-mediator interaction, use
infer()(type = "mc", treatment_level = ) instead: those RMediation
functions use only , the indirect effect at treatment level 0.
pool(object, ...)pool(object, ...)
object |
An MDMediationFit object. Anything else (a |
... |
Unused. |
Pooling math (migrated from the S4 pool_sem / pool_tidy / pool_cov):
It is the S7 successor of the S4 pool_sem() method.
The returned tidy table also carries a per-coefficient Wald test
(statistic, df, riv, fmi, p_value) and, when conf_int = TRUE was
set in set_md_mediation(), per-coefficient conf_low and conf_high at
conf_level on the same t reference; see MDMediationResult for the columns
and why they do not test or bound the indirect effect.
An MDMediationResult object.
Rubin, D. B. (1987). Multiple Imputation for Nonresponse in Surveys. Wiley.
Barnard, J., & Rubin, D. B. (1999). Small-sample degrees of freedom with multiple imputation. Biometrika, 86(4), 948–955.
set.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, conf_int = TRUE, treatment = "X", mediator = "M" ) res <- pool(run(md)) res # Per-coefficient table, with Rubin's df and conf_low/conf_high res@tidy_table[, c("term", "estimate", "std_error", "df", "conf_low", "conf_high")]set.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, conf_int = TRUE, treatment = "X", mediator = "M" ) res <- pool(run(md)) res # Per-coefficient table, with Rubin's df and conf_low/conf_high res@tidy_table[, c("term", "estimate", "std_error", "df", "conf_low", "conf_high")]
Runs the mediation specification held in an MDMediationData object on every
imputed dataset, delegating each fit to medfit::fit_mediation(). The result
is an MDMediationFit whose per_imputation slot is a list of named
medfit::MediationData objects (one per imputation) — the shape consumed by
both Rubin's-rules pooling (pool()) and D4-stacked MBCO (infer()).
run(object, ...)run(object, ...)
object |
An MDMediationData object. |
... |
|
An engine error is rethrown with the engine and the imputation it failed
on. Engine warnings (a glm that did not converge, fitted probabilities of
0 or 1) are collected and raised once, naming the imputations that produced
them.
It is the S7 successor of the S4 run_sem() method.
An MDMediationFit object.
set_md_mediation(), pool(), infer()
set.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" ) fit <- run(md) fitset.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" ) fit <- run(md) fit
Re-imputes the data across a grid of delta values and re-runs the mediation
pipeline at each rung, producing a sensitivity curve for the indirect
effect. It is not an estimator: MAR versus MNAR is not testable from observed
data, so nothing here is identified. A rung answers "if the unobserved values
of target sit delta units away from what MAR imputation implies, the
indirect effect is X".
sensitivity_mnar( object, delta, target = NULL, type = c("mc", "mbco"), seed = NULL, level = NULL, n.mc = 1e+05, ums = NULL, treatment_level = NULL, ariv = c("fixed", "own"), on_error = c("stop", "continue"), ... )sensitivity_mnar( object, delta, target = NULL, type = c("mc", "mbco"), seed = NULL, level = NULL, n.mc = 1e+05, ums = NULL, treatment_level = NULL, ariv = c("fixed", "own"), on_error = c("stop", "continue"), ... )
object |
An MDMediationData with |
delta |
Numeric vector (one rung per value, applied to |
target |
Name of the variable to shift. Defaults to the mediator. Must
be |
type |
Inference per rung: |
seed |
Integer seed pinned across rungs. Defaults to the seed stored in
the |
level, n.mc, treatment_level
|
Passed to |
ums |
Optional character vector for a covariate-varying delta, one
rung per string, passed verbatim to |
ariv |
For |
on_error |
|
... |
|
Delta-adjusted imputation in the pattern-mixture sense (van Buuren, FIMD §9.2; Leacy et al. 2017). For a continuous target the canonical procedure imputes under MAR and then adds the constant to the imputed values (Hayati Rezvan et al. 2018). How the delta enters depends on the target's imputation method:
"norm" with a ums string: delegated to mice's NARFCS method
mnar.norm (Tompsett et al. 2018; Moreno-Betancur, van Buuren & White
2020), delta in raw units. A numeric delta on a norm target uses the
post shift below – for a constant delta the two give identical draws.
"logreg" (a binary target): delegated to mnar.logreg, which offsets
the imputation model's linear predictor – delta on the log-odds scale.
anything else continuous (pmm, norm.nob, cart, ...), and norm with
a numeric delta: the drawn values are shifted through mice's post
argument, delta in raw units.
Each rung re-imputes from the mids object's stored settings –
never from its recorded call, which does not resolve outside the function
that built it.
delta is a conditional sensitivity parameter (CSP): a difference
conditional on all remaining variables and their missingness indicators.
The quantity an analyst can actually reason about – "non-respondents average
delta units higher" – is a marginal sensitivity parameter (MSP), and the
two are different numbers. Supplying an elicited MSP as if it were a CSP is
the standard failure mode of this method and can badly damage coverage
(Tompsett et al. 2018). This function therefore reports the realized MSP
at every rung; compare it against what you meant.
Only method = "mi". IPW has no imputations to shift.
A numeric 0/1 target whose imputations are themselves 0/1 (pmm, cart,
sample, ...) is refused unless it is imputed by logreg: an added delta
would turn its values into 1s and 2s. A normal-model (norm*) imputation
of a 0/1 variable is continuous and is allowed; its delta is on the raw
(probability) scale, so keep it small and check the realized msp.
Categorical targets: binary via logreg only. Multinomial and ordinal
targets (polyreg, polr, lda) are refused – mice has no NARFCS
method for them – and so is logreg.boot, which has no counterpart.
With pmm (mice's default), shifted values may fall outside the observed
range that pmm otherwise guarantees. A message is emitted once.
The curve assumes the supplied imputation model is compatible with the mediation model; missingmed cannot verify this.
set.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" ) # Shift the imputed mediator values by 0, 0.5 and 1 (on the mediator's # scale) and recompute the Monte Carlo interval at each rung. sens <- sensitivity_mnar(md, delta = c(0, 0.5, 1), n.mc = 1e3) sensset.seed(1) n <- 150 d <- data.frame(X = rbinom(n, 1, 0.5), C = rnorm(n)) d$M <- 0.5 * d$X + 0.3 * d$C + rnorm(n) d$Y <- 0.3 * d$M + 0.2 * d$X + 0.3 * d$C + rnorm(n) d$M[sample(n, 25)] <- NA imp <- mice::mice(d, m = 3, method = "norm", printFlag = FALSE, seed = 1) md <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M" ) # Shift the imputed mediator values by 0, 0.5 and 1 (on the mediator's # scale) and recompute the Monte Carlo interval at each rung. sens <- sensitivity_mnar(md, delta = c(0, 0.5, 1), n.mc = 1e3) sens
Constructs an MDMediationData object: the entry point of the missingmed S7
pipeline. It records a medfit-style mediation specification (outcome and
mediator formulas plus the treatment/mediator roles) together with the data.
Fitting is delegated to medfit::fit_mediation() downstream by run(). It is
the S7 successor of the S4 set_sem() constructor (removed in 0.7.0).
set_md_mediation( data, formula_y, formula_m, treatment, mediator, engine = "glm", family_y = stats::gaussian(), family_m = stats::gaussian(), method = c("mi", "ipw"), mechanism = c("mar", "mnar"), weight_formula = NULL, weight_stabilize = TRUE, weight_trim = 1, se_type = c("sandwich", "model"), conf_int = FALSE, conf_level = 0.95, model = NULL, outcome = NULL, fit_args = list() )set_md_mediation( data, formula_y, formula_m, treatment, mediator, engine = "glm", family_y = stats::gaussian(), family_m = stats::gaussian(), method = c("mi", "ipw"), mechanism = c("mar", "mnar"), weight_formula = NULL, weight_stabilize = TRUE, weight_trim = 1, se_type = c("sandwich", "model"), conf_int = FALSE, conf_level = 0.95, model = NULL, outcome = NULL, fit_args = list() )
data |
For |
formula_y |
Outcome model formula (e.g. |
formula_m |
Mediator model formula (e.g. |
treatment |
Name of the treatment/exposure variable. |
mediator |
Name of the mediator variable. |
engine |
Fitting engine: |
family_y, family_m
|
|
method |
Estimator axis: |
mechanism |
|
weight_formula |
(IPW) Missingness model: |
weight_stabilize |
(IPW) Use stabilized weights? Default |
weight_trim |
(IPW) Upper quantile to cap weights; |
se_type |
(IPW) |
conf_int |
Logical; if |
conf_level |
Numeric in (0, 1); confidence level for |
model |
( |
outcome |
Name of the outcome variable. Required for
|
fit_args |
Named list of extra arguments for the engine, stored on the
object and used by |
Two estimators share the interface (method):
"mi" — data is a mice::mids object; run() fits every imputation.
"ipw" — data is a raw data.frame; run() reweights the complete cases
by inverse missingness probability and fits once.
The model is validated before anything is fit. Formulas are first expanded
against the data, so Y ~ . is checked as the model that run() fits.
set_md_mediation() refuses:
a one-sided formula, or a treatment/mediator that is not a single
variable name;
a variable in either formula that is neither a column of the data nor defined in the formula's environment (the treatment and mediator must be columns);
a formula_m whose response is not the bare mediator column (a
transform such as log(M) needs its own column), or a formula_y whose
response involves the mediator;
a treatment that is not a main effect of formula_m and of
formula_y, or a mediator that is not a main effect of formula_y;
any other term involving the treatment or mediator. Both enter only as
main effects, plus, in formula_y only, one treatment-by-mediator
interaction (X:M, M:X or from X * M). Products such as X:C,
M:W or X:M:W, transforms such as I(X^2), poly(X, 2) or log(M),
and offsets involving either variable are refused. For moderated models,
mbco_d4() tests the indirect effect on the completed datasets;
an X:M term when family_y or family_m is not Gaussian with an
identity link;
a treatment column that is not numeric. Factor, character and logical treatments must be recoded to numeric (0/1 for a binary treatment);
a treatment or mediator that is not a syntactic R name (such as
"my M"), which medfit cannot fit. Covariates may have any name;
an engine other than "glm", or "regmedint" with medfit >= 0.4.0 and
method = "mi" (regmedint takes no case weights);
a weight_formula that is not NULL, a formula or a named list of
formulas, or that uses a variable found neither in the data nor in its
environment.
An MDMediationData object.
MDMediationData, run(), pool(), infer(), medfit::fit_mediation()
set.seed(1) d <- data.frame(X = rbinom(200, 1, .5), C = rnorm(200)) d$M <- .5 * d$X + .3 * d$C + rnorm(200) d$Y <- .2 * d$X + .4 * d$M + .3 * d$C + rnorm(200) d$M[sample(200, 30)] <- NA # MI imp <- mice::mice(d, m = 5, printFlag = FALSE, seed = 1) md_mi <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M") # IPW (raw data.frame) md_ipw <- set_md_mediation(d, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M", method = "ipw") # A structural equation model: lavaan syntax in `model`, and the `outcome` md_sem <- set_md_mediation(imp, model = "M ~ a * X + C\nY ~ b * M + cp * X + C", treatment = "X", mediator = "M", outcome = "Y", engine = "lavaan")set.seed(1) d <- data.frame(X = rbinom(200, 1, .5), C = rnorm(200)) d$M <- .5 * d$X + .3 * d$C + rnorm(200) d$Y <- .2 * d$X + .4 * d$M + .3 * d$C + rnorm(200) d$M[sample(200, 30)] <- NA # MI imp <- mice::mice(d, m = 5, printFlag = FALSE, seed = 1) md_mi <- set_md_mediation(imp, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M") # IPW (raw data.frame) md_ipw <- set_md_mediation(d, Y ~ X + M + C, M ~ X + C, treatment = "X", mediator = "M", method = "ipw") # A structural equation model: lavaan syntax in `model`, and the `outcome` md_sem <- set_md_mediation(imp, model = "M ~ a * X + C\nY ~ b * M + cp * X + C", treatment = "X", mediator = "M", outcome = "Y", engine = "lavaan")