Sparse PCA with multiple principal components in R.
The msPCA package computes sparse loading vectors that
explain a high fraction of variance while controlling non-redundancy
across components. It supports two non-redundancy definitions:
The package is listed in two CRAN Task Views: Machine Learning and Statistical Learning and Chemometrics and Computational Physics.
Install from CRAN:
install.packages("msPCA")
library(msPCA)Install development version from GitHub:
install.packages("devtools")
devtools::install_github("jeanpauphilet/msPCA")
library(msPCA)The main function is mspca().
Inputs (following the elasticnet convention, the data is
a single argument M plus a type selector):
M: the data matrix,type: "Sigma" (default) treats
M as a covariance/correlation matrix (p x p);
"X" treats M as a raw data matrix
(n observations x p variables),r: number of sparse principal components,ks: integer vector of length r with
sparsity budgets.With type = "X", mspca() applies the
algorithm to the data directly via the products
t(X) %*% (X %*% beta) and never forms the
p x p matrix, which reduces each iteration’s matrix–vector
product from O(p^2) to O(np). Pass
type = "X" when the number of variables greatly exceeds the
number of observations; when n > p the dense
type = "Sigma" path is cheaper.
Output fields:
x_best: sparse loading matrix
(p x r),objective_value,feasibility_violation,runtime,feasibilityConstraintType: the constraint enforced,
reused as the default for all diagnostics,nonredundancy: the pairwise violation matrices under
both constraint definitions, computed at fit time.Example on mtcars:
library(msPCA)
Sigma <- cor(datasets::mtcars)
set.seed(42)
res <- mspca(Sigma, r = 2, ks = c(4, 4), verbose = FALSE) # type = "Sigma" is the default
print(res)
summary(res)
feasibility_violation_off(Sigma, res$x_best, feasibilityConstraintType = 0)
fraction_variance_explained(Sigma, res$x_best)Equivalent workflow from the raw data matrix (no covariance matrix needed):
library(msPCA)
X <- as.matrix(datasets::mtcars)
set.seed(42)
# type = "X" treats the first argument as raw data; scale = TRUE operates on the
# correlation matrix, matching cor(mtcars) above.
res <- mspca(X, r = 2, ks = c(4, 4), type = "X", scale = TRUE, verbose = FALSE)
print(res)
summary(res)
fraction_variance_explained(cor(X), res$x_best)| Article | What it covers |
|---|---|
Worked
example on mtcars |
The basic workflow: fitting,
print()/summary(), the two constraint types,
diagnostics. Start here. |
| Case study: sparse factors in S&P 500 returns | A non-trivial application on the bundled snp500 data
(423 stocks), showing how the choice of non-redundancy constraint
changes the factors recovered. |
| Algorithm and implementation notes | The optimization problem, algorithm description in full,
implementation and complexity, and guidance on choosing ks,
the constraint type and the iteration budgets. |
| Benchmarking against other sparse PCA packages | msPCA against seven competing packages, eight functions
in all, on four real datasets, with the exact configuration used for
each. Website only — not shipped with the package. |
The first three are installed vignettes: after
install.packages("msPCA"), run
vignette(package = "msPCA") to list them, or e.g.
vignette("case-study-snp500", package = "msPCA").
ks is the main tuning input, and
feasibilityConstraintType selects the notion of
non-redundancy: 0 (default) enforces orthogonality of the
loading vectors, 1 enforces zero pairwise correlation of
the components. Use 0 when the loadings serve as a
geometric projection basis, 1 when statistical
decorrelation of the component scores is the priority.
For a fuller treatment — how to sweep ks and read the
resulting sparsity/variance trade-off, when the two constraint types
diverge, and how to set maxIter, maxRestartTPM
and minRestartTPM — see Algorithm
and implementation notes.
The script notebooks/notebook_synthetic.R compares
msPCA with elasticnet::spca() on synthetic
data across sample sizes and exports the figures below.
To regenerate these files, run
notebooks/notebook_synthetic.R from the repository root. It
writes to notebooks/; copy the two PNGs into
man/figures/ afterwards, since that is where this README
and the website read them from. For a broader comparison — seven
competing packages on four real datasets — see the benchmarking
article.
mspca(M, r, ks, type = c("Sigma", "X"), ...): multiple
sparse PCs.tpm(M, k, type = c("Sigma", "X"), ...): single sparse
PC via truncated power method.mspca() returns an object of class mspca
with print() and summary() methods. Both read
what they report from the fitted object, so neither needs the covariance
matrix passed back in. summary() reports the non-redundancy
violations under the constraint type used to fit; pass
feasibilityConstraintType explicitly to inspect the
solution under the other definition.
Useful optional arguments in mspca():
feasibilityConstraintTypefeasibilityTolerancemaxIterstallingTolerancetimeLimitTPMmaxRestartTPMminRestartTPMRaw-data arguments (type = "X"):
center (default TRUE), scale
(default TRUE, set FALSE for covariance),divisor ("n-1" for the sample covariance,
the default, or "n").Covariance-matrix validation arguments
(type = "Sigma"):
checkPSD (default TRUE),
symTolerance, psdTolerance.fraction_variance_explained(Sigma, U)fraction_variance_explained_perPC(Sigma, U)variance_explained_perPC(Sigma, U)feasibility_violation_off(Sigma, U, feasibilityConstraintType),
for scoring loadings that did not come from mspca(). Under
feasibilityConstraintType = 1 the violation is normalized
by the total variance tr(Sigma), making it invariant to a
rescaling of Sigma.print(sol_object, digits = 3) /
summary(sol_object)snp500: the market-deflated correlation matrix of daily
log-returns for 423 S&P 500 constituents, January 2010 – December
2019 (?snp500). Used by the S&P 500 case study; derived
from a CC0-licensed Kaggle dataset by
data-raw/snp500.R.If you use msPCA in academic work, please cite the
package and the underlying paper.
You can retrieve the package citation in R with:
citation("msPCA")Reference paper:
@article{cory2026sparse,
title = {Sparse PCA with Multiple Components},
author = {Cory-Wright, Ryan and Pauphilet, Jean},
year = {2026},
journal = {Operations Research},
doi = {10.1287/opre.2023.0598}
}Package structure overview:
R/
main.R: user-facing functions and helper
diagnostics.data.R: documentation for the bundled
snp500 dataset.RcppExports.R: R interface for compiled code, generated
by Rcpp::compileAttributes().src/
msPCA_R_CPP.cpp: C++ implementation of the core
algorithm and the dense/raw-data entry points.CovOperator.h: covariance-operator abstraction
(DenseOp for Sigma, GramOp for
X).ConstantArguments.h: internal algorithm constants.RcppExports.cpp: generated C++ interface.Makevars, Makevars.win: compilation
settings.man/: function documentation generated from roxygen
comments.vignettes/: the three installed vignettes, plus
articles/ for the website-only benchmarking article
(build-ignored).data-raw/: script and archival source for
data/snp500.rda (build-ignored).inst/vignette-data/: pre-computed results read by the
case-study vignette.replication/: scripts reproducing the benchmarking and
case-study results reported in the paper and in the benchmarking article
(build-ignored).tests/testthat/: unit tests.notebooks/
notebook_mtcars.Rnotebook_plot.Rnotebook_synthetic.RmsPCA_synthetic_results.csvFor interface changes, regenerate exports and documentation with
Rcpp::compileAttributes() and
devtools::document(). After editing
data-raw/snp500.R, rebuild the dataset with
source("data-raw/snp500.R").
See LICENSE.