Skip to content

Commit 86b5b17

Browse files
committed
fix standardization and MGCFA invariance bug
1 parent 921b5ea commit 86b5b17

File tree

3 files changed

+308
-38
lines changed

3 files changed

+308
-38
lines changed

R/confirmatoryfactoranalysis.R

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,12 @@ confirmatoryFactorAnalysisInternal <- function(jaspResults, dataset, options, ..
322322
if (options[["dataType"]] == "varianceCovariance") {
323323
.quitAnalysis(gettext("Bootstrapping is not available for variance-covariance matrix input."))
324324
}
325-
cfaResult[["lav"]] <- jaspSem::lavBootstrap(cfaResult[["lav"]], options$bootstrapSamples,
326-
standard = options[["standardized"]] != "none", typeStd = type)
325+
326+
# cfaResult[["lav"]] <- jaspSem::lavBootstrap(cfaResult[["lav"]], options$bootstrapSamples,
327+
# standard = options[["standardized"]] != "none", typeStd = type)
328+
329+
cfaResult[["lav"]] <- lavBootstrap(cfaResult[["lav"]], options$bootstrapSamples,
330+
standard = options[["standardized"]] != "none", typeStd = type)
327331
}
328332

329333
# Save cfaResult as state so it's available even when opts don't change
@@ -961,7 +965,7 @@ confirmatoryFactorAnalysisInternal <- function(jaspResults, dataset, options, ..
961965
}
962966

