# Can OpenMx fit Factor Mixture Models (FMM)?

3 posts / 0 new
Offline
Joined: 07/22/2018 - 15:34
Can OpenMx fit Factor Mixture Models (FMM)?

Dear Users and Maintainers,

Have you used OpenMx to fit Factor Mixture Models (FMM)? I saw that feature in Mplus but I couldn't figure out whether OpenMx can do it or not. Hence the question. Guidance on how to fit FMM using OpenMx would be much appreciated.

Offline
Joined: 05/24/2012 - 00:35
FMM

Yeah, I bet OpenMx can do it. Is there a specific model from the Mplus user guide that you'd like to see translated into OpenMx?

Offline
Joined: 07/31/2009 - 15:26
Example GMM

Here's an example Growth Mixture Model. A growth mixture model is a factor mixture model with fixed factor loadings. It's probably easy enough to modify this script to freely estimate the loadings.

require(OpenMx)
demo(package='OpenMx', 'GrowthMixtureModel_PathRaw')

Here' the code for inspection.

# -----------------------------------------------------------------------------

require(OpenMx)
# -----------------------------------------------------------------------------

data(myGrowthMixtureData)
# Prepare Data
# -----------------------------------------------------------------------------

# residual variances
resVars      <- mxPath( from=c("x1","x2","x3","x4","x5"), arrows=2,
free=TRUE, values = c(1,1,1,1,1),
labels=c("residual","residual","residual","residual","residual") )
# latent variances and covariance
latVars      <- mxPath( from=c("intercept","slope"), arrows=2, connect="unique.pairs",
free=TRUE, values=c(1,.4,1), labels=c("vari1","cov1","vars1") )
intLoads     <- mxPath( from="intercept", to=c("x1","x2","x3","x4","x5"), arrows=1,
free=FALSE, values=c(1,1,1,1,1) )
sloLoads     <- mxPath( from="slope", to=c("x1","x2","x3","x4","x5"), arrows=1,
free=FALSE, values=c(0,1,2,3,4) )
# manifest means
manMeans     <- mxPath( from="one", to=c("x1","x2", "x3", "x4","x5"), arrows=1,
free=FALSE, values=c(0,0,0,0,0) )
# latent means
latMeans     <- mxPath( from="one", to=c("intercept","slope"), arrows=1,
free=TRUE,  values=c(0,-1), labels=c("meani1","means1") )
# enable the likelihood vector
funML        <- mxFitFunctionML(vector=TRUE)
class1       <- mxModel("Class1", type="RAM",
manifestVars=c("x1","x2","x3","x4","x5"),
latentVars=c("intercept","slope"),
funML)

# latent variances and covariance
latVars2     <- mxPath( from=c("intercept","slope"), arrows=2, connect="unique.pairs",
free=TRUE, values=c(1,.5,1), labels=c("vari2","cov2","vars2") )
# latent means
latMeans2    <- mxPath( from="one", to=c("intercept", "slope"), arrows=1,
free=TRUE, values=c(5,1), labels=c("meani2","means2") )
class2       <- mxModel(class1, name="Class2", latVars2, latMeans2)

# Create an MxModel object
# -----------------------------------------------------------------------------

# request that individual likelihoods are used
# required for correct parameterization of class probabilities
classP       <- mxMatrix( type="Full", nrow=2, ncol=1,
free=c(TRUE, FALSE), values=1, lbound=0.001,
labels = c("p1","p2"), name="Props" )
mixExp       <- mxExpectationMixture(components=c('Class1', 'Class2'), weights='Props', scale='sum')
fit          <- mxFitFunctionML()
dataRaw      <- mxData( observed=myGrowthMixtureData, type="raw" )

gmm          <- mxModel("Growth Mixture Model",
dataRaw, class1, class2, classP, mixExp, fit)

gmmFit       <- mxRun(gmm, suppressWarnings=TRUE)

summary(gmmFit)

# Unscaled mixture proportions
mxEval(Props, gmmFit)

# Scaled mixture proportions
gmmFit$expectation$output$weights omxCheckCloseEnough(-2*logLik(gmmFit), 8739.05, 0.01) omxCheckCloseEnough(max(gmmFit$expectation$output$weights), 0.6009, 0.01)
omxCheckCloseEnough(min(gmmFit$expectation$output\$weights), 0.3991, 0.01)
# Check to see if results match within the specified bounds
# -----------------------------------------------------------------------------

Note that you could scale the mixture proportions differently if you want. See the scale argument of ?mxExpectationMixture for details. Many other software packages only allow "multinomial logisitc regression" or what we call "softmax" scaling.