Understanding runHelper() error: MxExpectationRAM: latent exogenous variables are not supported

Attachment | Size |
---|---|
ex9.23.dat | 761.72 KB |
I've been trying to modify [this example](https://github.com/OpenMx/OpenMx/blob/master/inst/models/nightly/mplus-ex9.23.R) from OpenMx' repos for testing. For this purpose, I am trying to fit the model using WLS. However, this returns the following error message:
Error in runHelper(model, frontendStart, intervals, silent, suppressWarnings, :
MxExpectationRAM: latent exogenous variables are not supported (x -> sw)
Even without any other changes to the code, I get this error. I've fitted models with WLS before without issue, but I fail to identify what may differs that would cause this error. Furthermore, I am trying to understand why I get this error using WLS, but not with ML.
Code sample:
# MPlus: Three-level growth model with a continuous outcome and one covariate on each of the three levels
# https://www.statmodel.com/usersguide/chapter9.shtml
library(OpenMx)
#mxOption(NULL, "Number of Threads", 8L)
options(width=120)
ex923 <- suppressWarnings(try(read.table("ex9.23.dat")))
# if (is(ex923, "try-error")) ex923 <- read.table("data/ex9.23.dat")
colnames(ex923) <- c(paste0('y',1:4), 'x', 'w', 'z', 'level2', 'level3')
ex923$level2 <- as.integer(ex923$level2)
ex923$level3 <- as.integer(ex923$level3)
level3Model <- mxModel(
'level3Model', type='RAM',
latentVars=c(paste0('y',1:4), 'ib3', 'sb3', 'z'),
mxData(ex923[!duplicated(ex923$level3),], 'raw', primaryKey='level3'),
mxPath('ib3', paste0('y',1:4), free=FALSE, values=1),
mxPath('sb3', paste0('y',1:4), free=FALSE, values=0:3),
mxPath(c('ib3','sb3'), arrows=2, connect="unique.pairs", values=c(1,0,1)),
mxPath('one', 'z', free=FALSE, labels="data.z"),
mxPath('z', c('ib3','sb3')),
mxPath('one', c('ib3','sb3')))
level2Model <- mxModel(
'level2Model', type='RAM', level3Model,
latentVars=c(paste0('y',1:4), 'ib2', 'sb2', 'w'),
mxData(ex923[!duplicated(ex923$level2),], 'raw', primaryKey='level2'),
mxPath('ib2', paste0('y',1:4), free=FALSE, values=1),
mxPath('sb2', paste0('y',1:4), free=FALSE, values=0:3),
mxPath(c('ib2','sb2'), arrows=2, connect="unique.pairs", values=c(1,0,1)),
mxPath('one', 'w', free=FALSE, labels="data.w"),
mxPath('w', c('ib2','sb2')),
mxPath(paste0('y',1:4), arrows=2),
mxPath(paste0('level3Model.y', 1:4), paste0('y',1:4), free=FALSE, values=1,
joinKey="level3"))
withinModel <- NULL
withinModel <- mxModel(
'withinModel',
type='RAM',
level2Model,
manifestVars=c(paste0('y',1:4)),
latentVars=c('iw','sw', 'x'),
mxData(ex923, 'raw'),
mxPath('iw', paste0('y',1:4), free=FALSE, values=1),
mxPath('sw', paste0('y',1:4), free=FALSE, values=0:3),
mxPath(paste0('y',1:4), arrows=2),
mxPath(c('iw','sw'), arrows=2, connect="unique.pairs", values=c(1,0,1)),
mxPath('one', 'x', free=FALSE, labels="data.x"),
mxPath('x', c('iw','sw')),
mxPath(paste0('level2Model.y', 1:4), paste0('y',1:4), free=FALSE, values=1,
joinKey="level2"),
mxFitFunctionWLS() # Added alternative fit function
)
withinModel <- mxRun(withinModel)
omxCheckEquals(withinModel$expectation$debug$rampartUsage, c(6000))
omxCheckCloseEnough(logLik(withinModel), -56044.82, 1e-2) # matches Mplus
No WLS with Multilevel
Although the error is complaining about "latent exogenous variables" the real problem is that OpenMx does not support WLS with multilevel models. WLS is based entirely on the summary statistics which do not exist with the multiple data sets being linked across levels in multilevel models. I'll create an issue on GitHub to catch this and improve the error message, but we have no plans to implement multilevel with WLS in OpenMx.
Log in or register to post comments
In reply to No WLS with Multilevel by mhunter
I'm confused
I have designed and fitted multilevel models in OpenMx in the past. If this is not feasible, I suppose I should not be doing this, but there's no error message of the sort mentioned above.
Log in or register to post comments
Why WLS?
I do apologize for OpenMx not catching this situation and giving a more reasonable error message. Future versions of OpenMx will.
Is there a particular reason you want to run this model or a similar model with WLS?
Log in or register to post comments
In reply to Why WLS? by mhunter
The reason I've been trying to to use WLS
dates back to previous posts. [ML does not handle ordinal variables in multilevel model](https://openmx.ssri.psu.edu/comment/9615#comment-9615), and adding a high number of ordinal variables also makes ML computationally unfeasible. However, it was suggested WLS might work (at least on two-level models), which is why I've been attempting WLS model estimation.
Now, it's the first time I've seen the error message I posted above. Actually, OpenMx does not really produce any error messages when fitting a multilevel model with WLS, which is problematic if this is not possible. Below I've modified the example above to one that will not produce the aforementioned error message. (For the sake of demonstration, I've added an ordinal variable on level 1 and latent interactions on all levels.) It does not produce any estimates or standard errors for upper levels, however, which sort of makes sense now. There really should be a warning/error message prohibiting fitting multilevel models using WLS. As for fitting multilevel models, there's a real need to implement ways to model ordinal/categorical/dichotomous variables in such models.
## ---------------------------------------------------------------------------------------------------------------------
Sys.setenv(OMP_NUM_THREADS=parallel::detectCores())
library(OpenMx)
library(dplyr)
library(magrittr)
#mxOption(NULL, "Number of Threads", 8L)
options(width=120)
## ---------------------------------------------------------------------------------------------------------------------
ex923 <- suppressWarnings(try(read.table("ex9.23.dat")))
colnames(ex923) <- c(paste0('y',1:4), 'x', 'w', 'z', 'level2', 'level3')
ex923 %<>%
mutate(across( x:z, ~ { round(. - min(.) + 1) } ) )
ex923$x %<>% as.ordered()
## ---------------------------------------------------------------------------------------------------------------------
# Level 3 model
level3Model <- mxModel(
# Model name
'level3Model',
# Model type
type = 'RAM',
# Variables. Manifest, latent and product variables, respectively.
manifestVars = 'z',
latentVars=c(paste0('y',1:4), 'ib3', 'sb3'),
productVars=c("ib3×sb3"),
# residual manifest variances
mxPath(
from = 'z',
arrows = 2,
free = FALSE,
values = 1
),
# latent variances & covariances
mxPath(
from = paste0('y',3:4),
connect = "unique.bivariate",
arrows = 2,
free = TRUE,
values = 0.1,
labels = "cov_y3_y4"
),
mxPath(
from = c('ib3','sb3'),
connect = "unique.pairs",
arrows = 2,
free = TRUE,
values = 0.4,
labels = c("e_ib3", "cov_ib3_sb3", "e_sb3")
),
# pattern coefficients
mxPath(
from = "ib3",
to = paste0('y',1:2),
arrows = 1,
free = c(FALSE, TRUE),
values = 1,
lbound = 1e-6,
labels = paste0('ib3_y',1:2)
),
mxPath(
from = "sb3",
to = paste0('y',3:4),
arrows = 1,
free = c(FALSE, TRUE),
values = 1,
lbound = 1e-6,
labels = paste0('sb3_y', 3:4)
),
mxPath(
from = c("ib3", "sb3"),
to = "z",
arrows = 1,
free = TRUE,
values = 1,
lbound = 1e-6,
labels = paste0(c("ib3", "sb3"), "_z")
),
# Latent interactions
mxPath(
from = c("ib3","sb3"),
to = "ib3×sb3",
arrows = 1,
free = FALSE,
values = 1
),
mxPath(
from = "ib3×sb3",
to = "z",
arrows = 1,
free = TRUE,
values = .5,
labels = "ib3×sb3_z"
),
# means
means = mxPath(
from = "one",
to = c(paste0('y',1:4), 'z', 'ib3', 'sb3'),
arrows = 1,
free = TRUE,
values = 0,
labels = paste0("μ_", c(paste0('y',1:4), 'z', 'ib3', 'sb3'))
),
# # thresholds
# mxThreshold(
# vars = "z",
# nThresh = max(as.double(ex923$z)) - 1,
# free = TRUE,
# values = mxNormalQuantiles(max(as.double(ex923$z)) - 1),
# labels = paste0("z_thresh_", 1:(max(as.double(ex923$z)) - 1)),
# ),
mxData(ex923[!duplicated(ex923$level3),], 'raw', primaryKey='level3')
)
# Level 2 model
level2Model <- mxModel(
# Model name
'level2Model',
# Model type
type = 'RAM',
# Higher level model to insert
level3Model,
# Variables. Manifest, latent and product variables, respectively.
manifestVars = 'w',
latentVars=c(paste0('y',1:4), 'ib2', 'sb2'),
productVars=c("ib2×sb2"),
# residual manifest variances
mxPath(
from = 'w',
arrows = 2,
free = FALSE,
values = 1
),
# latent variance & covariance
mxPath(
from = paste0('y',3:4),
connect = "unique.bivariate",
arrows = 2,
free = TRUE,
values = 0.1,
labels = "cov_y3_y4"
),
mxPath(
from = c('ib2','sb2'),
connect = "unique.pairs",
arrows = 2,
free = TRUE,
values = 0.4,
labels = c("e_ib2", "cov_ib2_sb2", "e_sb2")
),
# pattern coefficients
mxPath(
from = "ib2",
to = paste0('y',1:2),
arrows = 1,
free = c(FALSE, TRUE),
values = 1,
lbound = 1e-6,
labels = paste0('ib2_y',1:2)
),
mxPath(
from = "sb2",
to = paste0('y',3:4),
arrows = 1,
free = c(FALSE, TRUE),
values = 1,
lbound = 1e-6,
labels = paste0('sb2_y', 3:4)
),
mxPath(
from = c("ib2", "sb2"),
to = "w",
arrows = 1,
free = TRUE,
values = 1,
lbound = 1e-6,
labels = paste0(c("ib2", "sb2"), "_w")
),
# Latent interactions
mxPath(
from = c("ib2","sb2"),
to = "ib2×sb2",
arrows = 1,
free = FALSE,
values = 1
),
mxPath(
from = "ib2×sb2",
to = "w",
arrows = 1,
free = TRUE,
values = .5,
labels = "ib2×sb2_w"
),
# means
means = mxPath(
from = "one",
to = c(paste0('y',1:4), 'w', 'ib2', 'sb2'),
arrows = 1,
free = TRUE,
values = 0,
labels = paste0("μ_", c(paste0('y',1:4), 'w', 'ib2', 'sb2'))
),
# # thresholds
# mxThreshold(
# vars = "w",
# nThresh = max(as.double(ex923$w)) - 1,
# free = TRUE,
# values = mxNormalQuantiles(max(as.double(ex923$w)) - 1),
# labels = paste0("w_thresh_", 1:(max(as.double(ex923$w)) - 1)),
# ),
# Cross-level
mxPath(
from = paste0('level3Model.y', 1:4),
to = paste0('y',1:4),
free = FALSE,
values = 1,
joinKey = "level3"
),
# Data
mxData(ex923[!duplicated(ex923$level2),], 'raw', primaryKey='level2')
)
# Level 1 model. (Full model.)
withinModel <- mxModel(
# Model name
'withinModel',
# Model type
type='RAM',
# Higher level model to insert
level2Model,
# Variables. Manifest, latent and product variables, respectively.
manifestVars=c(paste0('y',1:4), 'x'),
latentVars=c('iw','sw'),
productVars=c("iw×sw"),
# residual variances
mxPath(
from = c(paste0('y',1:4), 'x'),
arrows = 2,
free = FALSE,
values = 1
),
# residual covariances
mxPath(
from = paste0('y',1:2),
connect = "unique.bivariate",
arrows = 2,
free = TRUE,
values = 0.1,
labels = "cov_y1_y2"
),
mxPath(
from = paste0('y',3:4),
connect = "unique.bivariate",
arrows = 2,
free = TRUE,
values = 0.1,
labels = "cov_y3_y4"
),
# latent variance & covariance
mxPath(
from = c('iw','sw'),
connect = "unique.pairs",
arrows = 2,
free = TRUE,
values = 0.4,
labels = c("e_iw", "cov_iw_sw", "e_sw")
),
# pattern coefficients
mxPath(
from = "iw",
to = paste0('y',1:2),
arrows = 1,
free = c(FALSE, TRUE),
values = 1,
lbound = 1e-6,
labels = paste0('iw_y',1:2)
),
mxPath(
from = "sw",
to = paste0('y',3:4),
arrows = 1,
free = c(FALSE, TRUE),
values = 1,
lbound = 1e-6,
labels = paste0('sw_y', 3:4)
),
mxPath(
from = c("iw", "sw"),
to = "x",
arrows = 1,
free = TRUE,
values = 1,
lbound = 1e-6,
labels = paste0(c("iw", "sw"), "_x")
),
# Latent interactions
mxPath(
from = c("iw","sw"),
to = "iw×sw",
arrows = 1,
free = FALSE,
values = 1
),
mxPath(
from = "iw×sw",
to = "x",
arrows = 1,
free = TRUE,
values = .5,
labels = "iw×sw_x"
),
# means
means = mxPath(
from = "one",
to = c(paste0('y',1:4), 'x', 'iw', 'sw'),
arrows = 1,
free = TRUE,
values = 0,
labels = paste0("μ_", c(paste0('y',1:4), 'x', 'iw', 'sw'))
),
# thresholds
mxThreshold(
vars = "x",
nThresh = max(as.double(ex923$x)) - 1,
free = TRUE,
values = mxNormalQuantiles(max(as.double(ex923$x)) - 1),
labels = paste0("x_thresh_", 1:(max(as.double(ex923$x)) - 1)),
),
# Cross-level
mxPath(
from = paste0('level2Model.y', 1:4),
to = paste0('y',1:4),
free = FALSE,
values = 1, joinKey = "level2"
),
# Data
mxData(ex923, 'raw'),
# Fit function: WLS
mxFitFunctionWLS(
# allContinuousMethod = 'marginals'
)
)
## ---------------------------------------------------------------------------------------------------------------------
withinModel %>%
mxRun() %>%
summary()
Log in or register to post comments
In reply to The reason I've been trying to to use WLS by pehkawn
Go Bayes or go home
OpenMx is not the right tool for the job, though. I am not aware of any *non-Bayesian* general method for fitting multilevel models to such variables. Look into stan.
Agreed.
Log in or register to post comments
Now throw error
OpenMx now throws an error message when someone tries to use multilevel with WLS. See: [41df8f2d](https://github.com/OpenMx/OpenMx/commit/41df8f2d84f1b862568f61dbba15668ad07e693e)
Log in or register to post comments
In reply to Now throw error by mhunter
next release
Or, at least it will in its next stable-version release.
Log in or register to post comments