@@ -174,6 +174,36 @@ lav_standardize_lv <- function(lavobject = NULL,
174174 ETA2 [ETA2 < 0 ] <- as.numeric(NA )
175175 ETA <- sqrt(ETA2 )
176176
177+ # Interaction/quadratic term correction (FV)
178+ # (based on Kelava & Brandt, 2022; Brandt et al., 2015)
179+ # For interaction terms A:B, the standardized coefficient should use
180+ # SD(A)*SD(B) instead of SD(A:B) (Eq. 13 in Brandt et al., 2015).
181+ lv.int.names <- lav_partable_vnames(lavpartable , " lv.interaction" ,
182+ block = g
183+ )
184+
185+ if (length(lv.int.names ) > 0L ) {
186+ # Replace ETA for interaction terms with SD(A)*SD(B)
187+ for (int.name in lv.int.names ) {
188+ components <- strsplit(int.name , " :" , fixed = TRUE )[[1L ]]
189+ idx.int <- match(int.name , lv.names )
190+ idx.a <- match(components [1 ], lv.names )
191+ idx.b <- match(components [2 ], lv.names )
192+ if (is.na(idx.a ) || is.na(idx.b ) || is.na(idx.int )) next
193+ ETA [idx.int ] <- ETA [idx.a ] * ETA [idx.b ]
194+ ETA2 [idx.int ] <- ETA [idx.int ]^ 2
195+ }
196+
197+ # Note: centering adjustments for non-zero latent means
198+ # are not yet implemented. LSAM has all the information needed;
199+ # Brandt et al., 2015, Eqs. 11-12 not needed.
200+ warning(" lavaan WARNING: centering adjustments for non-zero " ,
201+ " latent means are not yet implemented for interaction terms." ,
202+ call. = FALSE
203+ )
204+ }
205+ # End interaction/quadratic term correction for now (23/02/26;FV)
206+
177207 # 1a. "=~" regular indicators
178208 idx <- which(partable $ op == " =~" & ! (partable $ rhs %in% lv.names ) &
179209 partable $ block == g )
0 commit comments