Manually convert from unstandardized to standardized coefficients

608 views
Skip to first unread message

AM

unread,
Feb 1, 2023, 3:05:17 AM2/1/23
to lavaan
Hi,

Maybe this is not so much a lavaan question as a latent modeling question in general.

Let's say that I have a simple 2-factor latent model like this:

model <- '
A =~ A1 + A2 + A3
A ~~ A
B =~ B1 + B2 + B3
B ~~ B
A ~~ B
'
fit <- cfa(model = model, data = data)
summary(fit)

Without the std = TRUE argument, summary will only display unstandardized coefficients.

Due to some very complicated reasons, I need to calculate the standardized coefficients manually and not through the usage of std = T argument.

I am interested in calculating the standardized coefficient A ~~ B and A =~ A2, given their unstandardized values printed by the usage of summary. What would be the formula to achieve this?

Terrence Jorgensen

unread,
Feb 3, 2023, 9:14:25 AM2/3/23
to lavaan
I am interested in calculating the standardized coefficient A ~~ B and A =~ A2, given their unstandardized values printed by the usage of summary. What would be the formula to achieve this?

For standardize a covariance:
Cor(x,y) = Cov(x,y) / sqrt( var(x) * var(y) ) 

To standardize a slope:
Beta*(y ~ x) = Beta(y ~ x) * sd(x) / sd(y)

You can find the model-implied variances on the diagonal of lavInspect(fit, "cov.lv"), and SDs are their square-roots.

Terrence D. Jorgensen
Assistant Professor, Methods and Statistics
Research Institute for Child Development and Education, the University of Amsterdam

Yves Rosseel

unread,
Feb 5, 2023, 11:02:44 AM2/5/23
to lav...@googlegroups.com
For A ~~ B this is just converting a covariance to a correlation. As
Terrence mentioned:

Cor(A,B) = Cov(A,B) / sqrt( var(A) * var(B) )

A factor loading A =~ A2 is a regression coefficient, so you need

(A =~ A2) * sqrt(var(A)) / sqrt(var(A2))

where var(A) can be found on the diagonal of lavInspect(fit, "cov.lv")
and var(A2) can be found on the diagonal of lavInspect(fit, "cov.ov")

Yves.

AM

unread,
Feb 6, 2023, 4:44:08 AM2/6/23
to lavaan
Thank you very much for the answers! This is really helpful!

D. Refaeli

unread,
Jun 16, 2023, 5:03:17 AM6/16/23
to lavaan
This doesn't work for me for some reason. Not for CFA and not for SEM.

For CFA, suppose I run the following code:

data(HolzingerSwineford1939)
dat = HolzingerSwineford1939[,7:15]
model = 'visual =~ x1 + x2 + x3
        textual =~ x4 + x5 + x6
        speed =~ x7 + x8 + x9'
fit = cfa(model, data=dat)
summary(fit, standardized=T)

I get the following (truncated here): 

                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  visual =~                                                            
    x1                1.000                               0.900    0.772
    x2                0.554    0.100    5.554    0.000    0.498    0.424
    x3                0.729    0.109    6.685    0.000    0.656    0.581
...
Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .x1                0.549    0.114    4.833    0.000    0.549    0.404
   .x2                1.134    0.102   11.146    0.000    1.134    0.821
   .x3                0.844    0.091    9.317    0.000    0.844    0.662
...
 visual            0.809    0.145    5.564    0.000    1.000    1.000

The Std.lv seems to be the original estimates x sd(latent).  E.g., for x2, 0.498=0.554*sqrt(0.809). (I would have thought it to be divided by, and not multiplied by, but ok). 

But the Std.all doesn't seem to be this divided by the sd(variable). E.g. for x2, 0.424 != 0.554*sqrt(0.809/1.134)...

For SEM it's the same. Std.lv are equal, but std.all are not. E.g.:
model <- '
  # measurement model (CFA)
    ind60 =~ x1 + x2 + x3
    dem60 =~ y1 + y2 + y3 + y4
    dem65 =~ y5 + y6 + y7 + y8
  # regressions / path analysis
    dem60 ~ ind60
    dem65 ~ ind60 + dem60
  # residual correlations
    y1 ~~ y5
    y2 ~~ y4 + y6
    y3 ~~ y7
    y4 ~~ y8
    y6 ~~ y8
'
fit = sem(model, data=PoliticalDemocracy)
summary(fit, standardized=TRUE)

Any help would be appreciated.

Shu Fai Cheung (張樹輝)

unread,
Jun 16, 2023, 5:54:40 AM6/16/23
to lavaan
In the CFA example, note that 1.134 is *not* the variance of x2. It is the *error variance* of x2.

There is a dot before x2 (.x2). The dot denotes that the parameter is the variance of the error term of a variable.

As Yves mentioned in his post, to get the model-implied variances (and covariances) of all observed variables, including endogenous variables, for which their variances are not model parameters, call lavInspect() and set the second argument to "cov.ov":

> lavInspect(fit, "cov.ov")
      x1    x2    x3    x4    x5    x6    x7    x8    x9
x1 1.358                                                
x2 0.448 1.382                                          
x3 0.590 0.327 1.275                                    
x4 0.408 0.226 0.298 1.351                              
x5 0.454 0.252 0.331 1.090 1.660                        
x6 0.378 0.209 0.276 0.907 1.010 1.196                  
x7 0.262 0.145 0.191 0.173 0.193 0.161 1.183            
x8 0.309 0.171 0.226 0.205 0.228 0.190 0.453 1.022      
x9 0.284 0.157 0.207 0.188 0.209 0.174 0.415 0.490 1.015

The model implied variance of x2 is 1.382:

>  0.554 * sqrt(0.809 / 1.382)
[1] 0.4238674

The value is the standardized loading of x2 (under std.all), up to rounding error.

Hope this helps.

-- Shu Fai
Reply all
Reply to author
Forward
0 new messages