You are here

The goal of moderator analysis in metaSEM::wls()

14 posts / 0 new
Last post
sharmel's picture
Offline
Joined: 07/31/2023 - 13:19
The goal of moderator analysis in metaSEM::wls()

Dear Mike and Colleagues,

My understanding is that moderator analysis at the 2nd stage of metaSEM approach using metaSEM::wls() involves multi-group analysis.

Thus, as a user I have to subset my data by categories of my moderator, and fit the same model syntax to each subset as shown below.

But at the end, how can I conclude whether I have a significant moderator or not? For example, should/can I do: anova(wls1, wls2) and if one model is significantly different from another conclude that the moderator is significant? (I appreciate a demonstration.)

library(metaSEM)
 
## Sample sizes
n1 <- 100
n2 <- 200
 
## Variable labels
vars <- c("y", "x1", "x2")
 
## Group 1 data and model
R1 <- matrix(c(1.00, 0.22, 0.24, 
               0.22, 1.00, 0.30, 
               0.24, 0.30, 1.00), ncol=3, nrow=3,
             dimnames=list(vars, vars))
acov1 <- asyCov(R1, n1)
 
model1 <- "y ~ b1a*x1 + b2a*x2
           ## Variances of x1 and x2 are 1
           x1 ~~ 1*x1
           x2 ~~ 1*x2
           ## x1 and x2 are correlated
           x1 ~~ ra*x2"
 
RAM1 <- lavaan2RAM(model1, obs.variables=vars)
 
wls1 <- wls(model="Group1", Cov=R1, aCov=acov1, n=n1, RAM=RAM1)
 
## Group 2 data and model
R2 <- matrix(c(1.00, 0.33, 0.41, 
               0.33, 1.00, 0.35, 
               0.41, 0.35, 1.00), ncol=3, nrow=3,
             dimnames=list(vars, vars))
acov2 <- asyCov(R2, n2)
 
model2 <- "y ~ b1b*x1 + b2b*x2
           ## Variances of x1 and x2 are 1
           x1 ~~ 1*x1
           x2 ~~ 1*x2
           ## x1 and x2 are correlated
           x1 ~~ rb*x2"
 
RAM2 <- lavaan2RAM(model2, obs.variables=vars)
 
wls2 <- wls(model="Group2", Cov=R2, aCov=acov2, n=n2, RAM=RAM2)
Mike Cheung's picture
Offline
Joined: 10/08/2009 - 22:37
wls() was not decided for

wls() was not decided for moderator analysis. osmasem() is a better option.

sharmel's picture
Offline
Joined: 07/31/2023 - 13:19
Dear Mike,

Dear Mike,

Sure, but I asked this to clarify your comment HERE. In that comment, what is the purpose behind creating a combined model like: mxModel(model="combined", wls1, wls2, mxFitFunctionMultigroup(c("Group1", "Group2")))? What do the statistics of this combined model tell us about the moderatory role of "Group"?

In fact, as I ask in this thread, if the path models are exactly the same across two subsets of data, and we fit wls(...,run=F) for each model, then is it possible to compare the two models using mxModel(..., mxFitFunctionMultigroup(...)) and then mxRun()? I really appreciate a demonstration.

Mike Cheung's picture
Offline
Joined: 10/08/2009 - 22:37
The purpose is to conduct a

The purpose is to conduct a subgroup analysis in meta-analysis, which is equivalent to a multiple-group analysis in SEM.

The models with equality constraints and without equality constraints are nested. Thus, a chi-square difference statistic can be used to compare them.

There are plenty of resources for subgroup analysis in meta-analysis and multiple-group SEM. Here are some materials for a three-day workshop in MASEM. https://github.com/mikewlcheung/masemWorkshop2023

sharmel's picture
Offline
Joined: 07/31/2023 - 13:19
Dear Mike,

Dear Mike,

Is it possible to conduct pairwise comparisons between the elements of A.matrix and S.matrix from the two groups in the object output below?

## Sample sizes
n1 <- 100
n2 <- 200
 
## Variable labels
vars <- c("y", "x1", "x2")
 
## Group 1 data and model
R1 <- matrix(c(1.00, 0.22, 0.24, 
               0.22, 1.00, 0.30, 
               0.24, 0.30, 1.00), ncol=3, nrow=3,
             dimnames=list(vars, vars))
acov1 <- asyCov(R1, n1)
 
model1 <- "y ~ b1a*x1 + b2a*x2
           ## Variances of x1 and x2 are 1
           x1 ~~ 1*x1
           x2 ~~ 1*x2
           ## x1 and x2 are correlated
           x1 ~~ ra*x2"
 
## Group 2 data but model is the same as model1
R2 <- matrix(c(1.00, 0.33, 0.41, 
               0.33, 1.00, 0.35, 
               0.41, 0.35, 1.00), ncol=3, nrow=3,
             dimnames=list(vars, vars))
acov2 <- asyCov(R2, n2)
 
