You are here

Can OpenMx fit Factor Mixture Models (FMM)?

3 posts / 0 new
Last post
ZZ's picture
ZZ
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.

jpritikin's picture
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?

mhunter's picture
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.

The latest version of the demo script is run-able with

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

Here' the code for inspection.

# -----------------------------------------------------------------------------
 
require(OpenMx)
# Load Libraries
# -----------------------------------------------------------------------------
 
 
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") )
# intercept loadings
intLoads     <- mxPath( from="intercept", to=c("x1","x2","x3","x4","x5"), arrows=1,
                        free=FALSE, values=c(1,1,1,1,1) )
# slope loadings
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"), 
                        resVars, latVars, intLoads, sloLoads, manMeans, latMeans,
                        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.