Skip to content

Commit f7615c6

Browse files
committed
lavaan is now smarter in the multiple group setting when data is categorical and group.equal = "thresholds" (following the Wu & Estabrook approach)
1 parent 2d73081 commit f7615c6

File tree

3 files changed

+46
-38
lines changed

3 files changed

+46
-38
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: lavaan
22
Title: Latent Variable Analysis
3-
Version: 0.6-20.2334
3+
Version: 0.6-20.2335
44
Authors@R: c(person(given = "Yves", family = "Rosseel",
55
role = c("aut", "cre"),
66
email = "Yves.Rosseel@UGent.be",

R/lav_options.R

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,13 @@ lav_options_set <- function(opt = NULL) { # nolint
240240

241241
# if categorical, and group.equal contains "intercepts", also add
242242
# thresholds (and vice versa)
243-
if (opt$.categorical && any("intercepts" == opt$group.equal)) {
244-
opt$group.equal <- unique(c(opt$group.equal, "thresholds"))
245-
}
246-
if (opt$.categorical && any("thresholds" == opt$group.equal)) {
247-
opt$group.equal <- unique(c(opt$group.equal, "intercepts"))
248-
}
243+
# not any longer since 0.6-20
244+
#if (opt$.categorical && any("intercepts" == opt$group.equal)) {
245+
# opt$group.equal <- unique(c(opt$group.equal, "thresholds"))
246+
#}
247+
#if (opt$.categorical && any("thresholds" == opt$group.equal)) {
248+
# opt$group.equal <- unique(c(opt$group.equal, "intercepts"))
249+
#}
249250

250251
# clustered ####
251252
# brute-force override (for now)

R/lav_partable_flat.R

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,6 @@ lav_partable_flat <- function(FLAT = NULL, # nolint
144144
categorical <- TRUE
145145
}
146146

147-
# std.lv = TRUE, group.equal includes "loadings"
148-
# if(ngroups > 1L && std.lv && "loadings" %in% group.equal) {
149-
# suggested by Michael Hallquist
150-
# in 0.6.3, we gave a warning,
151-
# warning("lavaan WARNING: std.lv = TRUE forces all variances to be unity",
152-
# " in all groups, despite group.equal = \"loadings\"")
153-
# in >0.6.4, we free the lv variances in all but the first group,
154-
# }
155-
156-
157147
# do we have any EFA lv's? they need special treatment if auto.efa = TRUE
158148
if (!is.null(FLAT$efa) && auto.efa) {
159149
lv.names.efa <- unique(FLAT$lhs[FLAT$op == "=~" &
@@ -783,24 +773,33 @@ lav_partable_flat <- function(FLAT = NULL, # nolint
783773

784774
# specific changes per group
785775
for (g in 2:ngroups) {
786-
# label
787-
# label[group == g] <- paste(label[group == 1], ".g", g, sep="")
788-
789-
# free/fix intercepts
776+
# free/fix intercepts latent variables
790777
if (meanstructure) {
791778
int.idx <- which(op == "~1" &
792779
lhs %in% lv.names.noc &
793780
user == 0L &
794781
group == g)
795782
if (int.lv.free == FALSE && g > 1 &&
796-
("intercepts" %in% group.equal ||
797-
"thresholds" %in% group.equal) &&
783+
("intercepts" %in% group.equal) &&
798784
!("means" %in% group.equal)) {
799785
free[int.idx] <- 1L
800786
ustart[int.idx] <- as.numeric(NA)
801787
}
802788
}
803789

790+
# free intercept indicators if equal thresholds (new in 0.6-20)
791+
if (meanstructure && length(ov.names.ord) > 0L) {
792+
ord.idx <- which(op == "~1" &
793+
lhs %in% ov.names.ord &
794+
user == 0L &
795+
group == g)
796+
if (int.lv.free == FALSE && g > 1 &&
797+
"thresholds" %in% group.equal) {
798+
free[ord.idx] <- 1L
799+
ustart[ord.idx] <- as.numeric(NA)
800+
}
801+
}
802+
804803
# latent variances if std.lv = TRUE (new in 0.6-4)
805804
if (std.lv && "loadings" %in% group.equal &&
806805
!"lv.variances" %in% group.equal) {
@@ -842,21 +841,29 @@ lav_partable_flat <- function(FLAT = NULL, # nolint
842841
}
843842
}
844843

845-
# latent response scaling
846-
if (auto.delta && parameterization == "delta") {
847-
if (any(op == "~*~" & group == g) &&
848-
("thresholds" %in% group.equal)) {
849-
delta.idx <- which(op == "~*~" & group == g)
850-
free[delta.idx] <- 1L
851-
ustart[delta.idx] <- as.numeric(NA)
852-
}
853-
} else if (parameterization == "theta") {
854-
if (any(op == "~*~" & group == g) &&
855-
("thresholds" %in% group.equal)) {
856-
var.ord.idx <- which(op == "~~" & group == g &
857-
lhs %in% ov.names.ord & lhs == rhs)
858-
free[var.ord.idx] <- 1L
859-
ustart[var.ord.idx] <- as.numeric(NA)
844+
# latent response scaling -- categorical only
845+
# - if thresholds are equal -> free scalings/residual variances
846+
# - but not for binary indicators!
847+
if (length(ov.names.ord) > 0L) {
848+
nth <- sapply(ov.names.ord,
849+
function(x) sum(lhs == x & op == "|" & group == 1L))
850+
ov.names.ord.notbinary <- ov.names.ord[nth > 1L]
851+
if (auto.delta && parameterization == "delta") {
852+
if (any(op == "~*~" & group == g) &&
853+
("thresholds" %in% group.equal)) {
854+
delta.idx <- which(op == "~*~" & group == g &
855+
lhs %in% ov.names.ord.notbinary)
856+
free[delta.idx] <- 1L
857+
ustart[delta.idx] <- as.numeric(NA)
858+
}
859+
} else if (parameterization == "theta") {
860+
if (any(op == "~*~" & group == g) &&
861+
("thresholds" %in% group.equal)) {
862+
var.ord.idx <- which(op == "~~" & group == g &
863+
lhs %in% ov.names.ord.notbinary & lhs == rhs)
864+
free[var.ord.idx] <- 1L
865+
ustart[var.ord.idx] <- as.numeric(NA)
866+
}
860867
}
861868
}
862869

0 commit comments

Comments
 (0)