umxCP with two latent variables that loads in only part of the indicators
This is the first time I am playing with real data, and since I understand lavaans syntax better than openMx's I decided to start with umx.
I am trying to specify a CP model with two latents, but my measured indicators represents parenting style and personality (big5), 10 variables in total. See fig 1. I think they are better conceptualized as two latents that can correlate, but only loads in part of the variables, the five personality traits and the five parenting styles.
Initially I thought that I could either use umx_make_Twins or remove the unwanted paths with umxModify( update = ). However, it seems that with umxModify I can only remove the paths, but I cant add the correlation between latents. I ended up with fig 2.
- What should be the best approach to allow the two latents in fig 2 to correlate?
Below is a minimal working example to facilitate discussion:
dt <- ZA6701_en_add_scales_wid1_v4.0.0 %>%
left_join(ZA6701_en_master_v4.0.0, by = "pid") %>%
transmute(zygosity = zyg0102,
parcontt1 = as.numeric(parcontt),
parcontt2 = as.numeric(parcontt),
parincot1 = as.numeric(parincot),
parincot2 = as.numeric(parincot),
parmonit1 = as.numeric(parmonit),
parmonit2 = as.numeric(parmonit),
parnegct1 = as.numeric(parnegct),
parnegct2 = as.numeric(parnegct),
parwarmt1 = as.numeric(parwarmt),
parwarmt2 = as.numeric(parwarmt),
peragre1 = as.numeric(peragre),
peragre2 = as.numeric(peragre),
percons1 = as.numeric(percons),
percons2 = as.numeric(percons),
perneur1 = as.numeric(perneur),
perneur2 = as.numeric(perneur),
perextr1 = as.numeric(perextr),
perextr2 = as.numeric(perextr),
peropen1 = as.numeric(peropen),
peropen2 = as.numeric(peropen))
selDVs <- c("parcontt","parincot", "parmonit", "parnegct", "parwarmt",
"peragre", "percons", "perneur", "perextr","peropen")
vupdate <- c("cp_loadings_r6c1", "cp_loadings_r7c1", "cp_loadings_r8c1",
"cp_loadings_r9c1", "cp_loadings_r10c1", "cp_loadings_r1c2",
"cp_loadings_r2c2", "cp_loadings_r3c2", "cp_loadings_r4c2",
"cp_loadings_r5c2")
m2 <- umxCP(data = dt,
mzData = "1: monozygotic",
dzData = "2: dizygotic",
selDVs = selDVs,
sep = "",
nFac = 2)
umx
m3 <- umxModify(m2, update = vupdate, comparison = F)
plot(m3)
parameters(m2)
Thanks,
Luis
tidyverse is broken
I can't test because tidyverse doesn't exist for my version of R (4.0.3 which has been out ~2 months). But I can tell you how to do it. umxCP sets up OpenMx models with matrices. Most of these are in the 'top' model within the model that umxCP returns.
For example, the a paths are estimated in matrix a_cp, which we can see an example of by running the example from the ?umxCP help documentation.
$a_cp
DiagMatrix 'a_cp'
$labels
[,1] [,2] [,3]
[1,] "a_cp_r1c1" NA NA
[2,] NA "a_cp_r2c2" NA
[3,] NA NA "a_cp_r3c3"
$values
[,1] [,2] [,3]
[1,] 0.3191584 0.0000000 0.0000000
[2,] 0.0000000 0.5930313 0.0000000
[3,] 0.0000000 0.0000000 0.6918959
$free
[,1] [,2] [,3]
[1,] TRUE FALSE FALSE
[2,] FALSE TRUE FALSE
[3,] FALSE FALSE TRUE
$lbound
[,1] [,2] [,3]
[1,] 0 NA NA
[2,] NA 0 NA
[3,] NA NA 0
If you wish to allow the factors to correlate, you could use the Cholesky approach, which simply means freeing up the elements in the lower triangle of the a_cp, c_cp and e_cp matrices. If you only have 2 factors, this is easy because there's only one element in the lower triangle, so you could do something like this as Step 1:
model$top$a_cp$free[2,1] <- TRUE
model$top$c_cp$free[2,1] <- TRUE
model$top$e_cp$free[2,1] <- TRUE
Step 2 is to change the factor loading matrix, which is (cryptically) in matrix cp_loadings:
$cp_loadings
FullMatrix 'cp_loadings'
$labels
[,1] [,2] [,3]
[1,] "cp_loadings_r1c1" "cp_loadings_r1c2" "cp_loadings_r1c3"
[2,] "cp_loadings_r2c1" "cp_loadings_r2c2" "cp_loadings_r2c3"
[3,] "cp_loadings_r3c1" "cp_loadings_r3c2" "cp_loadings_r3c3"
[4,] "cp_loadings_r4c1" "cp_loadings_r4c2" "cp_loadings_r4c3"
[5,] "cp_loadings_r5c1" "cp_loadings_r5c2" "cp_loadings_r5c3"
[6,] "cp_loadings_r6c1" "cp_loadings_r6c2" "cp_loadings_r6c3"
$values
[,1] [,2] [,3]
[1,] 0.234958126 0.7924620 3.0297019
[2,] 0.049709341 -0.2315207 1.5511085
[3,] -0.009447686 0.7429272 0.5365263
[4,] 3.435387889 2.2862029 1.6776101
[5,] 1.923986619 2.6755898 2.2131049
[6,] -0.701359035 -1.4013078 -1.6685230
$free
[,1] [,2] [,3]
[1,] TRUE TRUE TRUE
[2,] TRUE TRUE TRUE
[3,] TRUE TRUE TRUE
[4,] TRUE TRUE TRUE
[5,] TRUE TRUE TRUE
[6,] TRUE TRUE TRUE
You can fix values to zero with a umx function, or with base OpenMx it might be done with omxSetParameters():
omxSetParameters(model$top, labels=c("cp_loadings_r4c1","cp_loadings_r5c1","cp_loadings_r6c1"), values=0, free=FALSE)
There are various tricks for using wildcards for labels that umx has that could be handy way to find all the factor loadings in a column without having to list them all out.
Step 3 is to run the model again once it has been modified.
Log in or register to post comments
Thanks Mike,
Seems easy enough, however I am stuck in Step 1, as umx models seems not to allow direct modifications. So with:
m2$submodels$top$e_cp$free[2,1] <- TRUE
I get:
You cannot directly set the matrices of a model. To set objects in the model, use the mxModel() function.
I tried mxModel() with something like:
mxModel(m2$submodels$top, name = "a_cp", free = T)
But this is the wrong syntax.
- How do I do this using mxModel?
best
Log in or register to post comments
In reply to Thanks Mike, by lf-araujo
umx generates OpenMx models
Second, sometimes direct tweaking of matrices isn't allowed (and I'm not sure what conditions affect this). Possibly, omitting the $submodels bit would solve the issue.
You can always replace the entire matrix by extracting it to an object first, modify that object, then put the matrix back into the model again. Sometimes it may be necessary to use mxModel(), with syntax of the form:
revisedModel <- mxModel(oldMxModel, matrixToBeReplaced)
where matrixToBeReplaced has the same name as the one it is replacing.
HTH
Log in or register to post comments
This error occurs when
Log in or register to post comments
In reply to This error occurs when by Leo
Thanks Leo,
This happens if I just try to change the content of the R object within the model like:
m2 <- umxCP(data = dt,
mzData = "1: monozygotic",
dzData = "2: dizygotic",
selDVs = selDVs,
sep = "",
nFac = 2)
m2$submodels$top$matrices$e_cp$free[2,1] <- TRUE
The same error happens. I am not sure if umx models are in any way different from the openMx ones. But I checked the structure and the above line was supposed to work. You mentioned a model with correlated factors, and I found an undocumented option in umx "correlatedA = T" that does what I wanted in terms of correlation (I think); the rest of Mike's instructions gets me where I wanted with this model.
Many thanks.
Log in or register to post comments
umxCP, correlated causes
The install_github(tbates/umx") version now deprecates `correlatedA` in favour of `correlatedACE` which is more accurately named, and defaults to leaving the lower triangle fixed at zero, allowing you to free up the (few) paths you want.
To create a 2-factor CP model, just set `nFac=2`
To restrict what manifests each CP loads on, in the resultant model, delete the paths from CP1 to the set 2 manifests, and from CP2 to the set 1 manifests using labels.
Paths from cp1 are in column 1 of the `cp_loadings` matrix. and labeled as "cp_loadings_r1c1"
You can match the relevant pattern with regular expressions, or if you like more concrete or fool proof approach, make a list and drop that... e.g.:
```R
tmp = umxCP(your setup here..., correlatedACE=TRUE, auto=FALSE)
pathsToDrop = c( "cp_loadings_r5c1", "cp_loadings_r6c1", ..., "cp_loadings_r1c2", ...)
m2 = umxModify(tmp, pathsToDrop, name = "style_and_personality")
```
you would then need to free the covariance of the a paths if you think that makes sense.
Log in or register to post comments
In reply to umxCP, correlated causes by tbates
problems with correlatedACE
I created a 2-factor CP model with
correlatedA = TRUE
a couple of weeks ago and it went well.m1 = umxCP("2cp", selDVs = selDVs, sep = "_T", dzData = dzData, mzData = mzData,
nFac = 2, correlatedA = TRUE, tryHard = "yes")
Now, I reran the model and received the polite note of changing
correlatedA
tocorrelatedACE
, as you just mentioned in your post. Unfortunately, in my case, usingcorrelatedACE
does not result in a model with correlated A,C, and E paths. The corresponding paths (a_cp_r2c1/c_cp_r2c1/e_cp_r2c1) do neither show up in the list of parameters nor are they plotted in the model. Do you have any idea to solve this problem?Thank you in advance and best wishes,
Ida
Log in or register to post comments
drop matrices word?
I don't usually have trouble changing objects within OpenMx models. One difference I notice is that you use:
m2$submodels$top$matrices$e_cp$free[2,1] <- TRUE
whereas I would use something like
m2$top$e_cp$free[2,1] <- TRUE
that is, refer to a matrix with model$matrix without either $submodels or $matrices in the string. I'm not sure that this is your issue though. Another option is to replace the whole matrix
e_cp <- m2$submodels$top$matrices$e_cp
e_cp$free[2,1]<-TRUE
m2$top <-mxModel(m2$top,tempMat)
although clumsier, the pass through mxModel has validation checks that can prevent runtime errors. HTH!
Log in or register to post comments