library(lavaan)
#> This is lavaan 0.6-15
#> lavaan is FREE software! Please report any bugs.
# Create a dataset from a simple mediation model
# Use a large N such that the delta method CIs
# are good approximation.
n <- 10000
set.seed(8570614)
x <- rnorm(n)
m <- .3 * x + sqrt(1 - .3^2) * rnorm(n)
y <- .4 * m + sqrt(1 - .4^2) * rnorm(n)
es <- MASS::mvrnorm(n, rep(0, 9), diag(9))
tmp1 <- rep(.8, 3)
tmp2 <- rep(0, 3)
lambda <- matrix(c(tmp1, tmp2, tmp2,
tmp2, tmp1, tmp2,
tmp2, tmp2, tmp1), 9, 3)
xs <- cbind(x, m, y) %*% t(lambda) + es
dat <- setNames(as.data.frame(xs), paste0("x", 1:9))
head(dat)
#> x1 x2 x3 x4 x5 x6
#> 1 0.7016253 0.06152504 0.68347512 2.02138386 -0.5280180 0.5149986
#> 2 -1.4035905 -0.99890373 -0.07430941 -0.59950393 -1.5518963 1.6910274
#> 3 -0.4406080 -2.39450424 -2.68569378 -1.24964144 -0.6139338 -0.4155945
#> 4 -0.2377625 1.45487535 1.69618648 0.08763255 1.6434675 0.4468465
#> 5 0.4607871 -0.43366503 -0.48382764 -0.17073499 -2.2080429 -0.8658210
#> 6 -1.7749543 -1.41704701 -0.43697121 1.23592253 0.7286826 -0.9211944
#> x7 x8 x9
#> 1 -1.2952441 0.18894715 -0.30120103
#> 2 -0.9708784 -0.05597014 0.11817134
#> 3 -2.3909430 -1.20812274 -2.08156240
#> 4 -1.2341506 1.63384080 -0.78532321
#> 5 -1.0080350 0.79803321 0.02724673
#> 6 -2.1795875 -0.10557664 -0.60519642
mod <-
"
x =~ x1 + x2 + x3
m =~ x4 + x5 + x6
y =~ x7 + x8 + x9
m ~ x
y ~ m
"
fit <- sem(mod, dat)
lavInspect(fit, "cor.lv")
#> x m y
#> x 1.000
#> m 0.311 1.000
#> y 0.129 0.415 1.000
# Get the unique elements
lav_matrix_vech(lavInspect(fit, "cor.lv"), diagonal = FALSE)
#> [1] 0.3113329 0.1292602 0.4151833
# Define a function for bootstrapLavaan()
cor_lv <- function(object) {
out <- lavaan::lav_matrix_vech(lavaan::lavInspect(object, "cor.lv"),
diagonal = FALSE)
stats::setNames(out, c("x~~m", "x~~y", "m~~y"))
}
# Check the function
cor_lv(fit)
#> x~~m x~~y m~~y
#> 0.3113329 0.1292602 0.4151833
lavInspect(fit, "cor.lv")
#> x m y
#> x 1.000
#> m 0.311 1.000
#> y 0.129 0.415 1.000
# Do bootstrapping
boot_out <- bootstrapLavaan(fit,
R = 2000,
FUN = cor_lv,
parallel = c("snow"),
ncpus = 9,
iseed = 53243)
# The output
head(boot_out)
#> x~~m x~~y m~~y
#> [1,] 0.3181989 0.1408866 0.4427627
#> [2,] 0.2961989 0.1221270 0.4123140
#> [3,] 0.3149348 0.1237163 0.3928313
#> [4,] 0.3311231 0.1387524 0.4190356
#> [5,] 0.3241540 0.1343073 0.4143318
#> [6,] 0.3240413 0.1373217 0.4237785
# Keep rows without NAs
boot_out <- na.omit(boot_out)
# Check the means of the bootstrap estimates
colMeans(boot_out)
#> x~~m x~~y m~~y
#> 0.3115700 0.1292970 0.4149475
cor_lv(fit)
#> x~~m x~~y m~~y
#> 0.3113329 0.1292602 0.4151833
# Form the 95% percentile CIs
t(apply(boot_out, 2, quantile, probs = c(.025, .975), na.rm = TRUE))
#> 2.5% 97.5%
#> x~~m 0.2842050 0.3385694
#> x~~y 0.1142739 0.1441069
#> m~~y 0.3866340 0.4437185
# Compare with delta method CIs
# For this model, correlations x~~m and m~~y
# are equal to the standardized m~x and y~m, respectively.
standardizedSolution(fit)[c(10:11), ]
#> lhs op rhs est.std se z pvalue ci.lower ci.upper
#> 10 m ~ x 0.311 0.014 21.918 0 0.283 0.339
#> 11 y ~ m 0.415 0.014 30.619 0 0.389 0.442
cor_lv <- function(object) {
out <- lavaan::lav_matrix_vech(lavaan::lavInspect(object, "cor.lv"),
diagonal = FALSE)
stats::setNames(out, c("x~~m", "x~~y", "m~~y"))
}
To view this discussion on the web visit https://groups.google.com/d/msgid/lavaan/AM0PR04MB595528B5FACC3327EE371290FC6F9%40AM0PR04MB5955.eurprd04.prod.outlook.com.
Thank you very much Shu Fai Cheung (張樹č¼) for your function, and again I thank Christian because I have seen the code you provided on October 11th 2022.
As my model is a CFA, I first looked for solutions in existing functions in lavaan but it is not so straight: parameterestimates() and standardizedSolution() are enough for models like the popular visual-textual-speed because all possible correlations within the model are already in āParameter Estimatesā (default output) but my model is a third-degree CFA and not all possible correlations are in āParameter Estimatesā. Letās say that, as seen in a plot, two different subfactors may be in two unrelated corners.
lavInspect(results, "cor.lv") provides all possible pairs of correlations. Maybe standardizedSolution() provides all of them by using GLIST, est, and partable arguments but I disregarded this approach after reading the Note āThe est, GLIST, and partable arguments are not meant for everyday users, but for authors of external R packages that depend on lavaan. Only to be used with great caution.ā
At the end I ran the following, which is easier than your elaborated approaches (for instance, no lav_matrix() is used):
bootCorrelations <- bootstrapLavaan(results, R=2000,FUN=lavInspect, what="cor.lv")
And then the 95% CI will be at quantiles 2.5 and 97.5%
I would appreciate if you could confirm this is also correct. The means of the bootstrap are pretty similar to cor.lv correlations.
Ā
Again, thank you very much for your attention. I will appreciate any help.
Best regards,
Xavier Suriol
boot_out_direct <- bootstrapLavaan(fit,
R = 2000,
FUN = lavInspect,
parallel = c("snow"),
ncpus = 9,
iseed = 53243,
what = "cor.lv")
# The output
head(boot_out_direct)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] 1 0.3181989 0.1408866 0.3181989 1 0.4427627 0.1408866 0.4427627 1
#> [2,] 1 0.2961989 0.1221270 0.2961989 1 0.4123140 0.1221270 0.4123140 1
#> [3,] 1 0.3149348 0.1237163 0.3149348 1 0.3928313 0.1237163 0.3928313 1
#> [4,] 1 0.3311231 0.1387524 0.3311231 1 0.4190356 0.1387524 0.4190356 1
#> [5,] 1 0.3241540 0.1343073 0.3241540 1 0.4143318 0.1343073 0.4143318 1
#> [6,] 1 0.3240413 0.1373217 0.3240413 1 0.4237785 0.1373217 0.4237785 1
# Keep rows without NAs
boot_out_direct <- na.omit(boot_out_direct)
# Check the means of the bootstrap estimates
colMeans(boot_out_direct)
#> [1] 1.0000000 0.3115700 0.1292970 0.3115700 1.0000000 0.4149475 0.1292970
#> [8] 0.4149475 1.0000000
cor_lv(fit)
#> x~~m x~~y m~~y
#> 0.3113329 0.1292602 0.4151833
# Form the 95% percentile CIs
t(apply(boot_out_direct, 2, quantile, probs = c(.025, .975), na.rm = TRUE))
#> 2.5% 97.5%
#> [1,] 1.0000000 1.0000000
#> [2,] 0.2842050 0.3385694
#> [3,] 0.1142739 0.1441069
#> [4,] 0.2842050 0.3385694
#> [5,] 1.0000000 1.0000000
#> [6,] 0.3866340 0.4437185
#> [7,] 0.1142739 0.1441069
#> [8,] 0.3866340 0.4437185
#> [9,] 1.0000000 1.0000000
# Compare with delta method CIs
# For this model, correlations x~~m and m~~y
# are equal to the standardized m~x and y~m, respectively.
standardizedSolution(fit)[c(10:11), ]
#> lhs op rhs est.std se z pvalue ci.lower ci.upper
#> 10 m ~ x 0.311 0.014 21.918 0 0.283 0.339
#> 11 y ~ m 0.415 0.014 30.619 0 0.389 0.442
To view this discussion on the web visit https://groups.google.com/d/msgid/lavaan/d8898629-6afb-4360-9bcc-1e9831ee8188n%40googlegroups.com.
Do you know how to cite a script released in a google group forum? Is the following correct?
Arnold, Christian (2023, May 6). Ā āBootstrap estimates, standardized solution, R squares, HTMT, group differences, consistent p-values, and other coefficients with a single bootstrap runā [Online forum script release]. Message posted to https://groups.google.com/g/lavaan/c/0RSsh4M6zQg
I was inspired by https://utas.libguides.com/c.php?g=498348&p=3412833
Best regards,