You are here

Carrying out latent profile analysis (LPA) by adapting code examples for a two-factor model and growth mixture models

23 posts / 0 new
Last post
joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
Carrying out latent profile analysis (LPA) by adapting code examples for a two-factor model and growth mixture models
AttachmentSize
Binary Data mixture.R2.7 KB

Hi everyone. I'm new to both OpenMX and mixture models and am trying to do a latent profile analysis, or a mixture model (initially with two classes) of a Two-factor Model.

With the good documentation and code examples for Two-factor Models and mixture models (specifically the code for a growth mixture model), I tried to combine the two. However, when I run it, I get the following error that I'm having trouble debugging:

Error: The reference 'Class1.fitfunction' is unknown in the algebra named 'Latent Profile Model.mixtureObj'

Can you help point me in the right direction? A reproducible example is given in the attached code. Thank you in advance for considering this!

jpritikin's picture
Offline
Joined: 05/24/2012 - 00:35
source code control

I made a few corrections to the attached code to let it run. I'm not sure whether its exactly the model you want, but it no longer fails to run.

Do you use source code control? You probably could have figured this out yourself if you did. There are lots of online tutorials to help you get up to speed such as the one at github

File attachments: 
joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
thank you. I do use GitHub.

thank you. I do use GitHub. In the future, I'll share code via it.

just wondering, how would source code control (use of GitHub) have helped figure out the error?

thank you again,
Josh

jpritikin's picture
Offline
Joined: 05/24/2012 - 00:35
source code control

Well, I might be wrong about how you put that script together. What it looked like is that you copied some working code and modified it. If you indeed followed this method then you could have examined each change to determine whether the change caused the script to break. Going back to an earlier version of the script is facilitated by source code control. In any case, I'm glad it's working now.

joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
In comparing them (as part of

In comparing them (as part of making a commit on GitHub), I now see some obvious errors (and one that was not obvious to me) - I'm sorry about that. Thanks again for the (quick) help and sorry for the (easy-to-fix) problems in the code!

jpritikin's picture
Offline
Joined: 05/24/2012 - 00:35
no problem

Happy to help people learn GIT.

joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
estimated parameters for both classes?

Thank you again. I'm circling back to this and noticed that the estimates for the free parameters seem to only be for Class 1 - whereas the estimates for this growth mixture model example here are for both Class 1 and Class 2.

Could I ask if you could help me to modify this code to allow both classes to have free parameters estimated in this example - now here on GitHub?

Thanks so much.

jpritikin's picture
Offline
Joined: 05/24/2012 - 00:35
remove the labels

When 2 or more parameters have the same label, they are treated as a single parameter that is populated to 2 or more locations in the model. To free a parameter to have different estimates in Class1 and Class2, simply remove the label.

joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
Thank you - when I remove the

Thank you - when I remove the labels, so change the means part of the model specification from:

means <- mxPath( from="one", to=c("x1","x2","x3","y1","y2","y3","F1","F2"),
arrows=1,
free=c(T,T,T,T,T,T,F,F), values=c(1,1,1,1,1,1,0,0),
labels=c("meanx1","meanx2","meanx3",
"meany1","meany2","meany3",NA,NA) )
To:

means <- mxPath( from="one", to=c("ce","be","ae"),
arrows=1,
free=c(T,T,T), values=c(1,1,1)
)

I now see different estimates for Class1 and Class2 mean parameters, but they have the same values (though different standard errors).

I pushed the change to the example here.

Thank you again very much for helping me work through this.

jpritikin's picture
Offline
Joined: 05/24/2012 - 00:35
per model

Currently your mixture is per-model, not per row. What kind of mixture do you want to estimate?

joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
I think per row - something

I think per row - something akin to a Latent Profile Analysis or Latent Class Analysis, in which each observation is assigned a posterior probability to one of the classes (and means and residuals variances for each of the classes are estimated).
Thank you again

AdminHunter's picture
Offline
Joined: 03/01/2013 - 11:03
?emxMixtureModel

An easy way to do standard mixture models is to use EasyMx, an R package that is a wrapper around OpenMx. Run this:

install.packages("EasyMx")
require(EasyMx)
?EasyMx  # look at the help page for the package
?emxMixtureModel # look at the help page for the mixture model helper

The general idea with the mixture model helper is that you give it a list of MxModel objects and it creates the needed mixture pieces. As I understand it, a latent profile analysis is a mixture model in which each mixture component freely estimates all of its means, variances, and covariances. Here's the example latent profile model:

# Latent Profile Example
require(EasyMx)
 
m1 <- omxSaturatedModel(demoOneFactor)[[1]]
m1 <- mxRename(m1, 'profile1')
 
m2 <- omxSaturatedModel(demoOneFactor)[[1]]
m2 <- mxRename(m2, 'profile2')
 
mod <- emxMixtureModel(list(m1, m2), data=demoOneFactor, run=TRUE)
summary(mod)
coef(mod)
 
mxGetExpected(mod$profile1, 'covariance')
mxGetExpected(mod$profile1, 'means')
mxGetExpected(mod$profile2, 'covariance')
mxGetExpected(mod$profile2, 'means')
joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
Thank you, this worked great.

Thank you, this worked great. Will explore EasyMx more.

Could I ask for help for specifying a model for equal variance across mixture components?

Thank you and Joshua very much.

joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
If I can, please allow me to

If I can, please allow me to clarify.

Can I ask for help specifying models with:

  • Varying means, fixed residuals and no covariances (covariances within profiles are set to 0)
  • Varying means, fixed residual variances and covariances

My understanding is the model fit in the example above is:

  • Varying means, varying residual variances and covariances.
neale's picture
Offline
Joined: 07/31/2009 - 15:14
Covariances may be set to zero

In many applications a latent profile model makes the same assumptions as a latent class analysis, so it has the (questionable) assumption of conditional independence of the measures, given class membership. Thus only the means and variances, not the covariances, would be free. If I controlled the nomenclature, I'd term models with non-zero covariances as general mixture distributions, of which a factor mixture model is one, and only diagonal covariance structures as latent profile, consistent with latent class models. But Hunter is as usual correct, see, e.g., http://members.home.nl/jeroenvermunt/ermss2004f.pdf

joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
Thanks for the clarifying

Thanks for the clarifying info. (and the link).

If only the means and variances would be freed, how can: a) the model in which both are freed and b) the model in which only the means are freed be estimated? And am I understanding correctly that the model (using EasyMx) specified above would be the model corresponding to a) the model in which both means and variances are freely estimated?

