Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

10.4. Principal Components Analysis

Introduction

In Chapter 10.3, we found the best direction for representing data by minimizing orthogonal projection error (equivalently, maximizing projected variance). We now use that result to define principal components and connect them to the SVD.


Principal Components

We’ve discovered that the single best direction to project the data onto is the eigenvector of X~TX~\tilde X^T \tilde X corresponding to the largest eigenvalue of X~TX~\tilde X^T \tilde X. Let’s call this eigenvector v1\vec v_1, as I did in the diagram above. More v\vec v’s are coming.

PV(v)=1nX~v2    v=v1\text{PV}(\vec v) = \frac{1}{n} \lVert \tilde X \vec v \rVert^2 \implies \vec v^* = \vec v_1

The values of the first principal component, PC1\text{PC}_1 (i.e. new feature 1), come from projecting each row of X~\tilde X onto v1\vec v_1:

PC1=[x~1v1x~2v1x~nv1]=X~v1\text{PC}_1 = \begin{bmatrix} \tilde x_1 \cdot \vec v_1 \\ \tilde x_2 \cdot \vec v_1 \\ \vdots \\ \tilde x_n \cdot \vec v_1 \end{bmatrix} = \tilde X \vec v_1

This projection of our data onto the line spanned by v1\vec v_1 is the linear combination of the original features that captures the largest possible amount of variance in the data.

(Whenever you see “principal component”, you should think “new feature”.)

Remember, one of our initial goals was to find multiple principal components that were uncorrelated with each other, meaning their correlation coefficient rr is 0. We’ve found the first principal component, PC1\text{PC}_1, which came from projecting onto the best direction. This is the single best linear combination of the original features we can come up with.

Let’s take a greedy approach. I now want to find the next best principal component, PC2\text{PC}_2, which should be uncorrelated with PC1\text{PC}_1. PC2\text{PC}_2 should capture all of the remaining variance in the data that PC1\text{PC}_1 couldn’t capture. Intuitively, since our dataset is 2-dimensional, together, PC1\text{PC}_1 and PC2\text{PC}_2 should contain the same information as the original two features.

PC2\text{PC}_2 comes from projecting onto the best direction among all directions that are orthogonal to v1\vec v_1. It can be shown that this “second-best direction” is the eigenvector v2\vec v_2 of X~TX~\tilde X^T \tilde X corresponding to the second largest eigenvalue of X~TX~\tilde X^T \tilde X.

PC2=X~v2\text{PC}_2 = \tilde X \vec v_2

In other words, the vector v\vec v that maximizes f(v)=X~v2v2\displaystyle f(\vec v) = \frac{\lVert \tilde X \vec v \rVert^2}{\lVert \vec v \rVert^2} subject to the constraint that v\vec v is orthogonal to v1\vec v_1, is v2\vec v_2. The proof of this is beyond the scope of what we’ll discuss here, as it involves some constrained optimization theory.

Image produced in Jupyter

Why is v2\vec v_2 orthogonal to v1\vec v_1? They are both eigenvectors of X~TX~\tilde X^T \tilde X corresponding to different eigenvalues, so they must be orthogonal, thanks to the spectral theorem (which applies because X~TX~\tilde X^T \tilde X is symmetric). Remember that while any vector on the line spanned by v2\vec v_2 is also an eigenvector of X~TX~\tilde X^T \tilde X corresponding to the second largest eigenvalue, we pick the specific v2\vec v_2 that is a unit vector.

v2\vec v_2 and v1\vec v_1 being orthogonal means that X~v2\tilde X \vec v_2 and X~v1\tilde X \vec v_1 are orthogonal, too. But because the columns of X~\tilde X are mean centered, we can show that this implies that the correlation between PC1\text{PC}_1 and PC2\text{PC}_2 is 0. I’ll save the algebra for now, but see if you can work this out yourself. You’ve done similar proofs in homeworks already.

The SVD Returns

