| Title: | Penalized Estimation for Latent Variable Models with 'lavaan' |
|---|---|
| Description: | Extends the popular 'lavaan' package by adding penalized estimation capabilities. It supports penalty on individual parameters as well as the difference between parameters. |
| Authors: | Hok Chio (Mark) Lai [aut, cre, cph] (ORCID: <https://orcid.org/0000-0002-9196-7406>) |
| Maintainer: | Hok Chio (Mark) Lai <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.0.1 |
| Built: | 2026-05-15 10:12:12 UTC |
| Source: | https://github.com/marklhc/plavaan |
Computes the total loss across all pairwise combinations of rows in a matrix.
composite_pair_loss(x, fun, trans = identity, rescale = "df", ...)composite_pair_loss(x, fun, trans = identity, rescale = "df", ...)
x |
A numeric vector, matrix, or data frame. If not a matrix, it will be coerced to one after applying the transformation function. |
fun |
A function to compute the loss for each pairwise difference.
The package supports the alignment loss ( |
trans |
A transformation function to apply to |
rescale |
Either |
... |
Additional arguments passed to the loss function |
The function works by:
Applying the transformation function trans to the input x
Converting the result to a matrix
Generating all possible pairwise combinations of row indices
Computing the difference between each pair of rows
Applying the loss function fun to each difference
Summing all the individual losses
A numeric scalar representing the sum of losses across all pairwise combinations of rows.
# Example with a simple matrix x <- matrix(runif(12), nrow = 4) composite_pair_loss(x, fun = alf) # Example with log transformation and L2 loss composite_pair_loss(x, fun = function(x) x^2, trans = log)# Example with a simple matrix x <- matrix(runif(12), nrow = 4) composite_pair_loss(x, fun = alf) # Example with log transformation and L2 loss composite_pair_loss(x, fun = function(x) x^2, trans = log)
For small eps this provides a smooth, numerically stable approximation of |x|^(1/2) (i.e. the square root of the absolute value). The function is vectorized over x.
alf(x, eps = 0.001) l0a(x, eps = 0.01)alf(x, eps = 0.001) l0a(x, eps = 0.01)
x |
Numeric vector. Input values to transform. |
eps |
Positive numeric scalar (default .001 for |
The ALF, (x^2 + eps)^(1/4), is useful when a smooth surrogate for sqrt(|x|) is required (for optimization or regularization) while maintaining numerical stability near x = 0.
L0a, x^2/(x^2 + eps), is an approximation of the L0 penalty.
Numeric vector of the same length as x.
alf(0) alf(c(-4, -1, 0, 1, 4)) alf(0.5, eps = 1e-6) l0a(0) l0a(c(0, 1e-3, 0.1, 1)) l0a(c(-2, 0, 2), eps = 1e-4)alf(0) alf(c(-4, -1, 0, 1, 4)) alf(0.5, eps = 1e-6) l0a(0) l0a(c(0, 1e-3, 0.1, 1)) l0a(c(-2, 0, 2), eps = 1e-4)
Performs penalized estimation on a lavaan model object by optimizing a penalized objective function. The function extracts the objective function from a lavaan model, applies a penalty function to specified parameters or pairwise differences of parameters, and returns an updated model with the optimized parameter estimates.
penalized_est( x, w, pen_par_id = NULL, pen_diff_id = NULL, pen_fn = "l0a", pen_gr = NULL, se = "none", opt_control = list() )penalized_est( x, w, pen_par_id = NULL, pen_diff_id = NULL, pen_fn = "l0a", pen_gr = NULL, se = "none", opt_control = list() )
x |
A fitted lavaan model object from which estimation components will be extracted. |
w |
Numeric scalar. Penalty weight (multiplier) applied to the penalty terms. |
pen_par_id |
Integer vector of parameter IDs to apply the penalty function
directly to, in the same order as returned by |
pen_diff_id |
List of matrices containing parameter IDs. For each matrix, the penalty is applied to the pairwise differences of parameters in the same column indicated by the IDs. For matrices with names starting with "loading", the log transformation is applied before computing differences. |
pen_fn |
A character string ( |
pen_gr |
A function that computes the gradient of the penalty function.
If |
se |
Character string specifying the type of standard errors to compute.
Options are |
opt_control |
A list of control parameters passed to |
The function uses nlminb() to minimize a penalized objective function that
combines the standard lavaan objective function with a penalty term. Only the
parameter estimates and the log-likelihood should be interpreted. The
returned object was not "fitted" (do.fit = FALSE) to avoid users
interpreting the standard errors, which are generally not valid with
penalized estimation. The degrees of freedom may also be inaccurate. If the
optimization does not converge (convergence code != 0), a warning is issued.
A lavaan model object updated with the penalized parameter estimates.
The returned object includes an attribute opt_info containing the
optimization information returned by nlminb().
The returned object is not fitted using standard ML. Standard errors reported
by summary() or parameterEstimates() will be missing unless
se = "robust.huber.white" was specified. Even then, they are based on an
experimental sandwich approximation and should be interpreted with caution.
library(lavaan) # Define a longitudinal factor model with PoliticalDemocracy data model <- " dem60 =~ y1 + y2 + y3 + y4 dem65 =~ y5 + y6 + y7 + y8 dem60 ~~ dem65 dem60 ~~ 1 * dem60 dem65 ~~ NA * dem65 dem60 ~ 0 dem65 ~ NA * 1 y1 ~~ y5 y2 ~~ y6 y3 ~~ y7 y4 ~~ y8 " # Fit the model without constraints first to get parameter table fit_un <- cfa(model, data = PoliticalDemocracy, std.lv = TRUE, meanstructure = TRUE, do.fit = FALSE) # Get parameter IDs pt <- parTable(fit_un) # Loadings load_60 <- pt$free[pt$op == "=~" & pt$lhs == "dem60"] load_65 <- pt$free[pt$op == "=~" & pt$lhs == "dem65"] # Intercepts int_60 <- pt$free[pt$op == "~1" & pt$lhs %in% c("y1", "y2", "y3", "y4")] int_65 <- pt$free[pt$op == "~1" & pt$lhs %in% c("y5", "y6", "y7", "y8")] # Apply penalized estimation to penalize differences in loadings and intercepts pen_fit <- penalized_est( x = fit_un, w = 0.03, pen_diff_id = list( loadings = rbind(load_60, load_65), intercepts = rbind(int_60, int_65) ), pen_fn = "l0a" ) # Compare parameter estimates summary(pen_fit)library(lavaan) # Define a longitudinal factor model with PoliticalDemocracy data model <- " dem60 =~ y1 + y2 + y3 + y4 dem65 =~ y5 + y6 + y7 + y8 dem60 ~~ dem65 dem60 ~~ 1 * dem60 dem65 ~~ NA * dem65 dem60 ~ 0 dem65 ~ NA * 1 y1 ~~ y5 y2 ~~ y6 y3 ~~ y7 y4 ~~ y8 " # Fit the model without constraints first to get parameter table fit_un <- cfa(model, data = PoliticalDemocracy, std.lv = TRUE, meanstructure = TRUE, do.fit = FALSE) # Get parameter IDs pt <- parTable(fit_un) # Loadings load_60 <- pt$free[pt$op == "=~" & pt$lhs == "dem60"] load_65 <- pt$free[pt$op == "=~" & pt$lhs == "dem65"] # Intercepts int_60 <- pt$free[pt$op == "~1" & pt$lhs %in% c("y1", "y2", "y3", "y4")] int_65 <- pt$free[pt$op == "~1" & pt$lhs %in% c("y5", "y6", "y7", "y8")] # Apply penalized estimation to penalize differences in loadings and intercepts pen_fit <- penalized_est( x = fit_un, w = 0.03, pen_diff_id = list( loadings = rbind(load_60, load_65), intercepts = rbind(int_60, int_65) ), pen_fn = "l0a" ) # Compare parameter estimates summary(pen_fit)