You are here

umxCP with two latent variables that loads in only part of the indicators

5 posts / 0 new
Last post
lf-araujo's picture
Joined: 11/25/2020 - 13:24
umxCP with two latent variables that loads in only part of the indicators
Image icon Fig 1100.7 KB
Image icon Fig 266.73 KB

Hi Tim and Mike,

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",
m2 <- umxCP(data = dt,
              mzData = "1: monozygotic",
              dzData = "2: dizygotic",
              selDVs = selDVs,
              sep = "",
              nFac = 2)
m3 <- umxModify(m2, update = vupdate, comparison = F)
AdminNeale's picture
Joined: 03/01/2013 - 14:09
tidyverse is broken

Hi Luis

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.

DiagMatrix 'a_cp' 
     [,1]        [,2]        [,3]       
[1,] "a_cp_r1c1" NA          NA         
[2,] NA          "a_cp_r2c2" NA         
[3,] NA          NA          "a_cp_r3c3"
          [,1]      [,2]      [,3]
[1,] 0.3191584 0.0000000 0.0000000
[2,] 0.0000000 0.5930313 0.0000000
[3,] 0.0000000 0.0000000 0.6918959
      [,1]  [,2]  [,3]
     [,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:

FullMatrix 'cp_loadings' 
     [,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"
             [,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
     [,1] [,2] [,3]

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.

lf-araujo's picture
Joined: 11/25/2020 - 13:24
Thanks Mike,

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?


Leo's picture
Joined: 01/09/2020 - 14:36
This error occurs when

This error occurs when exactly? Because, at first, you're not touching OpenMx, just basically modifying a matrix with base r. I remember setting up an identical model with umxcp. However, I just let the factors correlate (correlated factors) instead of specifying Cholesky.

lf-araujo's picture
Joined: 11/25/2020 - 13:24
Thanks 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.