The moment I said that v1\vec v_1 is an eigenvector of X~TX~\tilde X^T \tilde X, something should have been ringing in your head: v1\vec v_1 is a singular vector of X~\tilde X! Recall, if

X~=[u1urur+1un]U[σ100σr0000000000]Σ[v1TvrTvr+1TvdT]VT\tilde X = \underbrace{\begin{bmatrix} {\color{#3d81f6} |} & {\color{#3d81f6} \cdots} & {\color{#3d81f6} |} & {\color{#aaaaaa} |} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} |} \\ {\color{#3d81f6} \vec u_1} & {\color{#3d81f6} \cdots} & {\color{#3d81f6} \vec u_r} & {\color{#aaaaaa} \vec u_{r+1}} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} \vec u_n} \\ {\color{#3d81f6} |} & {\color{#3d81f6} \cdots} & {\color{#3d81f6} |} & {\color{#aaaaaa} |} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} |} \end{bmatrix}}_{U} \underbrace{\begin{bmatrix} {\color{orange} \sigma_1} & {\color{#aaaaaa} } & {\color{#aaaaaa} } & {\color{#aaaaaa} 0} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} 0} \\ {\color{#aaaaaa} } & {\color{orange} \ddots} & {\color{#aaaaaa} } & {\color{#aaaaaa} \vdots} & {\color{#aaaaaa} } & {\color{#aaaaaa} \vdots} \\ {\color{#aaaaaa} } & {\color{#aaaaaa} } & {\color{orange} \sigma_r} & {\color{#aaaaaa} 0} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} 0} \\[6pt] {\color{#aaaaaa} 0} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} 0} & {\color{#aaaaaa} 0} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} 0} \\ {\color{#aaaaaa} \vdots} & {\color{#aaaaaa} } & {\color{#aaaaaa} \vdots} & {\color{#aaaaaa} \vdots} & {\color{#aaaaaa} } & {\color{#aaaaaa} \vdots} \\ {\color{#aaaaaa} 0} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} 0} & {\color{#aaaaaa} 0} & {\color{#aaaaaa} \cdots} & {\color{#aaaaaa} 0} \end{bmatrix}}_{\Sigma} \underbrace{\begin{bmatrix} {\color{#d81a60}\text{---} \:\: \vec v_1^T \:\: \text{---}} \\ {\color{#d81a60} \vdots} \\ {\color{#d81a60}\text{---} \:\: \vec v_r^T \:\: \text{---}} \\ \\ {\color{#aaaaaa} \text{---} \:\: \vec v_{r+1}^T \:\: \text{---}} \\ {\color{#aaaaaa} \vdots} \\ {\color{#aaaaaa} \text{---} \:\: \vec v_d^T \:\: \text{---}} \\ \end{bmatrix}}_{V^T}

is the singular value decomposition of X~\tilde X, then the columns of VV are the eigenvectors of X~TX~\tilde X^T \tilde X, all of which are orthogonal to each other and have unit length.

We arranged the components in the singular value decomposition in decreasing order of singular values of X~\tilde X, which are the square roots of the eigenvalues of X~TX~\tilde X^T \tilde X.

σi=λi,where λi is the i-th largest eigenvalue of X~TX~\sigma_i = \sqrt{\lambda_i}, \quad \text{where } \lambda_i \text{ is the } i\text{-th largest eigenvalue of } \tilde X^T \tilde X

So,

  • the first column of VV is v1\vec v_1, the “best direction” to project the data onto,

  • the second column of VV is v2\vec v_2, the “second-best direction” to project the data onto,

  • the third column of VV is v3\vec v_3, the “third-best direction” to project the data onto,

  • and so on.

Let’s put this in context with a few examples.

Example: From R2\mathbb{R}^2 to R2\mathbb{R}^2

Let’s start with the 2-dimensional dataset of flipper length vs. body mass from the penguins dataset.

Image produced in Jupyter

The first thing you should notice is that while the original data points seem to have some positive correlation, the principal components are uncorrelated! This is a good thing; it’s what we wanted as a design goal. In effect, by converting from the original features to principal components, we’ve rotated the data to remove the correlation between the features.

I have picked two points to highlight, points 5 and 12 in the original data. The coloring in red and purple is meant to show you how original (centered) data points translate to points in the principal component space.

Notice the scale of the data: PC1\text{PC}_1 axis is much longer than the PC2\text{PC}_2 axis, since the first principal component captures much more variance than the second. We will make this notion - of the proportion of variance captured by each principal component - more precise soon.

(You’ll notice that the body mass and flipper length values on the left are much smaller than in the original datasets; in the original dataset, the body mass values were in the thousands, which distorted the scale of the plot and made it hard to see that the two eigenvectors are indeed orthogonal.)

Example: Starting from R3\mathbb{R}^3

The real power of PCA reveals itself when we start with high-dimensional data. Suppose we start with three of the features in the penguins dataset: bill_depth_mm, flipper_length_mm, and body_mass_g - and want to reduce the dimensionality of the data to 1 or 2. Points are colored by their species.

Loading...

Observe that penguins of the same species tend to be clustered together. This, alone, has nothing to do with PCA: we happen to have this information, so I’ve included it in the plot.

If XX is the 333×3333 \times 3 matrix of three features, and X~\tilde X is the mean-centered version, the best directions in which to project the data are the columns of VV in X~=UΣVT\tilde X = U \Sigma V^T.

X_three_features
Loading...
X_three_features.mean()
bill_depth_mm 17.164865 flipper_length_mm 200.966967 body_mass_g 4207.057057 dtype: float64
# This is the mean-centered version of X_three_features!
X_three_features - X_three_features.mean()
Loading...
X_three_features_centered = X_three_features - X_three_features.mean()
u, s, vt = np.linalg.svd(X_three_features_centered)

Now, the rows of VTV^T (vt), which are the columns of VV (vt.T), contain the best directions.

vt
array([[-1.15433983e-03, 1.51946036e-02, 9.99883889e-01], [ 1.02947493e-01, -9.94570148e-01, 1.52327042e-02], [ 9.94686122e-01, 1.02953123e-01, -4.16174416e-04]])

It’s important to remember that these “best directions” are nothing more than linear combinations of the original features. Since the first row of VTV^T is [0.001150.015190.99988]\begin{bmatrix} -0.00115 & 0.01519 & 0.99988 \end{bmatrix}, the first principal component is

PC 1i=0.00115bill depthi+0.01519flipper lengthi+0.99988body massi\text{PC 1}_i = -0.00115 \cdot \text{bill depth}_i + 0.01519 \cdot \text{flipper length}_i + 0.99988 \cdot \text{body mass}_i

while the second is

PC 2i=0.10294bill depthi0.99457flipper lengthi+0.01523body massi\text{PC 2}_i = 0.10294 \cdot \text{bill depth}_i - 0.99457 \cdot \text{flipper length}_i + 0.01523 \cdot \text{body mass}_i

and third is

PC 3i=0.99468bill depthi+0.10295flipper lengthi0.00042body massi\text{PC 3}_i = 0.99468 \cdot \text{bill depth}_i + 0.10295 \cdot \text{flipper length}_i - 0.00042 \cdot \text{body mass}_i

where i=1,2,,333i = 1, 2, \ldots, 333 is the index of the penguin.

To compute all three of these principal components at once, for every penguin, we just need to compute X~V\tilde X V.

pcs = X_three_features_centered @ vt.T
pcs
Loading...

Let’s plot the first two principal components: that is, for each of our 333 penguins, we’ll plot their value of X~v1\tilde X \vec v_1 on the xx-axis and their value of X~v2\tilde X \vec v_2 on the yy-axis.

Image produced in Jupyter

This is the best 2-dimensional projection of our 3-dimensional scatter plot. And here’s the kicker: penguins of the same species STILL tend to be clustered together in the principal component space!

What this tells us is that our technique for taking linear combinations of the original features is good at preserving the important information in the original dataset. We went from writing down 3 numbers per penguin to 2, but it seems that we didn’t lose much information per penguin.

What do I mean by “much important information”? Let’s make this idea more precise.


Explained Variance

The goal of PCA is to find new features - principal components - that capture as much of the variation in the data as possible, while being uncorrelated with each other.

PCA isn’t foolproof: it works better on some datasets than others. If the features we’re working with are already uncorrelated, PCA isn’t useful. And, even for datasets that are well suited for PCA, we need a systematic way to decide how many principal components to use. So far, we’ve often used 1 or 2 principal components for the purposes of visualization, but in general, we need a more systematic approach. What if we start with an n×50n \times 50 matrix and want to decide how many principal components to compute?

Let’s define the total variance of an n×dn \times d matrix XX as the sum of the variances of the columns of XX. If xi(j)x_i^{(j)} is the ii-th value of the jj-th feature, and μj\mu_j is the mean of the jj-th feature, then the total variance of XX is

total variance=j=1d(1ni=1n(xi(j)μj)2)\text{total variance} = \sum_{j = 1}^d \left(\frac{1}{n} \sum_{i = 1}^n (x_i^{(j)} - \mu_j)^2 \right)

Let’s compute this for X_three_features.

X_three_features.var(ddof=0) # The variances of the individual columns of X.
bill_depth_mm 3.866243 flipper_length_mm 195.851762 body_mass_g 646425.423171 dtype: float64
X_three_features.var(ddof=0).sum()
646625.1411755901

So, the total variance of X_three_features is approximately 646625.

This is also equal to the sum of the variances of the columns of X~V\tilde XV, i.e. the sum of the variances of the principal components!

# These three numbers are DIFFERENT than the numbers above,
# but their sum is the same.
(X_three_features_centered @ vt.T).var(ddof=0)
0 646575.552578 1 47.055776 2 2.532822 dtype: float64
(X_three_features_centered @ vt.T).var(ddof=0).sum() # Same sum as before.
646625.1411755901

Why? If we create the same number of principal components as we have original features, we haven’t lost any information - we’ve just written the same data in a different basis. The goal, though, is to pick a number of principal components that is relatively small (smaller than the number of original features), but still captures most of the variance in the data.

Each new principal component - that is, each column of X~V\tilde X V - captures some amount of this total variance. What we’d like to measure is the proportion (that is, fraction, or percentage) of the total variance that each principal component captures. The first principal component captures the most variance, since it corresponds to the direction in which the data varies the most. The second principal component captures the second-most variance, and so on. But how much variance does the first, second, or jj-th principal component capture?

Recall from earlier that the variance of the data projected onto a vector v\vec v is given by

PV(v)=1nX~v2\text{PV}(\vec v) = \frac{1}{n} \lVert \tilde X \vec v \rVert^2

v1\vec v_1 maximizes this quantity, which is why the first principal component is the projection of the data onto v1\vec v_1; v2\vec v_2 maximizes the quantity subject to being orthogonal to v1\vec v_1, and so on.

Then, the variance of PC1\text{PC}_1 is whatever we get back when we plug v1\vec v_1 into the formula above, and in general, the variance of PCj\text{PC}_j is PV(vj)\text{PV}(\vec v_j) (again, where vj\vec v_j is the jj-th column of VV in the SVD of X~\tilde X). Observe that if vj\vec v_j is the jj-th column of VV in X~=UΣVT\tilde X = U \Sigma V^T, then,

PV(vj)=1nX~vj2=1nσjuj2=1nσj2uj2=σj2n\text{PV}(\vec v_j) = \frac{1}{n} \lVert \tilde X \vec v_j \rVert^2 = \frac{1}{n} \lVert \sigma_j \vec u_j \rVert^2 = \frac{1}{n} \sigma_j^2 \lVert \vec u_j \rVert^2 = \frac{\sigma_j^2}{n}

Here, we used the ever important fact that X~vj=σjuj\tilde X \vec v_j = \sigma_j \vec u_j, where σj\sigma_j is the jj-th singular value of X~\tilde X and uj\vec u_j is the jj-th column of UU in the SVD of X~\tilde X.

What this tells us is that the variance of the jj-th principal component is σj2n\frac{\sigma_j^2}{n}. This is a beautiful result - it tells us that the variance of the jj-th principal component is simply the square of the jj-th singular value of X~\tilde X, divided by nn. The σj\sigma_j’s in the SVD represent the amount that the data varies in the direction of vj\vec v_j! We don’t need any other fancy information to compute the variance of the principal components; we don’t need to know the individual principal component values, or have access to a variance method in code.

u, s, vt = np.linalg.svd(X_three_features_centered)
s
array([14673.43378383, 125.1781673 , 29.04185933])
np.set_printoptions(precision=8)
n = X_three_features_centered.shape[0]
variances_of_pcs = s ** 2 / n
np.set_printoptions(suppress=True)
variances_of_pcs
array([646575.55257751, 47.05577648, 2.5328216 ])

Above, we see the exact same values as if we computed the variances of the principal components directly from the data.

(X_three_features_centered @ vt.T).var(ddof=0)
0 646575.552578 1 47.055776 2 2.532822 dtype: float64

Since the total variance of XX is the sum of the variances of its principal components, the total variance of XX is then the sum of σj2n\frac{\sigma_j^2}{n} over all j=1,2,,rj = 1, 2, \ldots, r, where r=rank(X)r = \text{rank}(X). (Remember that if k>rk > r, then σk=0\sigma_k = 0.)

total variance=j=1rσj2n\text{total variance} = \sum_{j = 1}^r \frac{\sigma_j^2}{n}

So, if we single out just one principal component, how much of the overall variation in XX does it capture? The answer is given by the proportion of variance explained by the jj-th principal component:

proportion of variance explained by PC j=σj2k=1rσk2\text{proportion of variance explained by PC } j = \frac{\sigma_j^2}{\sum_{k=1}^r \sigma_k^2}

This is a number between 0 and 1, which we can interpret as a percentage.

s # The singular values of X_three_features.
array([14673.43378383, 125.1781673 , 29.04185933])
(s ** 2) / (s ** 2).sum() # The proportions of variance explained by each principal component.
array([0.99992331, 0.00007277, 0.00000392])

The above tells us that in X_three_features, the first principal component captures 99.99% of the variance in the data! There’s very little information lost in projecting this 3-dimensional dataset into 1-dimensional space.

Often, the proportions above are visualized in a scree plot, as you’ll see in Homework 11. Scree plots allow us to visually decide the number of principal components to keep, based on where it seems like we’ve captured most of the variation in the data. We’ll work on a related example in live lecture.


The PCA Recipe

Let’s briefly summarize what I’ll call the “PCA recipe”, which describes how to find the principal components (new features) of a dataset.

  1. Starting with an n×dn \times d matrix XX of nn data points in dd dimensions, mean-center the data by subtracting the mean of each column from itself. The new matrix is X~\tilde X.

  2. Compute the singular value decomposition of X~\tilde X: X~=UΣVT\tilde X = U \Sigma V^T. The columns of VV (rows of VTV^T) describe the directions of maximal variance in the data.

  3. Principal component jj comes from multiplying X~\tilde X by the jj-th column of VV.

    PCj=X~vj=σjuj\text{PC}_j = \tilde X \vec v_j = \sigma_j \vec u_j

    The resulting principal components - which are the columns of X~V\tilde X V - are uncorrelated with one another.

  4. The variance of PCj\text{PC}_j is σj2n\frac{\sigma_j^2}{n}. The proportion of variance explained by PCj\text{PC}_j is

    proportion of variance explained by PC j=σj2k=1rσk2\text{proportion of variance explained by PC } j = \frac{\sigma_j^2}{\sum_{k=1}^r \sigma_k^2}