Answer

SVD and PCA: how the singular value decomposition does the same job

Inspired by a question on Cross Validated ·

pcadimensionality reduction

The short answer

Center the columns of X and factor it as X = U D V'. The columns of V are the principal axes, the scores are U D (equal to X V), and each squared singular value divided by n - 1 is a component's variance, i.e. its eigenvalue. Keeping the first k singular values gives the best rank-k summary of the data. R's prcomp() works this way because it is more accurate than forming the covariance matrix first.

Two routes to the same answer

Textbooks usually introduce PCA through the covariance matrix: compute it, find its eigenvectors and eigenvalues, and project the data onto the eigenvectors. (The plain-language PCA page explains what those eigenvectors and eigenvalues mean.) The singular value decomposition, or SVD, skips the covariance matrix and works on the data directly. Any n-by-p matrix with n at least p can be written as a product of three pieces:

The link to PCA takes one line of algebra. If X has centered columns, its covariance matrix is X'X / (n - 1). Substituting the SVD gives X'X = V D U'U D V' = V D² V', because U'U is the identity. So the covariance matrix equals V (D² / (n - 1)) V'. That is an eigen-decomposition: the eigenvectors are the columns of V, and the eigenvalues are the squared singular values divided by n - 1.

What each SVD piece means in PCA terms

Centering is essential. An SVD of the raw, uncentered matrix gives a first direction that mostly points towards the column means, which is not PCA. If you want PCA on the correlation matrix instead, also divide each centered column by its standard deviation before the SVD.

Checking the equivalence in R and Python

The example below simulates four variables for 200 observations, centers them, and does PCA both ways: eigen() on the covariance matrix and svd() on the data. It then checks the scores, compares with prcomp(), and builds a rank-2 approximation. The Python tab does the same with NumPy.

R

set.seed(134282)
n <- 200
# Four measurements driven by one strong and one weaker hidden factor
f1 <- rnorm(n); f2 <- rnorm(n)
X <- cbind(a = 2 * f1 + rnorm(n, sd = 0.5),
           b = 2 * f1 + f2 + rnorm(n, sd = 0.5),
           c = f2 + rnorm(n, sd = 0.5),
           d = rnorm(n))

Xc <- scale(X, center = TRUE, scale = FALSE)  # PCA needs centered columns

# Route 1: eigen-decomposition of the covariance matrix
e <- eigen(cov(Xc))
round(e$values, 4)

# Route 2: SVD of the centered data matrix, Xc = U D V'
s <- svd(Xc)
round(s$d, 4)                   # singular values
round(s$d^2 / (n - 1), 4)       # squared and divided by n - 1: the eigenvalues
round(abs(s$v) - abs(e$vectors), 10)  # V = eigenvectors (up to sign)

# Scores: U D equals Xc V
scores <- s$u %*% diag(s$d)
max(abs(abs(scores) - abs(Xc %*% s$v)))
round(apply(scores, 2, var), 4)  # variances again equal the eigenvalues

# prcomp() itself uses the SVD
p <- prcomp(X)
round(p$sdev^2, 4)
round(s$d^2 / sum(s$d^2), 4)    # proportion of variance explained

# Rank-2 reconstruction keeps the two largest singular values
X2 <- s$u[, 1:2] %*% diag(s$d[1:2]) %*% t(s$v[, 1:2])
round(1 - sum((Xc - X2)^2) / sum(Xc^2), 4)  # share of variance kept

Python

import numpy as np

rng = np.random.default_rng(134282)
n = 200
# Four measurements driven by one strong and one weaker hidden factor
f1 = rng.normal(0, 1, n)
f2 = rng.normal(0, 1, n)
X = np.column_stack([
    2 * f1 + rng.normal(0, 0.5, n),        # a
    2 * f1 + f2 + rng.normal(0, 0.5, n),   # b
    f2 + rng.normal(0, 0.5, n),            # c
    rng.normal(0, 1, n),                   # d
])

Xc = X - X.mean(axis=0)  # PCA needs centered columns

# Route 1: eigen-decomposition of the covariance matrix
values, vectors = np.linalg.eigh(np.cov(Xc, rowvar=False))
order = np.argsort(values)[::-1]          # eigh sorts ascending; flip
values, vectors = values[order], vectors[:, order]
print(np.round(values, 4))

# Route 2: SVD of the centered data matrix, Xc = U D V'
U, d, Vt = np.linalg.svd(Xc, full_matrices=False)
print(np.round(d, 4))                     # singular values
print(np.round(d**2 / (n - 1), 4))        # the eigenvalues
print(np.round(np.abs(Vt.T) - np.abs(vectors), 10))  # V = eigenvectors (up to sign)

# Scores: U D equals Xc V
scores = U * d
print(np.max(np.abs(np.abs(scores) - np.abs(Xc @ Vt.T))))
print(np.round(scores.var(axis=0, ddof=1), 4))
print(np.round(d**2 / np.sum(d**2), 4))   # proportion of variance explained

# Rank-2 reconstruction keeps the two largest singular values
X2 = U[:, :2] * d[:2] @ Vt[:2, :]
print(round(1 - np.sum((Xc - X2)**2) / np.sum(Xc**2), 4))  # share of variance kept

The figures below come from one seeded run of the R code. Python uses a different random number generator, so its figures differ slightly, but the pattern is the same, and every identity holds exactly in both.

Why software prefers the SVD route

How to report PCA variance explained in APA style (7th edition)

Readers care about the components, not the algorithm, so report the analysis as a PCA and mention the SVD only in the method section if at all. Say whether the variables were only centered (covariance matrix) or also standardized (correlation matrix), because the eigenvalues mean different things. Using the example above:

"A principal component analysis of the four centered variables (N = 200), computed by singular value decomposition, showed that the first component (eigenvalue = 9.54) explained 78.2% of the variance and the second (eigenvalue = 1.40) a further 11.5%. The two retained components together explained 89.6% of the variance."

List the weights or loadings for each retained component in a table, with one row per variable, and add rows for the eigenvalues and percentages of variance. A table note should say whether the values are eigenvector weights or loadings.

Related tools and guides

More answered questions

Working with your own data?

General answers only go so far. Send us your situation and we'll reply by email within two business days.

Ask your question

Written by AskStats with AI assistance. This is general information, not advice for your specific data or study. When the results matter, check your approach with a qualified statistician.