963967
# Intercepts ----
964-
if (options$meanStructure) {
968+
if (options$meanStructure || options$group != "") {
965969

966970
if (options$group != "") {
967971

@@ -1530,29 +1534,20 @@ confirmatoryFactorAnalysisInternal <- function(jaspResults, dataset, options, ..
15301534

15311535
}
15321536

1533-
# delete once jaspSem is merged
1534-
lavBootstrap <- function(fit, samples = 1000, standard = FALSE, typeStd = NULL) {
1535-
# Run bootstrap, track progress with progress bar
1536-
# Notes: faulty runs are simply ignored
1537-
# recommended: add a warning if not all boot samples are successful
1538-
# fit <- lavBootstrap(fit, samples = 1000)
1539-
# if (nrow(fit@boot$coef) < 1000)
1540-
# tab$addFootnote(gettextf("Not all bootstrap samples were successful: CI based on %.0f samples.", nrow(fit@boot$coef)),
1541-
# "<em>Note.</em>")
1542-
1537+
# delete this once jaspSem is merged
1538+
lavBootstrap <- function(fit, samples = 1000, standard = FALSE, typeStd = NULL, iseed = NULL) {
15431539

1544-
coef_with_callback <- function(lav_object) {
1540+
coefWithCallback <- function(lav_object) {
15451541
# Progress bar is ticked every time coef() is evaluated, which happens once on the main object:
15461542
# https://github.com/yrosseel/lavaan/blob/77a568a574e4113245e2f6aff1d7c3120a26dd90/R/lav_bootstrap.R#L107
15471543
# and then every time on a successful bootstrap:
15481544
# https://github.com/yrosseel/lavaan/blob/77a568a574e4113245e2f6aff1d7c3120a26dd90/R/lav_bootstrap.R#L375
15491545
# i.e., samples + 1 times
15501546
progressbarTick()
1551-
15521547
return(lavaan::coef(lav_object))
15531548
}
15541549

1555-
coef_with_callback_std <- function(lav_object, typeStd) {
1550+
coefWithCallbackStd <- function(lav_object, typeStd) {
15561551
std <- lavaan::standardizedSolution(lav_object, type = typeStd)
15571552
out <- std$est.std
15581553

@@ -1564,38 +1559,39 @@ lavBootstrap <- function(fit, samples = 1000, standard = FALSE, typeStd = NULL)
15641559
startProgressbar(samples + 1)
15651560

15661561
if (!standard) {
1567-
bootres <- lavaan::bootstrapLavaan(object = fit, R = samples, FUN = coef_with_callback)
1562+
bootres <- lavaan::bootstrapLavaan(object = fit, R = samples, FUN = coefWithCallback, iseed = iseed)
15681563
} else {
1569-
bootres <- lavaan::bootstrapLavaan(object = fit, R = samples, FUN = coef_with_callback_std, typeStd = typeStd)
1564+
bootres <- lavaan::bootstrapLavaan(object = fit, R = samples, FUN = coefWithCallbackStd, typeStd = typeStd, iseed = iseed)
15701565
}
15711566

15721567
# Add the bootstrap samples to the fit object
15731568
fit@boot <- list(coef = bootres)
15741569
fit@Options$se <- "bootstrap"
15751570

15761571
# exclude error bootstrap runs
1577-
err_id <- attr(fit@boot$coef, "error.idx")
1578-
if (length(err_id) > 0L) {
1579-
fit@boot$coef <- fit@boot$coef[-err_id, , drop = FALSE]
1572+
errId <- attr(fit@boot$coef, "error.idx")
1573+
if (length(errId) > 0L) {
1574+
fit@boot$coef <- fit@boot$coef[-errId, , drop = FALSE]
15801575
}
15811576

1582-
# we actually need the SEs from the bootstrap not the SEs from ML or something
1577+
# we actually need the SEs from the bootstrap not the SEs from ML or some other estimator
15831578
N <- nrow(fit@boot$coef)
1579+
P <- ncol(fit@boot$coef)
1580+
freePars <- which(fit@ParTable$free != 0)
15841581

15851582
# we multiply the var by (n-1)/n because lavaan actually uses n for the variance instead of n-1
15861583
if (!standard) {
15871584
# for unstandardized
1588-
fit@ParTable$se[fit@ParTable$free != 0] <- apply(fit@boot$coef, 2, sd) * sqrt((N-1)/N)
1585+
fit@ParTable$se[freePars] <- apply(fit@boot$coef, 2, sd) * sqrt((N-1)/N)
15891586
} else {
1590-
fit@ParTable$se <- apply(fit@boot$coef, 2, sd) * sqrt((N-1)/N)
1591-
# the standardized solution gives all estimates not only the unconstrained, so we need to change
1592-
# the free prameters in the partable and also change the estimate
1593-
fit@ParTable$free <- seq_len(ncol(fit@boot$coef))
1587+
# when there are contraints the parameterestimates() function expects a boot sample for the free parameters only
1588+
fit@ParTable$se[1:P] <- apply(fit@boot$coef, 2, sd) * sqrt((N-1)/N)
1589+
fit@boot$coef <- fit@boot$coef[, freePars, drop = FALSE]
15941590
std <- lavaan::standardizedSolution(fit, type = typeStd)
1595-
fit@ParTable$est <- std$est.std
1591+
# for the standardized output we also replace some constrained elements
1592+
fit@ParTable$est[1:P] <- std$est.std
15961593
}
15971594

1598-
15991595
return(fit)
16001596
}
16011597

renv.lock

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
},
126126
"RUnit": {
127127
"Package": "RUnit",
128-
"Version": "0.4.33",
128+
"Version": "0.4.33.1",
129129
"Source": "Repository",
130130
"Requirements": [
131131
"graphics",
@@ -136,7 +136,7 @@
136136
},
137137
"Rcpp": {
138138
"Package": "Rcpp",
139-
"Version": "1.0.14",
139+
"Version": "1.1.0",
140140
"Source": "Repository",
141141
"Requirements": [
142142
"methods",
@@ -176,7 +176,7 @@
176176
},
177177
"Rcsdp": {
178178
"Package": "Rcsdp",
179-
"Version": "0.1.57.5",
179+
"Version": "0.1.57.6",
180180
"Source": "Repository",
181181
"Requirements": [
182182
"methods"
@@ -419,7 +419,7 @@
419419
},
420420
"cluster": {
421421
"Package": "cluster",
422-
"Version": "2.1.8",
422+
"Version": "2.1.8.1",
423423
"Source": "Repository",
424424
"Requirements": [
425425
"graphics",
@@ -551,7 +551,7 @@
551551
},
552552
"evaluate": {
553553
"Package": "evaluate",
554-
"Version": "1.0.3",
554+
"Version": "1.0.4",
555555
"Source": "Repository",
556556
"Requirements": [
557557
"R"
@@ -1303,7 +1303,7 @@
13031303
},
13041304
"pbapply": {
13051305
"Package": "pbapply",
1306-
"Version": "1.7-2",
1306+
"Version": "1.7-4",
13071307
"Source": "Repository",
13081308
"Requirements": [
13091309
"parallel",
@@ -1318,7 +1318,7 @@
13181318
},
13191319
"pillar": {
13201320
"Package": "pillar",
1321-
"Version": "1.10.2",
1321+
"Version": "1.11.0",
13221322
"Source": "Repository",
13231323
"Requirements": [
13241324
"cli",
@@ -1413,7 +1413,7 @@
14131413
},
14141414
"psych": {
14151415
"Package": "psych",
1416-
"Version": "2.4.12",
1416+
"Version": "2.5.6",
14171417
"Source": "Repository",
14181418
"Requirements": [
14191419
"GPArotation",
@@ -1429,7 +1429,7 @@
14291429
},
14301430
"purrr": {
14311431
"Package": "purrr",
1432-
"Version": "1.0.4",
1432+
"Version": "1.1.0",
14331433
"Source": "Repository",
14341434
"Requirements": [
14351435
"cli",

0 commit comments

Comments
 (0)