Skip to content
DeepForgeby svx
All articles
Linear Algebra8 min read · 1 interactive demo

PCA & SVD in Practice

The eigenvector article ends at Av = λv. This one starts there: project the cloud, keep the top axes, rebuild — and account for every bit of what you threw away.

The eigenvector article answered a theoretical question: which directions does a matrix leave alone? Practice asks a different one: given a cloud of data points, which directions carry the signal?

The recipe starts by centering: subtract the average point so the cloud sits around the origin. Then build the covariance matrix C = (1/n)·XᵀX. Here X is the table of centered points with one row per point, n is the number of points, and the small T means rows and columns are swapped. The covariance matrix records how each feature varies with every other. Its eigenvectors are the principal components, and its eigenvalues are the variance — the amount of spread — along each one.

The first principal component is the single direction that captures the most spread when the points are projected onto it. The second is the best direction at a right angle to the first, and so on. The cloud does not have to be two-dimensional: the covariance table grows with the number of features, but the ordering story stays the same.

The principal components are also the right singular vectors of the centered data matrix. The singular value decomposition writes any matrix as X = UΣVᵀ. V holds the principal directions. Σ (capital sigma) is a diagonal table of numbers called singular values — the square roots of the eigenvalues. U holds each point's coordinates in the new basis. SVD works on rectangular tables, not just square ones, which is why it is the version that ships in software.

The rank of a matrix is the number of genuinely independent directions it contains. For any rank r — keeping the top r directions and dropping the rest — X_r = U_rΣ_rV_rᵀ is the best possible rank-r approximation of the data, and the squared error equals exactly the sum of the squared singular values you dropped, Σ_{i>r} σ_i². That is the Eckart–Young theorem. Truncating an SVD is not a guess; it is provably optimal.

Preprocessing changes the answer, so it is part of the method. Centering moves the axes so they explain spread around the average point instead of around the origin. Standardizing makes each feature's spread equal to one before the analysis, which turns the covariance table into a correlation table. Then a feature measured in centimeters cannot dominate one measured in kilometers.

That is usually what you want when the units differ, and usually not when they do not. PCA on standardized data is a genuinely different decomposition: the axes swing and the eigenvalues change. The demo lets you watch it happen.

Project, then rebuild from the top axesX ≈ X₂ = U₂Σ₂V₂ᴸ

Drag the tip of u (or focus the canvas and use the arrow keys). The dashed blue lines are the true principal axes.

var(u)
2.733
PC₁ variance
2.871
PC₁ share
95.0%
residual
0.288
u angle
17.7°

Projecting onto u = (0.95, 0.30): variance 2.733, residual 0.288. The best direction, PC₁, reaches 2.871 (95.0% of total). Drag u onto the dashed PC₁ line to make the residual as small as possible.

Start in the project tab and drag the tip of the direction arrow u. The dashed blue lines are the true principal axes for the current preprocessing choice, and the residual readout is the variance your chosen direction leaves on the table.

Drag u onto the first principal axis and the residual hits its lowest possible value, which equals the second eigenvalue. Every other direction explains less. That is what makes PCA a maximization problem rather than a change of basis you pick by eye.

Switch to reconstruct and keep one component. Every point collapses onto the first principal axis, and the warm segments show exactly what each point lost. Keep two and the rebuild is exact, because the space itself is two-dimensional. Toggle centering and standardization and watch the axes — and the reconstruction — change under the same cloud.

Covariance ellipse with principal axes, a scree plot, and the Eckart-Young truncation errorThree panels. On the left, a point cloud inside its covariance ellipse, with the two principal axes drawn from the center and labelled lambda one equal to 4.84 and lambda two equal to 0.64. In the middle, a scree plot of five eigenvalues as bars, the first bar dominant, with a dashed cut after the second component. On the right, the Eckart-Young identity shows the squared Frobenius error of a rank r truncation equals the sum of the dropped eigenvalues squared; cutting at r equals two of five components keeps 92.6 percent of the variance.Covariance is a shape · two axes hold the variancethe ellipse xᴸC⁻¹x = 1 · semi-axes √λᵢ · ordered by eigenvalueλ₁ = 4.84λ₂ = 0.64tilt comes from the covariance, not the coordinatesscree: variance per component12345truncate at r = 2steep drop, then a noise tailEckart–Young‖X − Xᵣ‖² = Σᵢ⁂ᵣ σᵢ²rank-r truncation has no bettercompetitor — drop the tail012345residual energy fractionr = 2 keeps 92.6%
The covariance matrix is a shape: the ellipse is one standard deviation out, its axes are the eigenvectors, and the scree plot says how much variance each direction holds. Dropping the tail is the Eckart–Young optimum, not a guess.

At scale nobody forms the full covariance table. Randomized SVD computes the top k components by multiplying the data by a small random sketch, cleaning up the result so its columns meet at right angles, and running a tiny decomposition on what is left. A few passes recover the dominant directions to high accuracy, and the cost grows with k instead of cubing with the dimension.

What people do with the components falls into four buckets. Denoise: drop the tail, where the noise lives. Compress: store only the top r coordinates. Whiten: rescale the components to equal variance so later models see uncorrelated features. Diagnose: read the scree plot, which shows how much variance each component holds, to see whether the data is closer to rank 3 or rank 300.

Low-rank thinking is everywhere in 2026. Matryoshka embeddings are trained so that cutting a 1024-dimension vector down to its first 256 coordinates keeps most of the retrieval quality — the PCA truncation argument baked into the loss. LoRA fine-tunes a model by learning a low-rank update ΔW = BA instead of the full matrix.

Neither trick runs an explicit SVD at inference time, but both are understandable only if you know what truncation costs. The pseudoinverse — the closest thing to an inverse for a table that cannot be inverted — is the same story from the other side: X⁺ = VΣ⁺Uᵀ, where tiny singular values are replaced rather than inverted — ridge regression in disguise.

The problems below center the data, build a rank-1 approximation, reconstruct at rank r, run power iteration for singular vectors, and compute the pseudoinverse and 2x2 singular values by hand.

Practice this

Open a problem and implement the idea from scratch. Progress saves to this browser.