lp: Lp Rotation

lpR Documentation

L^p Rotation

Description

Performs L^p rotation to obtain sparse factor loadings.

Usage

GPForth.lp(A, Tmat = diag(ncol(A)), p = 1, normalize = FALSE, eps = 1e-05,
           maxit = 10000, gpaiter = 5)
GPFoblq.lp(A, Tmat = diag(ncol(A)), p = 1, normalize = FALSE, eps = 1e-05,
           maxit = 10000, gpaiter = 5)

Arguments

A

Initial factor loadings matrix to be rotated.

Tmat

Initial rotation matrix.

p

Component-wise L^p where 0 < p \le 1.

normalize

Not recommended for L^p rotation.

eps

Convergence is assumed when the norm of the gradient is smaller than eps.

maxit

Maximum number of iterations allowed in the main loop.

gpaiter

Maximum iterations for the inner GPA rotation loop. The goal is to decrease the objective value, not fully optimize the inner loop. Warnings may appear but can be ignored if the main loop converges.

Details

These functions optimize an L^p rotation objective where 0 < p <= 1. A smaller value of p promotes greater sparsity in the loading matrix but increases computational difficulty. For guidance on choosing p, see the Concluding Remarks in Liu et al. (2023).

The user-facing wrapper functions lpT and lpQ provide random start functionality on top of GPForth.lp and GPFoblq.lp respectively, analogous to how GPFRSorth and GPFRSoblq wrap GPForth and GPFoblq for standard rotation criteria. For most users lpT and lpQ are the recommended entry points.

Since the L^p objective is nonsmooth, a different optimization method is required compared to smooth rotation criteria. GPForth.lp and GPFoblq.lp replace GPForth and GPFoblq for orthogonal and oblique L^p rotations, respectively.

The optimization uses an iterative reweighted least squares (IRLS) approach. The nonsmooth objective is approximated by a smooth weighted least squares function in the outer loop, which is then optimized using GPA in the inner loop (gpaiter controls the maximum inner iterations).

Normalization is not recommended for L^p rotation and may produce unexpected results.

Value

A GPArotation object, which is a list containing:

loadings

Rotated loadings matrix, with one column per factor.

Th

Rotation matrix, satisfying loadings %*% t(Th) = A.

Table

Data frame recording iteration details: iteration count, objective value, and elapsed time.

method

String indicating the rotation objective function.

orthogonal

Logical indicating whether the rotation is orthogonal.

convergence

Logical indicating whether convergence was achieved. Convergence is assessed element-wise using eps as tolerance.

Phi

Covariance matrix of rotated factors, t(Th) %*% Th.

Author(s)

Xinyi Liu, with minor modifications for GPArotation by C. Bernaards.

References

Liu, X., Wallin, G., Chen, Y., and Moustaki, I. (2023). Rotation to sparse loadings using L^p losses and related inference problems. Psychometrika, 88(2), 527–553. doi: 10.1007/s11336-023-09911-y

See Also

lpT, lpQ, vgQ.lp.wls

Examples

  data("WansbeekMeijer", package = "GPArotation")
  fa.unrotated <- factanal(factors = 2, covmat = NetherlandsTV, rotation = "none")
  options(warn = -1)

  # Orthogonal rotation — single start
  fa.lpT1 <- GPForth.lp(loadings(fa.unrotated), p = 1)

  # Orthogonal rotation — 10 random starts
  fa.lpT <- lpT(loadings(fa.unrotated), Tmat = Random.Start(2), p = 1,
                randomStarts = 10)
  print(fa.lpT, digits = 5, sortLoadings = FALSE, Table = TRUE, rotateMat = TRUE)

  # Oblique rotation — single start
  fa.lpQ1 <- GPFoblq.lp(loadings(fa.unrotated), p = 1)

  # Oblique rotation — 10 random starts
  fa.lpQ <- lpQ(loadings(fa.unrotated), p = 1, randomStarts = 10)
  summary(fa.lpQ, Structure = TRUE)

  # Compare Lp (p=1), Lp (p=0.5), and Geomin oblique rotations
  set.seed(1020)
  fa.lpQ1   <- lpQ(loadings(fa.unrotated), p = 1,   randomStarts = 10)
  fa.lpQ0.5 <- lpQ(loadings(fa.unrotated), p = 0.5, randomStarts = 10)
  fa.geo    <- geominQ(loadings(fa.unrotated),       randomStarts = 10)

  # With factor ordering using internal sortGPALoadings helper
  res <- round(cbind(GPArotation:::.sortGPALoadings(fa.lpQ1)$loadings,
                     GPArotation:::.sortGPALoadings(fa.lpQ0.5)$loadings,
                     GPArotation:::.sortGPALoadings(fa.geo)$loadings), 3)
  print(c("oblique --  Lp p=1        Lp p=0.5       Geomin"))
  print(res)

  # Without factor ordering
  res <- round(cbind(fa.lpQ1$loadings, fa.lpQ0.5$loadings, fa.geo$loadings), 3)
  print(c("oblique --  Lp p=1        Lp p=0.5       Geomin"))
  print(res)

  options(warn = 0)

GPArotation documentation built on April 29, 2026, 9:08 a.m.

Related to lp in GPArotation...