You are here

LCS: Effect of a time-independent predictor on a covariance

10 posts / 0 new
Last post
Pablo F Cáncer's picture
Joined: 08/15/2021 - 05:55
LCS: Effect of a time-independent predictor on a covariance

I'm trying to model the effect of a time-independent predictor on the parameters of a dual latent change score model (in discrete time). My predictor is the cohort to which the subjects belong. The groups (cohorts) of individuals in my data have different values for the parameters (e.g., different self-feedbacks, mean and variance of the slope...). I managed to successfully account for cohort effects on means, variances, and regression parameters, just using mxPaths. However, the covariance between the initial state of the system and the slope also changes across cohorts, and that's where I'm stuck.

How can I account for cohort effects on a covariance parameter in SEM using a RAM type model? Is it possible to manipulate the S matrix to include this effect? I've tried it but with 15 repeated measures it gets a bit cumbersome to work with these matrices, and I'm not even sure if thats the right way to go.

Thank you in advance!


Ryne's picture
Joined: 07/31/2009 - 15:12
This sounds fairly doable. If

This sounds fairly doable. If I understand correctly, you have created a multiple group model, with each group representing one cohort. You've managed to free other parameters across groups, and I don't see how the covariance between the latent intercept and the latent slope are any different. Can you post code and we can see where you're stuck?


Pablo F Cáncer's picture
Joined: 08/15/2021 - 05:55
Thank you Ryne. Of course I

Thank you Ryne. Of course I can. I paste my code below. My goal is to propose different methods to account for cohort differences in accelerated longitudinal designs. One of these methods is a multigroup model, where each cohort is treated as a group and has its own set of parameters. This works, but now I'm looking for a different approach.

I want to include the cohort as a moderator effect, without dividing the sample into groups. I've done this with state-space matrices and it works very well. Also, it is easy to manipulate the matrices A, x0, and P0 with mxAlgebra to include cohort effects. The problem is that I'm not sure how to translate this model to SEM, with matrices A, S, F and M.

Here is my code for SEM. y00 is the initial latent state, and yA is the slope (or additive component). The true values of the parameters are the initial values given to the model. Also, I got rid of the latent change scores, so the self-feedback, beta, is actually interpreted as 1 + beta. I attach a path diagram of the model and the data I'm using.

This model works well except when the covariance between y00 and yA (i.e., "y0Acv") changes across cohorts. Then, the estimates are biased because I'm not accounting for such effect. I think it may be possible to take the S matrix out of the model, include the cohort effect and then put it in the model again, but I don't know how.

