Multivariate Maximum Likelihood Regression With Full Information Missing Data Handling
Source:R/mlmr_mv.R
mlmr_mv.RdFits a multivariate multiple regression model by maximum
likelihood, with full information maximum likelihood handling of missing
values by default. Multiple outcomes are regressed on a shared
predictor set simultaneously, with the residual covariance among
outcomes estimated as part of the model. The formula interface
mirrors lm's multivariate syntax,
cbind(y1, y2, y3) ~ x1 + x2, and the returned object
supports the same family of S3 methods as a univariate
mlmr fit.
Usage
mlmr_mv(
formula,
data,
missing = c("fiml", "ml", "listwise", "pairwise", "available.cases"),
ci_method = c("profile", "wald", "boot"),
conf_level = 0.95,
B = 1000L,
boot_type = c("ordinary", "bollen.stine"),
boot_seed = NULL,
estimator = c("ML", "MLR", "MLM", "GLS"),
se = NULL,
fixed_x = FALSE,
auxiliary = NULL,
effect_sizes = TRUE,
enforce_es_bounds = FALSE,
...
)Arguments
- formula
A two-sided
formulawhose left-hand side iscbind(y1, y2, ...)for two or more numeric outcomes and whose right-hand side names the shared predictor set. Factor predictors, interactions, polynomial terms, and transformations are supported as inmlmr.- data
A
data.framecontaining the variables informula.- missing
Character; passed to lavaan. Defaults to
"fiml"(equivalently"ml"). Seemlmrfor the full list of accepted values.- ci_method
Character; confidence interval method for the regression coefficients.
"profile"(default),"wald", or"boot". The same trade-offs apply as inmlmr; with J outcomes and K predictors, profile likelihood requires \(O(JK)\) constrained refits."boot"resamples the rows ofdataBtimes (or draws Bollen-Stine model-based resamples), refits on each, drops resamples whose refit does not converge, and reports each coefficient's percentile interval, the empirical quantiles of its resampled estimates; as inmlmr, the percentile interval is the only bootstrap interval offered.- conf_level
Desired level of confidence. Defaults to
0.95.- B
Integer; number of bootstrap resamples when
ci_method = "boot". Defaults to1000.- boot_type
Character;
"ordinary"(default) or"bollen.stine".- boot_seed
Integer or
NULL. Defaults toNULL, which leaves the user's current RNG state intact; supply an integer for reproducible bootstraps. When set, the function saves and restores.Random.seedon exit.- estimator
Character; one of
"ML"(default),"MLR","MLM","GLS".- se
Character or
NULL; defaults toNULL, meaning "choose automatically fromestimator" (same logic asmlmr).- fixed_x
Logical; defaults to
FALSE(jointly model the predictor distribution, required for FIML to use rows with missing predictors).- auxiliary
Character vector of variable names in
datato include as auxiliary variables (saturated correlates; Graham, 2003), orNULL(default) for none. Each auxiliary is correlated with every outcome's residual, every predictor, and each other auxiliary, but is never entered as a predictor, so the per-outcome regression coefficients keep their meaning while the full information maximum likelihood draws on the auxiliaries' observed values (the inclusive analysis strategy; Collins, Schafer, & Kam, 2001). A name inauxiliarymust be numeric, present indata, absent fromformula, and requiresfixed_x = FALSE.- effect_sizes
Logical; whether to compute per-outcome standardized betas, semi-partial \(R^2\), and Cohen's \(f^2\). Defaults to
TRUE.- enforce_es_bounds
Logical; if
TRUE, negative per-outcome \(sr^2\) or \(f^2\) estimates (finite-sample artifacts) are clamped to zero. Defaults toFALSE.- ...
Additional arguments forwarded to
lavaan.
Value
An object of class "mlmr_mv", a list with
components similar to a univariate mlmr fit but
extended for multiple outcomes:
call,formula,terms,model,xlevelsAs in
mlmr.coefficientsA matrix with predictors (and an intercept row, when present) as rows and outcomes as columns, matching
coef.mlm.coef_tableA long
data.framewith one row per (outcome, term) combination; columns includeoutcome,term,estimate,se,z_value,p_value,ci_lower,ci_upper,std_estimate.vcovThe variance-covariance matrix of the regression coefficients across all outcomes, returned by
vcov(). Rows and columns follow the outcome-major order ofcoef_table(the column-major flattening ofcoefficients) and are named"outcome:term", for example"mpg:wt", the namingvcovuses for an"mlm"fit. The cross-outcome blocks carry the sampling covariance between coefficients of different outcomes, so joint Wald tests across outcomes compose withcoef().residual_covThe estimated residual covariance matrix among outcomes (J by J).
R2Named vector of model implied \(R^2\) per outcome.
adj_R2Named vector of adjusted \(R^2\) per outcome.
effect_sizesWhen
effect_sizes = TRUE, a longdata.framewith one row per (outcome, predictor) combination giving \(sr^2\) and Cohen's \(f^2\).fitted.values,residualsMatrices with rows = observations and columns = outcomes;
NAin rows where any predictor is missing.logLik,N,N_completeAs in
mlmr.lavaan_fitThe underlying lavaan fit.
Details
Why a separate function from mlmr. A univariate
mlmr fit handles the case of one outcome regressed on one
or more predictors. mlmr_mv extends to the case of two or
more outcomes regressed on the same predictor set, modeling the
residual covariance among outcomes explicitly. This is the
regression problem in which the FIML advantage over listwise
deletion is largest, because rows that are missing on one outcome
still contribute information about the other outcomes (via the
modeled residual covariance) and about the joint distribution of
the predictors. A user who fits separate univariate regressions
for each outcome under listwise deletion can discard a great deal
of information when the outcomes are correlated and missingness
patterns differ.
Same predictor set across outcomes. The formula
cbind(y1, y2) ~ x1 + x2 regresses both y1 and
y2 on x1 and x2. Per-outcome predictor sets
(sometimes called seemingly unrelated regression with
heterogeneous predictors) are not supported; users who need that
can fit separate mlmr models or call
sem directly with a custom model string.
Auxiliary variables. As in mlmr, variables
that are not part of the regression but are correlated with an
outcome or with the missingness can be supplied through
auxiliary and are entered as saturated correlates (Graham,
2003): each is correlated with every outcome's residual, every
predictor, and each other auxiliary, but never as a predictor, so
the per-outcome coefficients keep their meaning while the
likelihood draws on the auxiliaries' observed values (the inclusive
analysis strategy of Collins, Schafer, & Kam, 2001).
The bootstrap interval. ci_method = "boot"
resamples the rows of data with replacement B times
(1000 by default), refits the model on each resample, and reports
each coefficient's percentile interval. It is the interval to ask
for when the multivariate normality the likelihood assumes is
doubtful, since its coverage does not rest on that assumption. The
price is B refits of a model that already carries J
outcomes, so a bootstrap interval is a deliberate request rather
than a default. Bootstrap results vary from run to run; supply
boot_seed for reproducibility. The mechanics, including the
Bollen-Stine variant, are given in the ci_method argument
description and in mlmr.
Caveats. Same as mlmr: the function
assumes that, conditional on the predictors, the joint
distribution of the outcomes is multivariate normal with constant
covariance, and that missingness is at most MAR. Factor
predictors and interactions are expanded through
model.matrix once and reused for every
outcome.
Author
Ken Kelley kkelley@nd.edu
Examples
# Two outcomes on a shared predictor set. The residual covariance
# between the outcomes is estimated as part of the model, which is
# what separates this from two separate regressions. This fit asks
# for the Wald interval and leaves the effect sizes off so that it
# runs at example time. It is the only block here that runs; the
# rest is left as commented code so a reader can see the syntax
# without paying the run time.
fit <- mlmr_mv(cbind(t6_paragraph_comprehension, t9_word_meaning) ~
t5_general_information + t7_sentence,
data = holzinger_swineford,
ci_method = "wald", effect_sizes = FALSE)
coef(fit) # matrix: rows = predictors, cols = outcomes
#> t6_paragraph_comprehension t9_word_meaning
#> (Intercept) -0.25416035 -6.3377552
#> t5_general_information 0.07653333 0.2844663
#> t7_sentence 0.36460353 0.5811433
summary(fit)
#>
#> Call:
#> mlmr_mv(formula = cbind(t6_paragraph_comprehension, t9_word_meaning) ~
#> t5_general_information + t7_sentence, data = holzinger_swineford,
#> ci_method = "wald", effect_sizes = FALSE)
#>
#> Missing: fiml | Estimator: ML | SE: standard
#> Sample size used: 301 Complete cases: 301
#>
#> --- Outcome: t6_paragraph_comprehension ---
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) -0.25416 0.48954 -0.519 0.604
#> t5_general_information 0.07653 0.01521 5.031 4.87e-07 ***
#> t7_sentence 0.36460 0.03649 9.993 < 2e-16 ***
#> ---
#> Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#> R^2: 0.5734
#> Adj. R^2: 0.5706
#>
#> --- Outcome: t9_word_meaning ---
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) -6.33776 1.01314 -6.256 3.96e-10 ***
#> t5_general_information 0.28447 0.03148 9.036 < 2e-16 ***
#> t7_sentence 0.58114 0.07551 7.696 1.40e-14 ***
#> ---
#> Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#> R^2: 0.6211
#> Adj. R^2: 0.6186
#>
#> Residual covariance among outcomes:
#> t6_paragraph_comprehension t9_word_meaning
#> t6_paragraph_comprehension 5.1856 3.0942
#> t9_word_meaning 3.0942 22.2112
#>
#> Log likelihood: -3552 AIC: 7132 BIC: 7184
fit$R2 # per-outcome R^2
#> t6_paragraph_comprehension t9_word_meaning
#> 0.5734131 0.6211091
fit$residual_cov # residual covariance among outcomes
#> t6_paragraph_comprehension t9_word_meaning
#> t6_paragraph_comprehension 5.185583 3.094177
#> t9_word_meaning 3.094177 22.211178
# The interval menu is profile, Wald, and bootstrap. The default,
# ci_method = "profile", inverts the likelihood ratio test one
# coefficient at a time, and with two outcomes there are twice as
# many coefficients to profile; it is what a reported interval
# deserves. The bootstrap resamples rows and takes percentile
# limits; it is what to ask for when the multivariate normality the
# likelihood assumes is doubtful. Each refits the model many times,
# so neither is run here; the calls are
# mlmr_mv(cbind(t6_paragraph_comprehension, t9_word_meaning) ~
# t5_general_information + t7_sentence,
# data = holzinger_swineford)
# mlmr_mv(cbind(t6_paragraph_comprehension, t9_word_meaning) ~
# t5_general_information + t7_sentence,
# data = holzinger_swineford,
# ci_method = "boot", B = 1000, boot_seed = 113)
# with boot_seed supplied because bootstrap limits otherwise move
# from run to run.
# The per-outcome effect sizes come back on the fit rather than in
# summary(): one row per outcome and predictor, giving the
# semi-partial R^2 and Cohen's f^2. They cost one constrained refit
# per outcome and predictor, so they are left off above, which also
# leaves the standardized coefficients in coef_table missing. With
# the default effect_sizes = TRUE the table is
# fit_es <- mlmr_mv(cbind(t6_paragraph_comprehension,
# t9_word_meaning) ~
# t5_general_information + t7_sentence,
# data = holzinger_swineford,
# ci_method = "wald")
# print(fit_es$effect_sizes, row.names = FALSE)
# FIML versus listwise when one outcome has missing values. The
# revised second-form test t26_flags was administered to only 145
# of the 301 students, so it carries real missingness. A row with
# t26_flags missing still informs the likelihood about the other
# outcome, about the predictors, and, through the residual
# covariance, about t26_flags itself, so no row is discarded. Not
# run here because the comparison costs two more fits; the code is:
# fit_fiml <- mlmr_mv(cbind(t6_paragraph_comprehension,
# t26_flags) ~
# t7_sentence + t9_word_meaning,
# data = holzinger_swineford,
# ci_method = "wald", effect_sizes = FALSE)
# fit_lwd <- mlmr_mv(cbind(t6_paragraph_comprehension,
# t26_flags) ~
# t7_sentence + t9_word_meaning,
# data = holzinger_swineford,
# missing = "listwise", ci_method = "wald",
# effect_sizes = FALSE)
# c(N_fiml = nobs(fit_fiml), N_listwise = nobs(fit_lwd))
# cbind(FIML = coef(fit_fiml)[, "t6_paragraph_comprehension"],
# listwise = coef(fit_lwd)[, "t6_paragraph_comprehension"])
# Auxiliary variable (saturated correlates): the complete speed test
# t13_straight_and_curved_capitals informs the likelihood without
# entering either regression. Continuing from the model above, and
# again not run here:
# fit_aux <- mlmr_mv(cbind(t6_paragraph_comprehension,
# t26_flags) ~
# t7_sentence + t9_word_meaning,
# data = holzinger_swineford,
# ci_method = "wald",
# auxiliary = "t13_straight_and_curved_capitals",
# effect_sizes = FALSE)
# coef(fit_aux)