jpritikin's picture
Offline
Joined: 05/24/2012 - 00:35
what is mxmatrix?

You should be able to answer that question by yourself by inspecting the model. Have you even glanced at the documentation?

joshuarosenberg's picture
Offline
Joined: 07/06/2017 - 19:46
I have, but I will review the

I have, but I will review the documentation more closely. I was particularly asking about how to specify these models in light of how omxSaturatedModel() or the EasyMx code is used.

AdminHunter's picture
Offline
Joined: 03/01/2013 - 11:03
See help pages

The function omxSaturatedModel is just a quick way to build a saturated model. It returns a list where the first element is the saturated model (free means, free variances, free covariances), and the second element is the independence model (i.e., has free means, free variances, and zero covariances). The input to emxMixtureModel is just a list of models and a data set. I used omxSaturatedModel just as a one-line way to build a model. You'll have to do some thinking about what model you want to specify for the components of the mixture, and build those models.

You can constrain parameters to be equal by given them the same "label".

KB's picture
KB
Offline
Joined: 12/19/2018 - 13:53
Getting Posterior Probabilities?

Hi, Thank you for this - it is very helpful! How would I go about getting the posterior probabilities (for each row) following this code?

Thank you so much.

AdminRobK's picture
Offline
Joined: 01/24/2014 - 12:15
posterior class-membership probabilities

It would be something like this:

#The two mixture proportions are prior class-membership probabilities 
#for drawing inferences about each participant:
pri1 <- gmmFit$expectation$output$weights[1]
pri2 <- gmmFit$expectation$output$weights[2]
#The fitfunctions of the two component models return a vector of participants' 
#raw (not log) likelihoods:
lik1 <- as.vector(gmmFit$Class1$fitfunction$result)
lik2 <- as.vector(gmmFit$Class2$fitfunction$result)
#Calculate participants' marginal likelihoods (probably not be necessary):
marglik <- (pri1*lik1) + (pri2*lik2)
#Calculate participants' posterior densities:
post1 <- (pri1*lik1)/marglik
post2 <- (pri2*lik2)/marglik
#Create a matrix of participants' posterior densities for the two components, 
#and then normalize them into probabilities:
posteriors <- cbind(post1,post2)/rowSums(cbind(post1,post2))
#^^^Since each row of the matrix needs to be normalized, you probably 
#don't need the marginal likelihoods.

Neale or Hunter can correct me if I've made a mistake anywhere.

I wonder if this is something that should have a dedicated helper function to do it?

KB's picture
KB
Offline
Joined: 12/19/2018 - 13:53
EasyMx - initial values?

Hi,

I am also new to OpenMx and like Joshua, I am trying to run a simple LPA with two normal component densities. I was wondering, is there a way to easily change the initial values in EasyMx? Or if I want that kind of flexibility do I need to go back to working directly with OpenMx?

Thanks!

AdminNeale's picture
Offline
Joined: 03/01/2013 - 14:09
Many umx and EasyMx functions create mxModels.

Once you have used a helper function to build a generic model, it is easy to modify it for your specific application. Umx has some parameter fixing/freeing functions. The base omxSetParameters() can also be used. Finally, model elements can usually be changed directly with, e.g.,

 
model$matrix$values <- c(1,5)

or

model$matrix <- mxMatrix(...)

to replace a matrix inside a model.