Tmax <- 15
Y_manif <- paste0("Y", 0:(Tmax-1))
y_lat <- paste0("y", 0:(Tmax-1))
D <- paste0("D", 1:(Tmax-1))
LCS <- mxModel("LCSxcoh", 
               mxData(observed = data_ald, type="raw"),
               latentVars=c("y00", "yA", y_lat, D, "e", "excoh", "coh"),
               # From latent to manifest
               mxPath(from=y_lat, to=Y_manif, arrows=1, free=FALSE, values=1),
               # Measurement error variance
               mxPath(from=Y_manif, arrows=2, free=TRUE, values=2, labels="mery"),
               # From initial condition to first measurement occasion
               mxPath(from="y00", to=y_lat[1], arrows=1, free=FALSE, values=1),
               # From additive component to latent
               mxPath(from="yA", to=y_lat[2:Tmax], arrows=1, 
                      free=FALSE, values=1),
               # Initial conditions' means (effect of the cohort only on yA)
               mxPath(from="one", to=c("y00", "yA", "coh"), arrows=1, free=c(T, T, F), 
                      values=c(10, 13.5, 1), labels=c("meany00", "meanyA", "data.cohort")),
               mxPath(from="coh", to="yA", arrows=1, free=TRUE, values=0, labels="lambda_yAmn"),
               # Initial conditions' (co)variances (effect of the cohort only on yAv)
               mxPath(from = "y00", arrows=2, free=T, values=25, labels="vary00"),
               mxPath(from = "e", arrows=2, free=F, values=1),
               mxPath(from = "e", to = "yA", arrows=1, free=TRUE, values = 2.25,
                      labels = "sd_yA"),
               mxPath(from = "e", to = "excoh", arrows=1, free=FALSE, values = 1,
                      labels = "data.cohort"),
               mxPath(from = "excoh", to = "yA", arrows=1, free=TRUE, 
                      values = -.181818, labels = "lambda_yAv"),
               mxPath(from = "yA", to = "y00", arrows = 2, free=TRUE, values=7.875,
                      labels = "y0Acv"),
               # Self-feedback (AR) effect
               mxPath(from=y_lat[1:(Tmax-1)], to=y_lat[2:Tmax], arrows=1, 
                      free=TRUE, values=.55, labels="beta"),
               mxPath(from = y_lat[1:(Tmax-1)], to = D, arrows = 1,
                      free = FALSE, values = 1, labels = "data.cohort"),
               mxPath(from = D, to = y_lat[2:(Tmax)], arrows = 1,
                      free = TRUE, values = .03636, labels = "lambda_beta")
fit <- mxRun(LCS)
File attachments: 
Ryne's picture
Joined: 07/31/2009 - 15:12
So this would be relatively

So this would be relatively easy with multiple groups, where you'd make a dataset for each cohort and use labels to constrain some parameters to equality across groups and allow others to be free. But you said you didn't want to do that, so let's come up with a solution that fits in one MxModel without using multigroup.

You're using data.cohort as your label one of your paths. You can use the exact same logic on the yA to y00 label: you just can't use data.cohort, else you'd accidentally constrain this latent covariance to be equal to that other parameter. Instead, make a new variable called cohort2, and assign it labels that are (a) perfectly correlated with data.cohort, but (b) have unique values. It should look like this:

data$cohort <- c("g1", "g2", "g1",...)
data$cohort2 <- c("c1", "c2", "c1",...)

Then change the label for that covariance from "y0Acv" to data.cohort2.

If you get really fancy, you could even make the labels in cohort and cohort2 represent the parameters in question.

Have fun!

Pablo F Cáncer's picture
Joined: 08/15/2021 - 05:55
I'm afraid I'm not following

I'm afraid I'm not following you. You are defining cohort and cohort2 as variables of character type, but cohort should be numeric, right? I created a variable cohort2 in my dataset, following your conditions (a) and (b):
data_ald$cohort2 <- paste0("c", 0:11)[data_ald$cohort+1]

and then I replaced the "y0Acv" path with this one:
mxPath(from = "yA", to = "y00", arrows = 2, free=F, values=1, labels = "data.cohort2")

but it just causes R to abort session, so I guess I'm missing something here. Anyway, if OpenMx takes the values ("c1", "c2", "c3"...) in cohort2 and uses them as labels, wouldn't that result in the estimation of a different covariance parameter for each cohort, like in the multigroup approach?

The idea I have in mind is to avoid estimating one parameter for each cohort. For example, the paths in my model are made in such a way that beta = beta00 + lambda_beta*cohort, where beta00 is the value of the self-feedback when cohort=0 (so only 2 parameters are estimated, regardless of the number of cohorts). I was thinking about creating some paths representing a similar equation for y0Acv (e.g., y0Acv = y0Acv_00 + lambda_y0Acv*cohort).

Ryne's picture
Joined: 07/31/2009 - 15:12
Sorry, what I gave you won't

Sorry, what I gave you won't work. I should have checked my work before I posted. My apologies.

I think I understand your problem better now. I'd probably do that with an mxAlgebra statement. I'll write pseudo code below and let you see if you can get it working:

(1) Create an mxMatrix with two free parameters y0Acv_00 and lambda_y0Act. Whatever values you want to start with, and let them be free. I'll make it a 1x2 matrix for now, and call that matrix "B":

mxMatrix(name="B", "Full", 1, 2, free=TRUE , values=0, labels=c("y0Acv_00", "lambda_y0Act"))

(2) Put the definition variable in a matrix, which I'll make 2x1 and create a constant 1 for reasons I'll reveal in a second. This is "D".

mxMatrix(name="D", "Full", 2, 1, free=FALSE, values=1, labels=c("unity", "data.cohort"))

(3) Create an mxAlgebra statement where B %*% D. This will yield a 1x1 result of y0Act_00*1 + lambdaWhatever * data.cohort. Give it a name.

mxAlgebra(name="regResult", B %*% D)

(4) Constrain the path in question to the result of that algebra:

mxPath(from="X", to="Y", label="regResult[1,1]")

I've attached a simple example that shows how this can work.

File attachments: 
Pablo F Cáncer's picture
Joined: 08/15/2021 - 05:55
Thank you very much Ryne,

Thank you very much Ryne, that is exactly what I was looking for! Actually, the matrices and algebra I was building in the SSM framework were very similar to these. I didn't know it was possible to build a matrix and use matrixname[row,col] as a label. That's very helpful.

Just a couple of minor corrections, in case anyone else reads this thread looking for similar solutions. For the mxAlgebra B%*%D to work properly, matrix B should be 1x2, and D should be 2x1. Also, I believe the path for "data.cohort" should be FALSE:
mxMatrix("Full", 1, 2, free=TRUE, values=0, labels=c("y0Acv_00", "lambda_y0Act"), name="B")
mxMatrix("Full", 2, 1, free=c(FALSE, FALSE), values=1, labels=c("unity", "data.cohort"), name="D")

With these corrections, it works great. Thank you for taking time to write an example for me!

AdminNeale's picture
Joined: 03/01/2013 - 14:09
Another way

Another way that doesn’t need the algebra trick at all (it even works with crappy must-pay-to-play software) is to make 2 pathways from the common source, X -> Y and X->D->Y where D is a dummy latent variable, and has your definition variable on one path, and a free parameter on the other (order doesn’t matter here as scalar multiplication is commutative).

Pablo F Cáncer's picture
Joined: 08/15/2021 - 05:55
Hi Neale. I used that method

Hi Neale. I used that method (based on Yu et al. (2014)) to account for changes in the (auto)regression coefficients of my model (see the .jpg attached in my earlier comment). But I didn't find a way to do the same for changes in a covariance/correlation parameter. For that, only the algebra has worked so far.

Do you think this can be done with paths? It would be easier for the readers to understand a model if it could be described completely in a path diagram.

AdminNeale's picture
Joined: 03/01/2013 - 14:09
Formula is ok

While you could use the dummy variable method twice to specify some function of a definition variable on each of the variables you want to correlate, squaring the parameter would force it to be positive, so I don’t recommend it. Formula wins IMO.