model2 <- "y ~ b1b*x1 + b2b*x2
           ## Variances of x1 and x2 are 1
           x1 ~~ 1*x1
           x2 ~~ 1*x2
           ## x1 and x2 are correlated
           x1 ~~ rb*x2"
 
RAM1 <- lavaan2RAM(model1, obs.variables=vars)
RAM2 <- lavaan2RAM(model2, obs.variables=vars)
 
wls1 <- wls(model="Group1", Cov=R1, aCov=acov1, n=n1, RAM=RAM1, run=F)
wls2 <- wls(model="Group2", Cov=R2, aCov=acov2, n=n2, RAM=RAM2, run=F)
 
## Combine both groups
wls.model <- mxModel(model="combined", wls1, wls2, mxFitFunctionMultigroup(c("Group1", "Group2")))
 
wls.fit <- mxRun(wls.model, intervals=TRUE)
 
zz = summary(wls.fit)
 
(output = cbind(zz$parameters[-c(3:4,7:10)], zz$CI[c("lbound","ubound")]))
 
  name         matrix  Estimate  Std.Error       lbound    ubound
1  b1a Group1.Amatrix 0.1626374 0.09928458 -0.034465926 0.3598924
2  b2a Group1.Amatrix 0.1912088 0.09881592 -0.004306087 0.3874504
3   ra      Group1.S1 0.3000000 0.09100000  0.120857208 0.4785835
4  b1b Group2.Amatrix 0.2125356 0.06601287  0.082020597 0.3433026
5  b2b Group2.Amatrix 0.3356125 0.06395664  0.209506797 0.4626121
6   rb      Group2.S1 0.3500000 0.06204862  0.228300669 0.4720198
Mike Cheung's picture
Offline
Joined: 10/08/2009 - 22:37
Yes, but it is not automatic.

Yes, but it is not automatic.
You may test the constraints one by one, e.g., b1a=b1b, b2a=b2b. Please note that it does not adjust the issue of multiple comparisons.

sharmel's picture
Offline
Joined: 07/31/2023 - 13:19
Thank you, Mike. Can you

Thank you, Mike. Can you possibly demonstrate testing only one such constraint to help figure out the process?

Mike Cheung's picture
Offline
Joined: 10/08/2009 - 22:37
Here is an example.

Here is an example.

model1 <- "y ~ b1*x1 + b2a*x2
           ## Variances of x1 and x2 are 1
           x1 ~~ 1*x1
           x2 ~~ 1*x2
           ## x1 and x2 are correlated
           x1 ~~ ra*x2"
 
model2 <- "y ~ b1*x1 + b2b*x2
           ## Variances of x1 and x2 are 1
           x1 ~~ 1*x1
           x2 ~~ 1*x2
           ## x1 and x2 are correlated
           x1 ~~ rb*x2"
 
RAM1 <- lavaan2RAM(model1, obs.variables=vars)
RAM2 <- lavaan2RAM(model2, obs.variables=vars)
 
wls1 <- wls(model="Group1", Cov=R1, aCov=acov1, n=n1, RAM=RAM1, run=F)
wls2 <- wls(model="Group2", Cov=R2, aCov=acov2, n=n2, RAM=RAM2, run=F)
 
## Combine both groups
wls.b1 <- mxModel(model="b1", wls1, wls2, mxFitFunctionMultigroup(c("Group1", "Group2")))
fit.b1 <- mxRun(wls.b1, intervals=TRUE)
 
summary(fit.b1)
 
anova(wls.fit, fit.b1)
sharmel's picture
Offline
Joined: 07/31/2023 - 13:19
Truly appreciated, Mike. Just

Truly appreciated, Mike. Just curious, given that estimates from each group use independent pieces of data, can't we compute the standard error of their coefficients' differences e.g., SE(b1a - b1b = 0) by doing SE_dif = SQRT(SE_b1a^2 + SE_b1b^2) assuming near-normally distributed sampling distribution for b1a - b1b?

If yes, then H0: b1a - b1b = 0, may be tested with standard wald-type, Z-test.

Mike Cheung's picture
Offline
Joined: 10/08/2009 - 22:37
Yes, you can choose between

Yes, you can choose between the likelihood ratio and Wald tests.

sharmel's picture
Offline
Joined: 07/31/2023 - 13:19
Final confirmation, can Wald

Final confirmation, can Wald tests be applied both to the elements of A.matrix and S.matrix from the two groups?

Also, are there any references (papers, book chapters etc.) discussing the use of Wald tests in the manner that I suggested in my previous post for comparing across the estimates from independent groups?

Mike Cheung's picture
Offline
Joined: 10/08/2009 - 22:37
Wald test can be applied to

Wald test can be applied to both A and S matrices.

You may refer to the literature of multiple group SEM.

sharmel's picture
Offline
Joined: 07/31/2023 - 13:19
Dear Mike,

Dear Mike,

Is there any way to convert a wls() object with run=TRUE or run=FALSE to a corresponding lavaan object?

Mike Cheung's picture
Offline
Joined: 10/08/2009 - 22:37
I am afraid not because this

I am afraid not because this functionality is not available in lavaan.