8 主成分分析

8.1 主成分分析的基本思想

主成分分析(Principal Component Analysis, PCA)由Hotelling于1933年首先提出。 目的是把多个变量压缩为少数几个综合指标(称为主成分), 使得综合指标能够包含原来的多个变量的主要的信息。

如何度量变量中包含的信息? 如果变量取常数,就没有信息。 变量变化范围越大,越不容易预知其取值, 得到变量的观测值时获得的信息量就大。 所以,用方差衡量综合指标包含的信息多少。 寻找原始变量的线性组合作为综合指标, 使得综合指标的方差最大; 各个综合指标之间不相关。

主成分分析是变量降维的最主要方法之一。 实际中也经常对多个变量作主成分分析, 获得更有意义的指标。 比如, 许多学生的若干门课程的考试分数, 如果直接计算平均分或者总分, 会收到各门课程分数高低、区分程度的影响, 用主成分分析方法计算线性组合作为第一主成分, 是比较合理的多门课程的综合指标。 在经济研究中也涉及多个指数, 如物价、工资、居住等, 可以计算第一主成分作为综合指标。

有时第二、第三主成分更有用。 比如, 研究动物特征时, 测量了动物的多个长度指标, 这时第一主成分仅反映动物的大小, 而第二、第三主成分则可以用来区分动物的不同样貌。

多维数据很难用图形表示, 用主成分分析降维后, 可以用通常的散点图、三维散点图等方法作图显示。

在回归分析中, 如果自变量高度相关, 会引起多重共线性问题, 使得计算不稳定, 参数估计的标准误差变大, 用变量选择方法则会损失一定的信息, 可以计算自变量的主成分, 用前几个主成分作为回归自变量进行回归建模。

8.2 总体主成分

8.2.1 二维情形

设总体为一个\(p\)维随机向量\(\boldsymbol X = (X_1, X_2, \dots, X_p)^T\)。 以二维正态分布为例。设\(\boldsymbol X \sim \text{N}(\boldsymbol\mu, \Sigma)\), 其中 \[\begin{aligned} \boldsymbol\mu = \left( \begin{array}{c} \mu_1 \\ \mu_2 \end{array} \right),\; \Sigma = \left(\begin{array}{cc} \sigma_1^2 & \rho \sigma_1 \sigma_2 \\ \rho \sigma_1 \sigma_2 & \sigma_2^2 \end{array}\right) . \end{aligned}\] 设此总体有\(n\)个观测值\((x_{i1}, x_{i2}), i=1,2,\dots,n\),

模拟一批二维正态分布的样本的散点图:

library(mvtnorm)
set.seed(101)

n <- 10000
mu <- c(0, 0)
rho <- 0.5
sigma1 <- 1.6
sigma2 <- 1
S <- rbind(c(sigma1^2, rho*sigma1*sigma2),
           c(rho*sigma1*sigma2, sigma2^2))
d <- rmvnorm(n, mu, S)
colnames(d) <- c("x", "y")
##plot(d, xlab=expression(x[1]), ylab=expression(x[2]),
##     xlim=c(-5,5), ylim=c(-5,5))
library(ggplot2)
ggplot(data=as.data.frame(d), mapping=aes(x=x, y=y)) +
  geom_point(size=0.3, alpha=0.2)

如果取散点变化最大的方向为新坐标\(y_1\)轴, 其正交方向为\(y_2\)轴, 这些点的新\(y_1\)坐标可以更好地概括观测点的信息。

设坐标变换原点不变, 仅进行逆时针\(\theta\)弧度的旋转变换,则变换后的随机向量为 \[\begin{aligned} \boldsymbol Y = \left( \begin{array}{c} Y_1 \\ Y_2 \end{array} \right) = \left( \begin{array}{cc} \cos\theta & \sin\theta \\ -\sin\theta & \cos\theta \end{array} \right) \left( \begin{array}{c} X_1 \\ X_2 \end{array} \right) \end{aligned}\]

记变换矩阵为\(P^T\),这是一个正交阵(\(P^T P = I\))。 \(y_1\)\(x_1, x_2\)的线性组合,线性组合系数是单位向量 (这里指欧式长度等于1的向量)。 适当选取\(\theta\)可以使得\(Y_1\)的方差最大, 而且\(Y_1\)\(Y_2\)不相关。

考虑三种极端情况。

  • 如果\(\rho=0\), \(\sigma_1 = \sigma_2\), 这时散点呈现为正圆,不需要旋转变换 (任何旋转方向都不会使得方差变大)。 取任何一个线性组合,都只能保留原来50%的信息。
  • 如果\(\rho\)接近于\(\pm 1\), 这时图形呈现出一个很扁的椭圆, 变换后的\(Y_1\)可以包含主要的信息。
  • 如果\(\sigma_1\)远大于\(\sigma_2\), 这时仅保留\(X_1\)就保留了\((X_1, X_2)\)的大部分信息。

\(\boldsymbol Y = P^T \boldsymbol X\)。 考虑求\(P\)使得\(\text{Var}(Y_1)\)最大的问题。

\(\Sigma\)有如下的特征值分解: \[\begin{aligned} \Sigma = P \Lambda P^T, \end{aligned}\] 其中\(\lambda_1 \geq \lambda_2 > 0\)是正定阵\(\Sigma\)的两个特征值, \(\Lambda=\text{diag}(\lambda_1, \lambda_2)\), \(P\)的两列分别为这两个特征值对应的特征向量。

这时, \[\begin{aligned} \text{Var}(\boldsymbol Y) =& \text{Var}(P^T \boldsymbol X) = P^T \Sigma P = \Lambda, \end{aligned}\]\(Y_1 = X_1 \cos\theta + X_2 \sin\theta\) 是所有单位向量作为线性组合系数的线性变换中方差最大的。 \(Y_1\)\(Y_2\)不相关。

\(\boldsymbol\alpha = (\alpha_1, \alpha_2)^T\)是线性组合系数, 满足\(\| \boldsymbol\alpha \| = \sqrt{\alpha_1^2 + \alpha_2^2} = 1\)。 令\(Z = \boldsymbol\alpha^T \boldsymbol X\), 则 \[\begin{aligned} \text{Var}(Z) =& \boldsymbol\alpha^T \Sigma \boldsymbol\alpha = \boldsymbol\alpha^T P \Lambda P^T \boldsymbol\alpha = (P^T \boldsymbol\alpha)^T \Lambda (P^T \boldsymbol\alpha), \end{aligned}\]

\(\boldsymbol\beta=P^T \boldsymbol\alpha\), 则\(\| \boldsymbol\beta \| = 1\), 有 \[\begin{aligned} \text{Var}(Z) =& \boldsymbol\beta^T \Lambda \boldsymbol\beta = \lambda_1 \beta_1^2 + \lambda_2 \beta_2^2 \\ \leq& \lambda_1 \beta_1^2 + \lambda_1 \beta_2^2 = \lambda_1 (\beta_1^2 + \beta_2^2) = \lambda_1, \end{aligned}\] 所以\(\boldsymbol\beta=(1,0)^T\)使得\(\text{Var}(Z) = \lambda_1\)达到最大。 这时\(\boldsymbol\alpha = P\boldsymbol\beta\)\(P\)的第一列, 即\(\Sigma\)的特征值\(\lambda_1\)对应的单位特征向量。

注意: \(-\boldsymbol\alpha\)也满足要求。

8.2.2 多维情形

定理6.1  设总体\(\boldsymbol X = (X_1, X_2, \dots, X_p)^T\)的协方差阵为\(\Sigma\), \(\lambda_1 \geq \lambda_2 \geq \dots \geq \lambda_p \geq 0\)\(\Sigma\)\(p\)个特征值, \(\Sigma\)有如下的特征值分解 \[\begin{aligned} \Sigma = P \Lambda P^T, \end{aligned}\] 其中\(\Lambda = \text{diag}(\lambda_1, \lambda_2, \dots,\lambda_p)\), \(P\)为正交阵,\(P\)的第\(j\)列是\(\lambda_j\)对应的特征向量,记为\(\boldsymbol p_j\)。 则\(X\)的第\(j\)个主成分为 \[\begin{aligned} Y_j = \boldsymbol p_j^T \boldsymbol X, \end{aligned}\]\(Y = (Y_1, Y_2, \dots, Y_p)^T\), 则\(\boldsymbol Y = P^T \boldsymbol X\), \(\text{Var}(\boldsymbol Y) = \Lambda\)

在线性组合系数必须是单位向量的约束下, 这样得到的\(\text{Var}(Y_1)\)最大,\(Y_j\)在与\(Y_1, \dots, Y_{j-1}\)不相关的前提下达到最大方差。

\(\boldsymbol Y = P^T \boldsymbol X\)\[ \boldsymbol X = P \boldsymbol Y = \begin{pmatrix} \boldsymbol p_1, \boldsymbol p_2, \dots, \boldsymbol p_p \end{pmatrix} \begin{pmatrix} Y_1 \\ Y_2 \\ \vdots \\ Y_p \end{pmatrix} = Y_1 \boldsymbol p_1 + Y_2 \boldsymbol p_2 + \dots + Y_p \boldsymbol p_p , \] 原始变量可以用主成分的线性组合表示, \(\boldsymbol p_1\)是主成分\(Y_1\)对原始变量\(\boldsymbol X\)的贡献, \(\boldsymbol p_2\)是主成分\(Y_2\)对原始变量\(\boldsymbol X\)的贡献, …………, 称矩阵\(P\)为主成分分析的载荷矩阵。参见节9.1.1的解释。

8.2.3 主成分的性质

\(\boldsymbol Y = (Y_1, Y_2, \dots, Y_p)^T\)\(\boldsymbol X\)的主成分向量。 \(\text{Var}(\boldsymbol X) = \Sigma = P \Lambda P^T\), \(\boldsymbol Y = P^T \boldsymbol X\)

性质1 主成分向量\(\boldsymbol Y\)的协方差阵是对角阵 \(\Lambda = \text{diag}(\lambda_1, \lambda_2, \dots, \lambda_p)\)

事实上, \[\begin{aligned} \text{Var}(\boldsymbol Y) =& \text{Var}(P^T \boldsymbol X) = P^T \Sigma P = P^T P \Lambda P^T P \\ =& \Lambda \end{aligned}\]

性质2 主成分\(Y_1, Y_2, \dots, Y_p\)的方差之和等于原始变量 \(X_1, X_2, \dots, X_p\)的方差之和。

事实上, \[\begin{aligned} & \sum_{j=1}^p \text{Var}(Y_j) = \text{trace}(\text{Var}(\boldsymbol Y)) = \text{trace}(\text{Var}(P^T \boldsymbol X)) \\ =& \text{trace}(P^T \Sigma P) = \text{trace}(P P^T \Sigma) = \text{trace}(\Sigma) \\ =& \sum_{j=1}^p \text{Var}(X_j). \end{aligned}\]

性质3 原始变量\(X_k\)与主成分\(Y_j\)的相关系数为 \[\begin{aligned} \rho_{kj} = \rho(X_k, Y_j) = \frac{\sqrt{\lambda_j}}{\sqrt{\sigma_{kk}}} p_{kj}, \end{aligned}\] 其中\(\boldsymbol p_j = (p_{1j}, p_{2j}, \dots, p_{pj})^T\)\(P\)的第\(j\)列, \(p_{kj}\)\(P\)的第\((k,j)\)元素。

证明: 记\(\boldsymbol e_k\)为单位阵\(I_p\)的第\(k\)列, 即仅第\(k\)元素为1,其它元素都为0的\(p\)维向量。 则\(X_k = \boldsymbol e_k^T \boldsymbol X\), \(Y_j = \boldsymbol p_j^T \boldsymbol X\), 于是 \[\begin{aligned} \text{Cov}(X_k, Y_j) =& \text{Cov}(\boldsymbol e_k^T \boldsymbol X, \boldsymbol p_j^T \boldsymbol X) = \boldsymbol e_k^T \Sigma \boldsymbol p_j \\ =& \boldsymbol e_k^T (\Sigma \boldsymbol p_j) = \boldsymbol e_k^T (\lambda_j \boldsymbol p_j) \quad\text{($\boldsymbol p_j$是$\lambda_j$对应的特征向量)} \\ =& \lambda_j (\boldsymbol e_k^T \boldsymbol p_j) = \lambda_j p_{kj}, \\ \rho_{kj} =& \rho(X_k, Y_j) = \frac{\text{Cov}(X_k, Y_j)}{\sqrt{\text{Var}(X_k) \cdot \text{Var}(Y_j)}} \\ =& \frac{\lambda_j p_{kj}}{\sqrt{\sigma_{kk} \lambda_j}} = \frac{\sqrt{\lambda_j}}{\sqrt{\sigma_{kk}}} p_{kj} . \end{aligned}\]

性质4 原始变量\(X_k\)与主成分\(Y_j\)的相关系数满足 \[\begin{aligned} \sum_{j=1}^p \rho_{kj}^2 = 1, \ j=1,2,\dots,p . \end{aligned}\]

证明: 注意到 \[\begin{aligned} \Sigma = P \Lambda P^T = \sum_{j=1}^p \lambda_j \boldsymbol p_j \boldsymbol p_j^T, \end{aligned}\] 所以 \[\begin{aligned} \sigma_{kk} = \sum_{j=1}^p \lambda_j p_{kj}^2, \end{aligned}\] 于是 \[\begin{aligned} \sum_{j=1}^p \rho_{kj}^2 = \frac{1}{\sigma_{kk}} \sum_{j=1}^p \lambda_j p_{kj}^2 = 1. \end{aligned}\] 这样,\(\rho_{kj}^2\)可以看成是在原始变量\(X_k\)的方差中, 第\(j\)主成分\(Y_j\)能够解释的方差比例。

8.3 样本主成分

实际问题中, 总体的分布通常是未知的, 需要从样本中估计。 设总体\(\boldsymbol X\)\(n\)个独立观测, 保存为矩阵\(M = (x_{ij})_{n \times p}\), 其中第\(i\)行为第\(i\)个观测, 第\(j\)列为第\(j\)分量的所有\(n\)个观测组成的向量。 这也是统计中最常见的数据表示,常称为数据集, 在R中通常用数据框(data frame)存储。

8.3.1 R型主成分与Q型主成分

因为现在有总体的\(n\)个独立观测, 所以从\(M\)估计总体的主成分, 将得到\(n\)个第一主成分值, \(n\)个第二主成分值,……。 如果仅取前\(k\)个主成分, 则数据集\(M\)被压缩成\(n \times k\)的得分数据集, 每列为一个主成分得分(总体主成分的估计值)。 原来每行是一个\(p\)维向量,压缩为每行\(k\)个得分(\(k<p\)), 这样的主成分分析称为R型主成分分析。

对矩阵\(M\),还可以把每列的\(n\)维向量, 用类似方法压缩为\(k\)个得分, 这样的主成分分析称为Q型主成分分析。 这里仅考虑R型主成分分析。

8.3.2 标准化

主成分的组成,不仅与\(\boldsymbol X\)的相关结构有关系, 与每个分量的方差也有关系, 方差大的分量在第一主成分中贡献更大。

在变量的各分量可比的情况下(比如,都是考试分数且满分相同), 这不成为问题,而且是合理的。 但是,如果分量之间不可比,这时量纲会对主成分结果造成很大影响。 比如,某分量的量纲从千克变成了克,其方差就变大到了原来的一百万倍, 在第一主成分中的贡献就比原来大得多了。

所以,主成分分析经常对标准化的分量 \[\begin{aligned} Y_j = \frac{X_j - E(X_j)}{\sqrt{\text{Var}(X_J)}}, \ j=1,2,\dots,p, \quad \boldsymbol Y = (Y_1, Y_2, \dots, Y_p)^T \end{aligned}\] 进行。 这时\(\text{Var}(\boldsymbol Y) = R\), 其中\(R\)\((j,j)\)元素等于1, \(R\)\((i,j)\)元素为\(\rho(X_i, X_j)\), 是\(\boldsymbol X\)的相关阵。 对标准化的随机向量进行主成分分析, 相当于用相关阵\(R\)代替协方差阵\(\Sigma\)作特征值分解, 以\(R\)的各特征向量为主成分的线性组合系数。 对\(S\)作特征值分解与对\(R\)作特征值分解, 结果一般是不同的。 选择基于\(R\)作主成分分析, 同时也默认了所有\(p\)个变量具有相同的重要性。

为估计协方差阵\(\Sigma\), 令 \[\begin{aligned} \bar x_{\cdot j} =& \frac{1}{n} \sum_{i=1}^n x_{ij}, \ j=1,2,\dots, p, \\ s_{jk} =& \frac{1}{n} \sum_{i=1}^n (x_{ij} - \bar x_{\cdot j})(x_{ik} - \bar x_{\cdot k}), \ j, k=1,2,\dots,p, \end{aligned}\] \(S=(s_{jk})_{p \times p}\)\(\Sigma\)的估计。 注意方差和协方差估计用了\(1/n\)而不是\(1/(n-1)\)

为估计相关阵\(R\),令 \[\begin{aligned} r_{jk} =& \frac{s_{jk}}{\sqrt{s_{jj} \cdot s_{kk}}}, \ j, k=1,2,\dots,p, \end{aligned}\] \(\hat R = (r_{jk})_{p \times p}\), 则\(\hat R\)是相关阵\(R\)相合估计。 在不引起混淆时可简记\(\hat R\)\(R\)

8.3.3 样本主成分推导

从观测数据\(M\)求样本主成分, 其理论推导与总体主成分推导类似。 以使用协方差阵\(S\)为例。 令\(M_C = H M\)\[ S = \frac{1}{n} M_C^T M_C = \frac{1}{n} M^T H M . \] 考虑\(M_C\)各行都用线性组合系数\(\boldsymbol g_1\)作线性组合, 得到第一主成分得分向量\(\boldsymbol z_1 = M_C \boldsymbol g_1\), 由§4.1.5\(\boldsymbol z_1\)的样本方差(用除以\(n\)的公式)为 \[ S_{z_1}^2 = \boldsymbol g_1^T S \boldsymbol g_1, \]\(\| \boldsymbol g_1 \| = 1\)约束下令\(S_{z_1}^2\)达到最大, 用与8.2同样的推理可知应取\(S\)的最大特征值\(\lambda_1\)对应的单位特征向量\(\boldsymbol g_1\)作为线性组合系数。 这时,\(S_{z_1}^2 = \lambda_1\)

第二、第三等主成分得分的线性组合系数类似, 取为\(S\)的从大到小排列的特征值对应的单位特征向量, 第\(j\)样本主成分得分的样本方差为\(S\)的从大到小第\(j\)特征值\(\lambda_j\)

可以证明, 第一主成分得分对应的系数\(\boldsymbol g_1\), 等价于在\(\mathbb R^p\)中求一条经过原点的直线, 使得\(M_C\)的每行对应的\(n\)个点, 到此直线的垂直距离的平方和最小。 比如, 当\(p=2\)时, 设第一样本主成分的线性组合系数\(\boldsymbol g_1 = (a_1, b_1)^T\), 则它决定一条直线\(a_1 x + b_1 y = 0\)\(M_C\)\(n\)行对应的\(n\)个观测点到此直线的垂直距离平方和最小。

8.3.4 样本主成分计算

不论使用协方差阵还是相关阵, 样本数据\(M\)的每列都要中心化(减去列平均值\(\bar x_{\cdot j}\), \(j=1,2,\dots,p\))。

\(H = I_n - \frac{1}{n} \boldsymbol 1_n \boldsymbol 1_n^T\), 则中心化的数据集为\(M_C = H M\)。 样本协方差阵为\(S = \frac{1}{n} M_C^T M_C = \frac{1}{n} M^T H M\)

如果使用相关阵, \(M\)的每列要标准化, 标准化时,每列减去列平均值\(\bar x_{\cdot j}\)后除以标准差估计\(\sqrt{s_{jj}}\) (\(j=1,2,\dots,p\))。

\(D = \text{diag}(s_{11}, s_{22}, \dots, s_{pp})\), 则标准化数据集为\(M_S = H M D^{-1/2}\), 相关系数阵估计为\(\hat R = D^{-1/2} S D^{-1/2}\)

设样本协方差阵\(S\)(或样本相关阵\(R\))有特征值分解\(S = G L G^T\), 其中\(L = \text{diag}(l_1, l_2, \dots,l_p)\), \(G\)为正交阵。

设数据阵\(M\)已进行中心化或标准化, 记为\(\tilde M\)(\(\tilde M = M_C\)\(M_S\)), 记\(\tilde M\)的第\(i\)行记为\((\boldsymbol x^{(i)})^T\), 则\((\boldsymbol x^{(i)})^T G\)为一个\(p\)维行向量, 第一个元素是第\(i\)个观测的第一样本主成分, 第二个元素是第\(i\)个观测的第二样本主成分,…………。 令\(Z = \tilde M G\), 则\(Z\)是一个\(n \times p\)矩阵, \(Z\)的第\(i\)行的\(p\)个元素是观测数据的第\(i\)个观测的\(p\)个样本主成分; \(Z\)的第\(j\)列是所有观测的第\(j\)主成分,称为第\(j\)主成分得分。

如果只需要前\(k\)个主成分, 则只需要计算 \[\begin{align*} Z_{n \times k} = \tilde M G_{p \times k} = (\tilde M \boldsymbol g_1, \tilde M \boldsymbol g_2, \dots, \tilde M \boldsymbol g_k), \end{align*}\] 其中\(G_{p \times k}\)表示\(G\)的前\(k\)列组成的子矩阵, \(\boldsymbol g_j\)表示\(G\)的第\(j\)列。

8.3.5 用奇异值分解计算样本主成分

设数据阵\(M\)已进行中心化或标准化, 记为\(\tilde M\)(\(\tilde M = M_C\)\(M_S\)), 下面以中心化为例说明。 对\(\tilde M\)作如下的奇异值分解: \[ \tilde M = U L^{\frac{1}{2}} G^T \] 其中\(L\)\(\tilde M^T \tilde M\)的特征值组成的对角阵, \(L^{\frac{1}{2}}\)为各个奇异值, \(U\)\(G\)\(n \times r\)\(p \times r\)列单位正交阵, 对于样本数据, 可取\(r = \min(n, p)\)。 这时, \[ \frac{1}{n-1} \tilde M^T \tilde M = G (\frac{1}{n-1} L) G^T \] 构成协方差阵估计或相关阵估计的特征值分解。

\[ Z = \tilde M G \]\(P\)\(n \times p\)矩阵, \(G\)是方差阵(或相关阵)的特征值分解中的各个特征向量, 所以是主成分得分矩阵。

各个主成分的方差阵估计为\(\frac{1}{n} L\)

8.3.6 样本主成分性质

\(Z\)的每列的样本平均值都等于零;

\(j\)个样本主成分的样本方差满足 \[\begin{align*} \frac{1}{n} \sum_{i=1}^n z_{ij}^2 = l_j, \ j=1,2,\dots, p . \end{align*}\] 证明见Zelterman(2015) §11.6 (11.30-11.31)。

由于\(S = G L G^T\)\(\text{trace}(S) = \text{trace}(L)\), 所以\(M\)\(p\)个变量的总方差等于\(Z\)\(p\)个主成分得分的总方差。

8.3.7 主成分个数的确定

主成分分析的主要目的是降维, 用较少的综合指标代替原来的多个变量, 所以一般只保留少数的几个主成分。

因为\(p\)个原始变量和\(p\)个主成分得分的总方差不变, 但是主成分的方差是由大到小的, 可以用\(\omega_k = \frac{\lambda_k}{\sum_{j=1}^p l_j}\) 度量第\(k\)个主成分的贡献, 称为第\(k\)个主成分的方差贡献率。

\(\sum_{i=1}^k w_i = \frac{\sum_{j=1}^k l_j}{\sum_{j=1}^p l_j}\) 表示前\(k\)个主成分能解释原始变量的信息的比例, 称为前\(k\)个主成分的累计贡献率。 经常取\(k\)使得累计贡献率达到\(80\%\)以上就不再增加主成分。 如果少量的主成分不能达到较好的累计贡献率, 也可以降低对累计贡献率的要求或使用更多个主成分。 常用的累计贡献率界限为\(70%\)\(90%\)

还可以取超过平均方差(\(p\)个原始变量的方差的平均值)的前几个主成分。 在使用相关系数阵分解计算主成分时, 平均方差是1, 可以取方差超过1的主成分, 还有人建议取方差超过\(0.7\)的主成分。

\[\begin{aligned} \hat{\boldsymbol X} = \sum_{j=1}^k Y_j \boldsymbol p_j, \end{aligned}\]\[\begin{aligned} & E \| \boldsymbol X - \hat{\boldsymbol X} \|^2 \\ =& E \| \sum_{j=k+1}^p Y_j \boldsymbol p_j \|^2 \\ =& \sum_{j=k+1}^p \lambda_j, \end{aligned}\] 可见当累计贡献率较高时, 可以用主成分较好地近似原始变量。

保留的主成分个数的变化不影响计算的主成分得分。 对因子分析, 选择不同的因子个数, 计算得到的因子得分也会有差别。

8.4 样本主成分的几何解释

前面对主成分分析的解释, 是从用原始变量的单位长度系数的线性组合最大化方差来给出的, 对样本主成分就简单地使用了样本方差阵来代替理论方差阵。 下面我们说明, 将\(n \times p\)的观测数据集\(M\)的每一行看成\(\mathbb R^p\)中的点, 样本主成分实际是将这\(p\)个点用\(\mathbb R^p\)中的子空间(或仿射集)进行最优逼近产生的降维结果。

8.4.1 已经中心化的数据

先考虑将\(M_C\)(中心化的数据)的\(n\)个点用\(\mathbb R^p\)的一维子空间中的点来逼近: \[ \mathcal S = \{ \lambda \boldsymbol u: \lambda \in \mathbb R \} . \] 其中\(\boldsymbol u \in \mathbb R^p\), \(\| \boldsymbol u \| = 1\)。 求\(\boldsymbol u\)使得这\(n\)个点到\(\mathcal S\)的总距离平方最小: \[ \min_{\boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1} \sum_i \| \boldsymbol x^{(i)} - \lambda_i \boldsymbol u \|^2 . \] 其中\(\boldsymbol x^{(i)}\)表示\(M_C\)的第\(i\)行的转置, \(\lambda_i \boldsymbol u\)\(\boldsymbol x^{(i)}\)\(\mathcal S\)中的最佳逼近, 即 \[ \lambda_i = \text{argmin}_{\lambda \in \mathbb R} \| \boldsymbol x^{(i)} - \lambda \boldsymbol u \|^2 . \] 这是一个投影问题, 因为\(\boldsymbol u\)是单位长度向量, 所以 \[ \lambda_i = \langle \boldsymbol x^{(i)}, \boldsymbol u \rangle = [\boldsymbol x^{(i)}]^T \boldsymbol u^ . \] 由投影的正交性有 \[ \| \boldsymbol x^{(i)} - \lambda_i \boldsymbol u \|^2 = \| \boldsymbol x^{(i)} \|^2 - \lambda_i^2 = \| \boldsymbol x^{(i)} \|^2 - \langle \boldsymbol x^{(i)}, \boldsymbol u \rangle^2 . \] 求最优的\(\boldsymbol u\), 问题转化为 \[ \min_{\boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1} \left\{ \sum_i \| \boldsymbol x^{(i)} \|^2 - \sum_i \langle \boldsymbol x^{(i)}, \boldsymbol u \rangle^2 \right\} . \] 目标函数的第一项不依赖于\(\boldsymbol u\), 所以问题为 \[ \max_{\boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1} \sum_i \langle \boldsymbol x^{(i)}, \boldsymbol u \rangle^2 = \max_{\boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1} \sum_i ([\boldsymbol x^{(i)}]^T \boldsymbol u)^2 . \]\(\boldsymbol y = M_C \boldsymbol u\), 则\(y_i = [\boldsymbol x^{(i)}]^T \boldsymbol u\), 目标函数为 \[ \sum_i y_i^2 = \| \boldsymbol y \|^2 = \boldsymbol y^T \boldsymbol y = \boldsymbol u^T M_C^T M_C \boldsymbol u = n \boldsymbol u^T S \boldsymbol u . \] 其中\(S\)是样本方差阵, 问题变成 \[ \max_{\boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1} \boldsymbol u^T S \boldsymbol u . \] 于是当数据为\(M_C\)(中心化的数据集)时, 取\(S\)的最大特征值对应的单位特征向量\(\boldsymbol u\), 得到的\(\boldsymbol y = M_c \boldsymbol u\)是将\(n\)个点用\(\mathcal S\)中的点进行最小二乘逼近的最优结果(参见1.5.3)。

可以进一步考虑用\(\mathbb R^p\)的二维子空间的\(n\)个点近似原来的\(n\)个点。 取 \[ \mathbb S = \{ \lambda_1 \boldsymbol u_1 + \lambda_2 \boldsymbol u_2: \lambda_1, \lambda_2 \in \mathbb R \} , \] 其中\(\boldsymbol u_1, \boldsymbol u_2 \in \mathbb R^p\)\(\| \boldsymbol u_1 \| = \| \boldsymbol u_2 \| = 1\), 且\(\langle \boldsymbol u_1, \boldsymbol u_2 \rangle = 0\)。 这是穿过原点的一个平面。 求\(\boldsymbol u_1, \boldsymbol u_2\)使得 \[ \min \sum_{i} \| \boldsymbol x^{(i)} - \lambda_1 \boldsymbol u_1 - \lambda_2 \boldsymbol u_2 \|^2, \] 其中 \[ (\lambda_1, \lambda_2) = \text{argmin} \| \boldsymbol x^{(i)} - \lambda_1 \boldsymbol u_1 - \lambda_2 \boldsymbol u_2 \|^2 . \] 这仍是\(\boldsymbol x^{(i)}\)\(\mathcal S\)的正交投影, 有 \[ \lambda_j = \langle \boldsymbol x^{(i)}, \boldsymbol u_j \rangle, \ j=1, 2 . \] 可以证明优化问题的解是\(\boldsymbol u_1\)\(S\)的最大特征值对应的单位特征向量, \(\boldsymbol u_2\)\(S\)的最大特征值对应的单位特征向量。

8.4.2 未中心化的数据

从以上推导看出, \(M\)不需要中心化, 则这时\(\boldsymbol u\)\(M^T M\)的最大特征值对应的单位特征向量。 但是, 这样的逼近不是\(\mathbb R^p\)的一维子集的最小二乘逼近, 当\(\mathcal S\)为一维时, 上面的方法中的\(\mathcal S\)是一条穿过原点的直线, 为了最优逼近\(\mathbb R^p\)中的\(n\)个点, 取消“穿过原点”这个要求可以得到更合理的逼近结果。 当\(\mathcal S\)为二维时, 上面的方法中的\(\mathcal S\)是穿过原点的平面, 取消“穿过原点”这个要求也可以得到更合理的逼近结果。

所以, 我们可以考虑\(\mathbb R^p\)这样的仿射子集: \[ \mathcal S = \{\boldsymbol c + \lambda \boldsymbol u: \ \lambda \in \mathbb R \}, \] 其中\(\boldsymbol c, \boldsymbol u \in \mathbb R^p\)\(\| \boldsymbol u \| = 1\)。 考虑最小化问题 \[\begin{align} \min_{\boldsymbol c, \boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1} \;\min_{\{ \lambda_i \}} \sum_i \| \boldsymbol x^{(i)} - \boldsymbol c - \lambda_i \boldsymbol u \|^2 . \tag{8.1} \end{align}\] 其中 \[ \lambda_i = \text{argmin}_{\lambda} \| \boldsymbol x^{(i)} - \boldsymbol c - \lambda \boldsymbol u \|^2 . \] 这又是一个投影问题, 有 \[ \lambda_i = \langle \boldsymbol x^{(i)} - \boldsymbol c, \boldsymbol u \rangle . \] 于是 \[\begin{aligned} & \| \boldsymbol x^{(i)} - \boldsymbol c - \lambda_i \boldsymbol u \|^2 \\ =& \| \boldsymbol x^{(i)} - \boldsymbol c \|^2 - \langle \boldsymbol x^{(i)} - \boldsymbol c, \boldsymbol u \rangle^2, \\ & \sum_i \| \boldsymbol x^{(i)} - \boldsymbol c - \lambda_i \boldsymbol u \|^2 \\ =& \sum_i \| \boldsymbol x^{(i)} - \boldsymbol c \|^2 - \sum_i \langle \boldsymbol x^{(i)} - \boldsymbol c, \boldsymbol u \rangle^2 . \end{aligned}\]\[\begin{aligned} \bar{\boldsymbol x} =& \frac{1}{n} \sum_i \boldsymbol x^{(i)}, \\ \boldsymbol d =& \boldsymbol c - \bar{\boldsymbol x}, \\ \boldsymbol x^{(i)} - \boldsymbol c =& (\boldsymbol x^{(i)} - \bar{\boldsymbol x}) + (\bar{\boldsymbol x} - \boldsymbol c) \\ =& (\boldsymbol x^{(i)} - \bar{\boldsymbol x}) - \boldsymbol d . \end{aligned}\] 将目标函数化为 \[\begin{align} \sum_i \| \boldsymbol x^{(i)} - \bar{\boldsymbol x} \|^2 + n \| \boldsymbol d \|^2 - \sum_i \langle \boldsymbol x^{(i)} - \bar{\boldsymbol x}, \boldsymbol u \rangle^2 - n \langle \boldsymbol d, \boldsymbol u \rangle^2 . \tag{8.2} \end{align}\] 上式的推导利用了 \[\begin{aligned} & \sum_i \langle \boldsymbol x^{(i)} - \bar{\boldsymbol x}, \;\boldsymbol d \rangle = 0, \\ & \sum_i \langle \boldsymbol x^{(i)} - \bar{\boldsymbol x}, \; \boldsymbol u \rangle \langle \boldsymbol d, \; \boldsymbol u \rangle = 0 . \end{aligned}\]

考虑(8.2)关于\(\boldsymbol d\)\(\boldsymbol u\)的最小值问题。 前面已经证明 \[ - \sum_i \langle \boldsymbol x^{(i)} - \bar{\boldsymbol x}, \boldsymbol u \rangle^2 = -n \boldsymbol u^T S \boldsymbol u . \] 所以问题转化为 \[\begin{align} \min_{\boldsymbol d, \boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1 } \left\{ \boldsymbol d^T \boldsymbol d - \boldsymbol u^T S \boldsymbol u - (\boldsymbol u^T \boldsymbol d)^2 \right\} . \tag{8.3} \end{align}\]

如果\(\boldsymbol d = \boldsymbol 0\), 即\(\boldsymbol c = \bar{\boldsymbol x}\), 则\(\boldsymbol u\)的解为\(S\)的最大特征值对应的单位特征向量。 记\(\boldsymbol u_1\)为这个解。

如果\(\boldsymbol d \neq 0\), 约束最小问题(8.3)的Lagrange乘子函数为 \[ g(\boldsymbol u, \boldsymbol d, \alpha) = \boldsymbol d^T \boldsymbol d - \boldsymbol u^T S \boldsymbol u - (\boldsymbol u^T \boldsymbol d)^2 - \alpha (\boldsymbol u^T \boldsymbol u - 1) . \] 约束最小值点应满足 \[\begin{aligned} \boldsymbol 0 =& \frac{\partial g}{\partial \boldsymbol u} = -2 S \boldsymbol u - 2 (\boldsymbol u^T \boldsymbol d) \boldsymbol d - 2 \alpha \boldsymbol u, \\ \boldsymbol 0 =& \frac{\partial g}{\partial \boldsymbol d} = 2 \boldsymbol d - 2 (\boldsymbol u^T \boldsymbol d) \boldsymbol u, \\ 0 =& \frac{\partial g}{\partial \alpha} = 1 - \boldsymbol u^T \boldsymbol u . \end{aligned}\] 第三式要求\(\boldsymbol u^T \boldsymbol u = 1\)。 由第二式得 \[ \boldsymbol d = (\boldsymbol u^T \boldsymbol d) \boldsymbol u, \] 这是约束最小值点的必要条件。 于是初始的优化问题(8.1)变成 \[\begin{aligned} & \min_{\boldsymbol c, \boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1} \;\min_{\{ \lambda_i \}} \sum_i \| \boldsymbol x^{(i)} - \bar{\boldsymbol x} - \boldsymbol d - \lambda_i \boldsymbol u \|^2 \\ =& \min_{\boldsymbol c, \boldsymbol u \in \mathbb R^p, \| \boldsymbol u \| = 1} \;\min_{\{ \lambda_i \}} \sum_i \| \boldsymbol x^{(i)} - \bar{\boldsymbol x} - [\lambda_i + (\boldsymbol u^T \boldsymbol d) ] \boldsymbol u \|^2 , \end{aligned}\] 这要求 \[ \lambda_i + (\boldsymbol u^T \boldsymbol d) = \langle \boldsymbol x^{(i)} - \bar{\boldsymbol x}, \boldsymbol u \rangle , \] 且要求\(\boldsymbol u = \boldsymbol u_1\)。 这与\(\boldsymbol d = \boldsymbol 0\)的目标函数值相等, 所以原来的约束极值问题的解为:

用仿射集\(\mathcal S = \{\boldsymbol c + \lambda \boldsymbol u \}\)逼近\(M\)的各行代表的\(n\)个点时, 在最小二乘准则下的解是令\(\boldsymbol c = \bar{\boldsymbol x}\)\(\boldsymbol u\)取为\(S\)的最大特征值对应的单位特征向量。 这正是求解第一个样本主成分得分的方法。

更多的样本主成分解释类似, 比如取两个样本主成分时, 采用的子空间为: \[ \mathcal S = \{ \boldsymbol c + \lambda_1 \boldsymbol u_1 + \lambda_2 \boldsymbol u_2: \lambda_1, \lambda_2 \in \mathbb R \}, \] 其中\(\boldsymbol c, \boldsymbol u_1, \boldsymbol u_2 \in \mathbb R^p\)\(\| \boldsymbol u_1 \| = \| \boldsymbol u_2 \| = 1\)\(\langle \boldsymbol u_1, \boldsymbol u_2 \rangle = 0\)。 这时\(\boldsymbol c\)仍取为\(\bar{\boldsymbol x}\)\(\boldsymbol u_1\)\(S\)的最大特征值对应的单位特征向量, \(\boldsymbol u_2\)\(S\)的第二大特征值对应的单位特征向量。 解决的问题是用\(\mathbb R^p\)中的一个平面上的点对\(M\)代表的\(n\)个点在最小二乘意义下进行最优逼近。

8.5 双重散点图(biplot)

8.5.1 R型主成分分析与Q型主成分分析的对偶关系

设矩阵\(A\)\(n^{-1/2}M_C\)\(n^{-1/2} M_S\), 这时\(A^T A\)是协方差阵\(S\) 或相关阵\(R\)

\(A = n^{-1/2} M_C\)为例。样本协方差阵\(S\)有特征值分解 \[\begin{align*} S =& A^T A = \sum_{j=1}^p l_j \boldsymbol g_j \boldsymbol g_j^T, \end{align*}\] 其中\(l_j>0\)\(A^T A\)的特征值, \(\boldsymbol g_j \in \mathbb R^p\)是矩阵\(A^T A\)对应于特征值\(l_j\)的单位特征向量(长度等于1的特征向量)。

由线性代数知识,矩阵\(A^T A\)\(A A^T\)具有相同的正特征值, 且若\(\boldsymbol g_j\)是矩阵\(A^T A\)对应于正特征值\(l_j\)的单位特征向量, 则\(A \boldsymbol g_j \in \mathbb R^n\)是矩阵\(A A^T\)对应于正特征值\(l_j\)的特征向量, 归一化得\(\boldsymbol f_j = l_j^{-1/2} A \boldsymbol g_j\)是矩阵\(A A^T\)对应于正特征值\(l_j\)的单位特征向量。

\(\boldsymbol f_j\)是矩阵\(A A^T\)对应于正特征值\(l_j\)的单位特征向量, 则\(\boldsymbol g_j = l_j^{-1/2} A^T \boldsymbol f_j\)是矩阵\(A^T A\)对应于正特征值\(l_j\)的特征向量。

\(A\)有奇异值分解 \[\begin{aligned} A = F \Lambda G^T = \sum_{j=1}^p l_j^{1/2} \boldsymbol f_j \boldsymbol g_j^T, \end{aligned}\] 其中\(\Lambda = \text{diag}(l_1^{1/2}, l_2^{1/2}, \dots, l_p^{1/2})\), \(G = (\boldsymbol g_1, \dots, \boldsymbol g_p)\), \(F = (\boldsymbol f_1, \dots, \boldsymbol f_p)\)

在R型主成分分析中, 第\(j\)个R型主成分得分为 \[\begin{align*} M_C \boldsymbol g_j = n^{1/2} A \boldsymbol g_j = n^{1/2} l_j^{1/2} \boldsymbol f_j, j=1,2,\dots, p. \end{align*}\]

在根据\(M_C\)作的Q型主成分分析中, 第\(j\)个Q型主成分得分为 \[\begin{align*} M_C^T \boldsymbol f_j = n^{1/2} A^T \boldsymbol f_j = n^{1/2} l_j^{1/2} \boldsymbol g_j , j=1,2,\dots, p. \end{align*}\]

8.5.2 双重散点图

若仅取前两个R型主成分得分,得 \[\begin{aligned} M_C = n^{1/2} A \approx n^{1/2} l_1^{1/2} \boldsymbol f_1 \boldsymbol g_1^T + n^{1/2} l_2^{1/2} \boldsymbol f_2 \boldsymbol g_2^T, \end{aligned}\] 其中\(n^{1/2} l_1^{1/2} \boldsymbol f_1 = M_C \boldsymbol g_1\)是第一主成分得分\(\boldsymbol s_1\)\(n^{1/2} l_2^{1/2} \boldsymbol f_2 = M_C \boldsymbol g_2\)是第二主成分得分\(\boldsymbol s_2\), 所以 \[\begin{align*} M_C \approx \boldsymbol s_1 \boldsymbol g_1^T + \boldsymbol s_2 \boldsymbol g_2^T . \end{align*}\]

可以在\((s_{i1}, s_{i2})\)坐标画散点, 把\(n\)个观测画成散点图。

注意第\(j\)个Q型主成分\(n^{1/2} l_j^{1/2} \boldsymbol g_j\) 就是载荷阵\(G\)的第\(j\)列的伸缩。

\((g_{j1}, g_{j2})\)为第\(j\)个变量的坐标, 在原来观测的主成分的散点图上画从原点到 \((g_{j1}, g_{j2})\)的矢量, 这样的图形叫做双重散点图(biplot)。 当某个变量与某个观测接近时, 表示该变量在该观测上取值较大。 关于这一点将在“对应分析”部分更详细地阐述。

8.6 主成分分析的做法

8.6.1 主成分分析的步骤

  • 选择是否标准化数据;
  • 计算样本协方差阵或相关阵;
  • \(S\)\(R\)的特征值分解;
  • 按主成分累积贡献率超过80%(或其它满意的比例)的要求确定主成分的个数\(k\)
  • 计算各个主成分得分;
  • 对分析结果做统计意义和实际意义的解释。
  • 实际计算时用R函数计算,这些步骤已经被整合在一些R函数中。

8.6.2 R中的有关函数

R中用princomp()函数进行主成分分析。 用cor=选择是否进行标准化, 用scores=选择是否输出主成分得分。

summary()函数显示主成分分析的概况, 用loadings=TRUE要求显示载荷, 即主成分线性组合系数, 也即特征向量。

loadings()单独输出载荷。

predict()函数用来对训练数据或新数据计算主成分得分。 注意:计算主成分得分时,都按训练数据的列均值进行中心化。

screeplot()函数显示从大到小排列的特征值的线图或条形图, 便于在特征值骤减的位置截断从而确定主成分个数。

取前两个主成分时, 用biplot()函数作双重散点图(biplot)。

8.6.3 例6.1(学生六科成绩的主成分分析)

P.83例6.1, 数据为52名学生的数学(x1)、物理(x2)、化学(x3)、语文(x4)、历史(x5)和英语(x6)的成绩。 作主成分分析。

读入数据,变量名用有意义的短名:

student6Score <- readr::read_csv("data/学生六科成绩.csv",
  show_col_types = FALSE)
names(student6Score) <- c(
  "数学", "物理", "化学", "语文", "历史", "英语")
d <- student6Score

显示数据:

knitr::kable(d)
数学 物理 化学 语文 历史 英语
65 61 72 84 81 79
77 77 76 64 70 55
67 63 49 65 67 57
78 84 75 62 71 64
66 71 67 52 65 57
83 100 79 41 67 50
86 94 97 51 63 55
67 84 53 58 66 56
69 56 67 75 94 80
77 90 80 68 66 60
84 67 75 60 70 63
62 67 83 71 85 77
91 74 97 62 71 66
82 70 83 68 77 85
66 61 77 62 73 64
90 78 78 59 72 66
77 89 80 73 75 70
72 68 77 83 92 79
72 67 61 92 92 88
81 90 79 73 85 80
68 85 70 84 89 86
85 91 95 63 76 66
91 85 100 70 65 76
74 74 84 61 80 69
88 100 85 49 71 66
87 84 100 74 81 76
64 79 64 72 76 74
60 51 60 78 74 76
59 75 81 82 77 73
64 61 49 100 99 95
56 48 61 85 82 80
62 45 67 78 76 82
86 78 92 87 87 77
80 98 83 58 66 66
83 71 81 63 77 73
67 83 65 68 74 60
71 58 45 83 77 73
90 83 91 58 60 59
73 80 64 75 80 78
87 98 87 68 78 64
69 72 79 89 82 73
79 73 69 65 73 73
87 86 88 70 73 70
76 61 73 63 60 70
99 100 99 53 63 60
78 68 52 75 74 66
72 90 73 76 80 79
69 64 60 68 74 80
52 62 65 100 96 100
70 72 56 74 82 74
72 74 75 88 91 86
68 74 70 87 87 83

每列中心化:

d2 <- scale(d, scale=FALSE) 

每列标准化:

d3 <- scale(d) 

8.6.3.1 用方差阵作主成分分析

因为是分数,是可比的,所以可以用样本方差阵, 方差阵估计:

knitr::kable(var(d), digits=2)
数学 物理 化学 语文 历史 英语
数学 108.63 92.46 100.64 -74.69 -44.72 -48.67
物理 92.46 188.23 109.00 -88.13 -45.33 -66.87
化学 100.64 109.00 192.46 -67.30 -35.75 -35.99
语文 -74.69 -88.13 -67.30 163.38 97.88 113.57
历史 -44.72 -45.33 -35.75 97.88 88.64 82.05
英语 -48.67 -66.87 -35.99 113.57 82.05 113.30

用图形方式显示方差阵:

library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ tibble  3.1.8           ✔ dplyr   1.0.99.9000
## ✔ tidyr   1.2.1.9001      ✔ stringr 1.5.0.9000 
## ✔ readr   2.1.2           ✔ forcats 0.5.2      
## ✔ purrr   1.0.0
## Warning: package 'purrr' was built under R version 4.2.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
dgg <- tibble(
  x = rep(1:6, 6), 
  y = rep(1:6, each=6),
  Cov = c(as.matrix(var(d)))) %>%
  mutate(
    x = factor(x, levels=1:6, labels=names(d)),
    y = factor(y, levels=6:1, labels=rev(names(d))))
ggplot(data=dgg, mapping=aes(x=x, y=y, fill=Cov)) +
  geom_raster() +
  scale_fill_gradient2() +
  labs(x=NULL, y=NULL)

计算主成分分析:

pca1 <- princomp(d, cor=FALSE)

用方差阵得到的主成分结果:

oldop <- options("digits")
options(digits=4)
print(pca1)
## Call:
## princomp(x = d, cor = FALSE)
## 
## Standard deviations:
## Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 
## 22.636 13.326  8.807  5.790  4.610  3.985 
## 
##  6  variables and  52 observations.
print(summary(pca1, loadings=TRUE))
## Importance of components:
##                         Comp.1  Comp.2  Comp.3 Comp.4  Comp.5  Comp.6
## Standard deviation     22.6360 13.3263 8.80699 5.7900 4.61027 3.98464
## Proportion of Variance  0.6113  0.2119 0.09254 0.0400 0.02536 0.01894
## Cumulative Proportion   0.6113  0.8232 0.91570 0.9557 0.98106 1.00000
## 
## Loadings:
##      Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6
## 数学  0.373  0.205  0.121  0.882  0.112  0.120
## 物理  0.482  0.288 -0.802 -0.143        -0.147
## 化学  0.440  0.588  0.541 -0.407              
## 语文 -0.471  0.432 -0.125         0.759       
## 历史 -0.298  0.383 -0.184        -0.458  0.721
## 英语 -0.352  0.444         0.190 -0.448 -0.664
options(oldop)

结果中有各主成分的标准差,贡献率,累积贡献率。 可以看出前两个主成分累积贡献率达到82%, 第一主成分贡献率为61%。 从载荷看, 第一主成分在理科上系数为正,在文科上系数为负,反映了学生理科相对于文科的优势。 第二主成分都为正数且值相近,反映学生所有六科的整体水平。

注意:得到载荷阵后, 经常可以用每一列的系数来解释每一个对应的主成分含义。 当数据确实有很强的变量分类时, 这可能是有意义的。 但是, 大多数情况下, 这些系数只是数学上矩阵特征值分解的结果, 并不保证能够有实际解释, 及时能有听起来合理的解释, 也可能仅是巧合, 数据的随机扰动就有可能修改系数造成不同的解释。

单独用loadings()函数显示载荷阵:

print(loadings(pca1))
## 
## Loadings:
##      Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6
## 数学  0.373  0.205  0.121  0.882  0.112  0.120
## 物理  0.482  0.288 -0.802 -0.143        -0.147
## 化学  0.440  0.588  0.541 -0.407              
## 语文 -0.471  0.432 -0.125         0.759       
## 历史 -0.298  0.383 -0.184        -0.458  0.721
## 英语 -0.352  0.444         0.190 -0.448 -0.664
## 
##                Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6
## SS loadings     1.000  1.000  1.000  1.000  1.000  1.000
## Proportion Var  0.167  0.167  0.167  0.167  0.167  0.167
## Cumulative Var  0.167  0.333  0.500  0.667  0.833  1.000

loadings函数显示的第二部分没有什么用处。

screeplot()用于帮助确定主成分个数:

screeplot(pca1, type='lines')

或者:

library(tidyverse)
dgg <- tibble(
  `主成分` = seq(length(summary(pca1)$sdev)),
  `方差` = summary(pca1)$sdev^2,
  `方差比例` = `方差` / sum(`方差`),
  `累计方差比例` = cumsum(`方差比例`))
ggplot(data=dgg, mapping=aes(
  x=`主成分`, y = `方差`)) + 
  geom_line() +
  geom_point()

也可以画成百分比:

ggplot(data=dgg, mapping=aes(
  x=`主成分`, y = `方差比例`)) + 
  geom_line() +
  geom_point() +
  scale_y_continuous(labels = scales::percent)

也可以做方差累计比例图:

ggplot(data=dgg, mapping=aes(
  x=`主成分`, y = `累计方差比例`)) + 
  geom_line() +
  geom_point() +
  scale_y_continuous(
    limits=c(0, NA),
    labels = scales::percent)

计算前两个主成分的主成分得分:

da1 <- cbind(id=seq(nrow(d)), d, predict(pca1)[,1:2])
knitr::kable(da1, digits=2)
id 数学 物理 化学 语文 历史 英语 Comp.1 Comp.2
1 65 61 72 84 81 79 -22.02 2.65
2 77 77 76 64 70 55 13.09 -11.42
3 67 63 49 65 67 57 -9.55 -33.20
4 78 84 75 62 71 64 13.87 -6.28
5 66 71 67 52 65 57 8.57 -26.90
6 83 100 79 41 67 50 41.22 -15.10
7 86 94 97 51 63 55 42.09 -0.63
8 67 84 53 58 66 56 6.28 -28.65
9 69 56 67 75 94 80 -25.12 0.63
10 77 90 80 68 66 60 18.66 -2.92
11 84 67 75 60 70 63 9.51 -11.64
12 62 67 83 71 85 77 -9.77 5.26
13 91 74 97 62 71 66 22.87 7.32
14 82 70 83 68 77 85 0.12 9.41
15 66 61 77 62 73 64 -1.41 -13.42
16 90 78 78 59 72 66 17.18 -3.81
17 77 89 80 73 75 70 9.61 6.84
18 72 68 77 83 92 79 -16.64 12.82
19 72 67 61 92 92 88 -31.57 11.00
20 81 90 79 73 85 80 4.64 15.62
21 68 85 70 84 89 86 -15.07 15.17
22 85 91 95 63 76 66 25.98 12.16
23 91 85 100 70 65 76 23.99 17.84
24 74 74 84 61 80 69 7.54 0.54
25 88 100 85 49 71 66 35.13 1.53
26 87 84 100 74 81 76 15.36 24.59
27 64 79 64 72 76 74 -8.33 -6.39
28 60 51 60 78 74 76 -28.01 -14.91
29 59 75 81 82 77 73 -9.31 5.68
30 64 61 49 100 99 95 -51.05 9.82
31 56 48 61 85 82 80 -37.61 -8.15
32 62 45 67 78 76 82 -29.79 -8.69
33 86 78 92 87 87 77 0.31 26.30
34 80 98 83 58 66 66 27.55 0.11
35 83 71 81 63 77 73 6.68 1.25
36 67 83 65 68 74 60 2.57 -12.73
37 71 58 45 83 77 73 -29.32 -17.48
38 90 83 91 58 60 59 31.83 -2.86
39 73 80 64 75 80 78 -8.51 0.35
40 87 98 87 68 78 64 24.34 11.92
41 69 72 79 89 82 73 -12.69 10.63
42 79 73 69 65 73 73 1.12 -6.72
43 87 86 88 70 73 70 17.43 10.66
44 76 61 73 63 60 70 1.85 -15.61
45 99 100 99 53 63 60 48.01 8.01
46 78 68 52 75 74 66 -11.68 -16.76
47 72 90 73 76 80 79 -0.93 9.19
48 69 64 60 68 74 80 -15.09 -11.86
49 52 62 65 100 96 100 -48.88 18.12
50 70 72 56 74 82 74 -15.72 -8.71
51 72 74 75 88 91 86 -19.16 18.25
52 68 74 70 87 87 83 -20.13 11.20

用前两个主成分作散点图:

ggplot(data=da1, mapping=aes(
  x = Comp.1, y = Comp.2)) + 
  geom_point()

显示编号:

library(ggplot2)
library(ggrepel)
ggplot(data=da1, mapping=aes(
  x = Comp.1, y = Comp.2, label=id)) + 
  geom_text_repel() +
  geom_point(alpha=0.5, color="blue")

为了验证主成分得分的计算公式, 自己用载荷和中心化数据计算第二主成分:

print(round(c(as.matrix(d2) %*% loadings(pca1)[,2]), 2))
##  [1]   2.65 -11.42 -33.20  -6.28 -26.90 -15.10  -0.63 -28.65   0.63  -2.92
## [11] -11.64   5.26   7.32   9.41 -13.42  -3.81   6.84  12.82  11.00  15.62
## [21]  15.17  12.16  17.84   0.54   1.53  24.59  -6.39 -14.91   5.68   9.82
## [31]  -8.15  -8.69  26.30   0.11   1.25 -12.73 -17.48  -2.86   0.35  11.92
## [41]  10.63  -6.72  10.66 -15.61   8.01 -16.76   9.19 -11.86  18.12  -8.71
## [51]  18.25  11.20

与predict()结果一致。

显示载荷阵的前两列,作为变量压缩结果(R型主成分分析):

dgl <- loadings(pca1)[,1:2]
plot(dgl, type='n',
     xlab='PCA1', ylab='PCA2',
     main='Loadings')
text(dgl, rownames(dgl))

或:

dgl <- as.data.frame(loadings(pca1)[,1:2])
dgl[["varnames"]] <- rownames(dgl)
ggplot(data=dgl, mapping=aes(
  x=Comp.1, y=Comp.2, label=varnames)) +
  geom_text()

制作双重散点图:

biplot(da1[,c("Comp.1", "Comp.2")], 
  loadings(pca1)[,1:2])

或:

library(ggfortify)
dggd <- as.data.frame(da1[,c("Comp.1", "Comp.2")])
dggl <- as.data.frame(loadings(pca1)[,1:2])
dggl[["varnames"]] <- rownames(loadings(pca1))
ggbiplot(
  dggd, dggl,
  loadings=TRUE,
  loadings.label=TRUE,
  loadings.label.label="varnames")

8.6.3.2 用相关阵作主成分分析

上述分析用相关阵重做一遍。 相关阵为:

print(round(cor(d), 3))
##        数学   物理   化学   语文   历史   英语
## 数学  1.000  0.647  0.696 -0.561 -0.456 -0.439
## 物理  0.647  1.000  0.573 -0.503 -0.351 -0.458
## 化学  0.696  0.573  1.000 -0.380 -0.274 -0.244
## 语文 -0.561 -0.503 -0.380  1.000  0.813  0.835
## 历史 -0.456 -0.351 -0.274  0.813  1.000  0.819
## 英语 -0.439 -0.458 -0.244  0.835  0.819  1.000

相关阵图形:

library(ggcorrplot)
## Warning: package 'ggcorrplot' was built under R version 4.2.2
ggcorrplot(cor(d), hc.order=TRUE,
  lab = TRUE)

计算主成分分析结果:

pca2 <- princomp(d, cor=TRUE, scores=TRUE)
print(pca2)
## Call:
## princomp(x = d, cor = TRUE, scores = TRUE)
## 
## Standard deviations:
##    Comp.1    Comp.2    Comp.3    Comp.4    Comp.5    Comp.6 
## 1.9261112 1.1236019 0.6639552 0.5200978 0.4117231 0.3830929 
## 
##  6  variables and  52 observations.

主成分分析结果pca2中,有如下变量:

  • sdev: 主成分的标准差,即各个特征值的平方根
  • loadings: 载荷阵,每列是一个特征向量
  • center: 中心化所用的中心,应为各列平均值
  • scale: 如果做了标准化,所用的除数,应为各列标准差但使用1/n而非1/(n-1)的公式
  • n.obs: 观测个数
  • scores: 输出的主成分得分(需要scores=TRUE选项)

summary()输出更详细的结果:

print(summary(pca2, loadings=TRUE, scores=TRUE))
## Importance of components:
##                           Comp.1    Comp.2     Comp.3     Comp.4     Comp.5
## Standard deviation     1.9261112 1.1236019 0.66395522 0.52009785 0.41172308
## Proportion of Variance 0.6183174 0.2104135 0.07347275 0.04508363 0.02825265
## Cumulative Proportion  0.6183174 0.8287309 0.90220369 0.94728732 0.97553997
##                            Comp.6
## Standard deviation     0.38309295
## Proportion of Variance 0.02446003
## Cumulative Proportion  1.00000000
## 
## Loadings:
##      Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6
## 数学  0.412  0.376  0.216  0.788         0.145
## 物理  0.381  0.357 -0.806 -0.118 -0.212 -0.141
## 化学  0.332  0.563  0.467 -0.588              
## 语文 -0.461  0.279               -0.599  0.590
## 历史 -0.421  0.415 -0.250         0.738  0.205
## 英语 -0.430  0.407  0.146  0.134 -0.222 -0.749

结果中有各主成分的标准差,贡献率,累积贡献率。 可以看出前两个主成分累积贡献率达到82%, 第一主成分贡献率为61%。 从载荷看, 第一主成分在理科上系数为负,在文科上系数为正,反映了学生在文理科的相对优势; 第二主成分都为负数且值相近,可以把系数都取相反数,反映学生所有六科的整体水平。

loadings()函数单独求载荷。pca2$loadings也是:

print(loadings(pca2))
## 
## Loadings:
##      Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6
## 数学  0.412  0.376  0.216  0.788         0.145
## 物理  0.381  0.357 -0.806 -0.118 -0.212 -0.141
## 化学  0.332  0.563  0.467 -0.588              
## 语文 -0.461  0.279               -0.599  0.590
## 历史 -0.421  0.415 -0.250         0.738  0.205
## 英语 -0.430  0.407  0.146  0.134 -0.222 -0.749
## 
##                Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6
## SS loadings     1.000  1.000  1.000  1.000  1.000  1.000
## Proportion Var  0.167  0.167  0.167  0.167  0.167  0.167
## Cumulative Var  0.167  0.333  0.500  0.667  0.833  1.000

loadings函数的第二部分输出没有什么用处。

predict()计算前两个主成分的主成分得分, 数据是标准化过的, pca2$scores也是得分:

da2 <- cbind(id=seq(nrow(d)), d, predict(pca2)[,1:2])
knitr::kable(da2, digits=2)
id 数学 物理 化学 语文 历史 英语 Comp.1 Comp.2
1 65 61 72 84 81 79 -1.85 -0.10
2 77 77 76 64 70 55 1.38 -0.93
3 67 63 49 65 67 57 -0.04 -2.80
4 78 84 75 62 71 64 1.26 -0.41
5 66 71 67 52 65 57 1.14 -2.27
6 83 100 79 41 67 50 3.52 -0.82
7 86 94 97 51 63 55 3.52 0.10
8 67 84 53 58 66 56 0.98 -2.33
9 69 56 67 75 94 80 -2.25 0.13
10 77 90 80 68 66 60 1.68 -0.32
11 84 67 75 60 70 63 1.18 -0.76
12 62 67 83 71 85 77 -1.16 0.21
13 91 74 97 62 71 66 1.94 0.78
14 82 70 83 68 77 85 -0.13 0.91
15 66 61 77 62 73 64 0.09 -1.28
16 90 78 78 59 72 66 1.62 0.05
17 77 89 80 73 75 70 0.65 0.55
18 72 68 77 83 92 79 -1.71 1.01
19 72 67 61 92 92 88 -2.82 0.88
20 81 90 79 73 85 80 -0.04 1.51
21 68 85 70 84 89 86 -1.75 1.18
22 85 91 95 63 76 66 1.87 1.17
23 91 85 100 70 65 76 1.90 1.49
24 74 74 84 61 80 69 0.46 0.13
25 88 100 85 49 71 66 2.74 0.58
26 87 84 100 74 81 76 0.84 2.12
27 64 79 64 72 76 74 -0.71 -0.67
28 60 51 60 78 74 76 -1.96 -1.59
29 59 75 81 82 77 73 -0.98 -0.03
30 64 61 49 100 99 95 -4.49 0.69
31 56 48 61 85 82 80 -2.96 -1.11
32 62 45 67 78 76 82 -2.21 -1.07
33 86 78 92 87 87 77 -0.35 2.19
34 80 98 83 58 66 66 2.21 0.13
35 83 71 81 63 77 73 0.56 0.32
36 67 83 65 68 74 60 0.36 -1.13
37 71 58 45 83 77 73 -1.88 -1.50
38 90 83 91 58 60 59 2.94 -0.11
39 73 80 64 75 80 78 -0.77 0.08
40 87 98 87 68 78 64 1.76 1.22
41 69 72 79 89 82 73 -1.19 0.55
42 79 73 69 65 73 73 0.28 -0.40
43 87 86 88 70 73 70 1.36 1.00
44 76 61 73 63 60 70 0.70 -1.40
45 99 100 99 53 63 60 3.97 1.05
46 78 68 52 75 74 66 -0.44 -1.27
47 72 90 73 76 80 79 -0.39 0.74
48 69 64 60 68 74 80 -1.03 -0.99
49 52 62 65 100 96 100 -4.62 1.00
50 70 72 56 74 82 74 -1.20 -0.65
51 72 74 75 88 91 86 -2.01 1.42
52 68 74 70 87 87 83 -1.95 0.76

pca2中scale的值,是\(1/n\)版本的各列标准差:

print(pca2$scale)
##      数学      物理      化学      语文      历史      英语 
## 10.321675 13.587161 13.738793 12.658624  9.323969 10.541198

数据各列样本标准差:

print(apply(d, 2, sd))
##      数学      物理      化学      语文      历史      英语 
## 10.422377 13.719722 13.872833 12.782125  9.414936 10.644042

数据各列样本标准差,用\(1/n\)版本:

print(apply(d, 2, sd) *sqrt((nrow(d)-1) / nrow(d)))
##      数学      物理      化学      语文      历史      英语 
## 10.321675 13.587161 13.738793 12.658624  9.323969 10.541198

自己用载荷和标准化数据计算的第二主成分(方差公式修正后):

n <- nrow(d)
d4 <- d3 * sqrt(n/(n-1))
print(c(as.matrix(d4) %*% loadings(pca2)[,2]))
##  [1] -0.09869937 -0.93264526 -2.80445313 -0.40584893 -2.26874265 -0.81974747
##  [7]  0.10402783 -2.32640027  0.12983805 -0.32466680 -0.76065098  0.21478424
## [13]  0.78320441  0.90867535 -1.27591036  0.05220103  0.54509667  1.01214990
## [19]  0.87577409  1.50659205  1.18458088  1.17348168  1.48958061  0.12565877
## [25]  0.57906315  2.11740575 -0.66945216 -1.59387782 -0.03447987  0.69279938
## [31] -1.11323328 -1.07153767  2.18736513  0.12884205  0.31668122 -1.13107716
## [37] -1.49590296 -0.10994465  0.08283856  1.22435661  0.54556585 -0.40186537
## [43]  1.00322527 -1.40038695  1.05380785 -1.27115334  0.73806851 -0.99052635
## [49]  0.99651436 -0.65135618  1.42323676  0.75714295

screeplot(),用于帮助确定主成分个数:

screeplot(pca2, type='lines')

前两个主成分载荷的散点图,可以看出前两个主成分使用各个变量的多少:

load2 <- loadings(pca2)
plot(load2[,1:2], type='n',
     xlab='PCA1', ylab='PCA2',
     main='Loadings')
text(load2[,1:2], rownames(load2))

可以看出PCA1受到所有变量的影响,其中x1,x2,x3是负系数,x4,x5,x6是正系数。 PCA2主要受到x3影响,其它几个变量也有影响。

计算corr(PCA[k], X[j]), 在princomp的输出结果中, 元素sdev是各主成分的标准差,即特征值的平方根。 因为用了相关阵,所以sigma[k,k]=1。

rhoxy <- loadings(pca2) %*% diag(pca2$sdev)
plot(rhoxy[,1:2], type='n',
     xlim=c(-1,1), ylim=c(-1,1),
     asp=1,
     xlab='PCA1', ylab='PCA2',
     main='Correlations')
tmpx <- seq(0, 2*pi, length=50)
lines(cos(tmpx), sin(tmpx))
text(rhoxy[,1:2], rownames(rhoxy), pty="s")

或:

library(tidyverse)
library(ggrepel)
rhoxy <- as.data.frame(loadings(pca2) %*% diag(pca2$sdev))[,1:2]
names(rhoxy) <- c("Comp.1", "Comp.2")
rhoxy[["varnames"]] <- rownames(rhoxy)
dcirc <- tibble(
  theta = seq(0, 2*pi, length=100),
  x = cos(theta),
  y = sin(theta))
ggplot() + 
  geom_text_repel(data=rhoxy, mapping=aes(
    x = Comp.1, y = Comp.2, label=varnames)) +
  geom_point(data=rhoxy, mapping=aes(
    x = Comp.1, y = Comp.2), 
    alpha=0.5, color="blue") +
  geom_path(data=dcirc, mapping=aes(
  x=x, y=y)) +
  coord_fixed() 

相关系数矩阵的每行的平方和等于1,前两个元素的平方和小于等于1,都在单位圆内。 六个变量的方差都得到了很好的解释。 这个图中代表变量的点的夹角可以近似表示变量间的相关,锐角正相关,直角不相关,钝角负相关。

biplot, 同时画出前两个主成分得分和前两个载荷:

library(ggfortify)
dggd <- as.data.frame(pca2$scores[,1:2])
dggl <- as.data.frame(loadings(pca2)[,1:2])
dggl[["varnames"]] <- rownames(loadings(pca2))
ggbiplot(
  dggd, dggl,
  loadings=TRUE,
  loadings.label=TRUE,
  loadings.label.label="varnames")

8.7 FactoMineR例子

例子来自FactoMineR文档。

library(FactoMineR)
data(decathlon)
res.pca <- PCA(decathlon, quanti.sup = 11:12, quali.sup=13)
## Warning: ggrepel: 3 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## plot of the eigenvalues
## barplot(res.pca$eig[,1],main="Eigenvalues",names.arg=1:nrow(res.pca$eig))
summary(res.pca)
## 
## Call:
## PCA(X = decathlon, quanti.sup = 11:12, quali.sup = 13) 
## 
## 
## Eigenvalues
##                        Dim.1   Dim.2   Dim.3   Dim.4   Dim.5   Dim.6   Dim.7
## Variance               3.272   1.737   1.405   1.057   0.685   0.599   0.451
## % of var.             32.719  17.371  14.049  10.569   6.848   5.993   4.512
## Cumulative % of var.  32.719  50.090  64.140  74.708  81.556  87.548  92.061
##                        Dim.8   Dim.9  Dim.10
## Variance               0.397   0.215   0.182
## % of var.              3.969   2.148   1.822
## Cumulative % of var.  96.030  98.178 100.000
## 
## Individuals (the 10 first)
##                 Dist    Dim.1    ctr   cos2    Dim.2    ctr   cos2    Dim.3
## SEBRLE      |  2.369 |  0.792  0.467  0.112 |  0.772  0.836  0.106 |  0.827
## CLAY        |  3.507 |  1.235  1.137  0.124 |  0.575  0.464  0.027 |  2.141
## KARPOV      |  3.396 |  1.358  1.375  0.160 |  0.484  0.329  0.020 |  1.956
## BERNARD     |  2.763 | -0.610  0.277  0.049 | -0.875  1.074  0.100 |  0.890
## YURKOV      |  3.018 | -0.586  0.256  0.038 |  2.131  6.376  0.499 | -1.225
## WARNERS     |  2.428 |  0.357  0.095  0.022 | -1.685  3.986  0.482 |  0.767
## ZSIVOCZKY   |  2.563 |  0.272  0.055  0.011 | -1.094  1.680  0.182 | -1.283
## McMULLEN    |  2.561 |  0.588  0.257  0.053 |  0.231  0.075  0.008 | -0.418
## MARTINEAU   |  3.742 | -1.995  2.968  0.284 |  0.561  0.442  0.022 | -0.730
## HERNU       |  2.794 | -1.546  1.782  0.306 |  0.488  0.335  0.031 |  0.841
##                ctr   cos2  
## SEBRLE       1.187  0.122 |
## CLAY         7.960  0.373 |
## KARPOV       6.644  0.332 |
## BERNARD      1.375  0.104 |
## YURKOV       2.606  0.165 |
## WARNERS      1.020  0.100 |
## ZSIVOCZKY    2.857  0.250 |
## McMULLEN     0.303  0.027 |
## MARTINEAU    0.925  0.038 |
## HERNU        1.227  0.091 |
## 
## Variables
##                Dim.1    ctr   cos2    Dim.2    ctr   cos2    Dim.3    ctr
## 100m        | -0.775 18.344  0.600 |  0.187  2.016  0.035 | -0.184  2.420
## Long.jump   |  0.742 16.822  0.550 | -0.345  6.869  0.119 |  0.182  2.363
## Shot.put    |  0.623 11.844  0.388 |  0.598 20.607  0.358 | -0.023  0.039
## High.jump   |  0.572  9.998  0.327 |  0.350  7.064  0.123 | -0.260  4.794
## 400m        | -0.680 14.116  0.462 |  0.569 18.666  0.324 |  0.131  1.230
## 110m.hurdle | -0.746 17.020  0.557 |  0.229  3.013  0.052 | -0.093  0.611
## Discus      |  0.552  9.328  0.305 |  0.606 21.162  0.368 |  0.043  0.131
## Pole.vault  |  0.050  0.077  0.003 | -0.180  1.873  0.033 |  0.692 34.061
## Javeline    |  0.277  2.347  0.077 |  0.317  5.784  0.100 | -0.390 10.807
## 1500m       | -0.058  0.103  0.003 |  0.474 12.946  0.225 |  0.782 43.543
##               cos2  
## 100m         0.034 |
## Long.jump    0.033 |
## Shot.put     0.001 |
## High.jump    0.067 |
## 400m         0.017 |
## 110m.hurdle  0.009 |
## Discus       0.002 |
## Pole.vault   0.479 |
## Javeline     0.152 |
## 1500m        0.612 |
## 
## Supplementary continuous variables
##                Dim.1   cos2    Dim.2   cos2    Dim.3   cos2  
## Rank        | -0.671  0.450 |  0.051  0.003 | -0.058  0.003 |
## Points      |  0.956  0.914 | -0.017  0.000 | -0.066  0.004 |
## 
## Supplementary categories
##                 Dist    Dim.1   cos2 v.test    Dim.2   cos2 v.test    Dim.3
## Decastar    |  0.946 | -0.600  0.403 -1.430 | -0.038  0.002 -0.123 |  0.289
## OlympicG    |  0.439 |  0.279  0.403  1.430 |  0.017  0.002  0.123 | -0.134
##               cos2 v.test  
## Decastar     0.093  1.050 |
## OlympicG     0.093 -1.050 |
plot(res.pca,choix="ind",habillage=13)
## Warning: ggrepel: 3 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Not run: 
## To describe the dimensions
dimdesc(res.pca, axes = 1:2)
## $Dim.1
## 
## Link between the variable and the continuous variables (R-square)
## =================================================================================
##             correlation      p.value
## Points        0.9561543 2.099191e-22
## Long.jump     0.7418997 2.849886e-08
## Shot.put      0.6225026 1.388321e-05
## High.jump     0.5719453 9.362285e-05
## Discus        0.5524665 1.802220e-04
## Rank         -0.6705104 1.616348e-06
## 400m         -0.6796099 1.028175e-06
## 110m.hurdle  -0.7462453 2.136962e-08
## 100m         -0.7747198 2.778467e-09
## 
## $Dim.2
## 
## Link between the variable and the continuous variables (R-square)
## =================================================================================
##           correlation      p.value
## Discus      0.6063134 2.650745e-05
## Shot.put    0.5983033 3.603567e-05
## 400m        0.5694378 1.020941e-04
## 1500m       0.4742238 1.734405e-03
## High.jump   0.3502936 2.475025e-02
## Javeline    0.3169891 4.344974e-02
## Long.jump  -0.3454213 2.696969e-02
## To draw ellipses around the categories of the 13th variable (which is categorical)
plotellipses(res.pca,13)
## Warning: ggrepel: 3 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

8.8 factoMineR包和factoextra包

参见factoMineR包介绍文档。

例子来自factoextra包文档。

library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
# Principal component analysis
# ++++++++++++++++++++++++++++++
data(iris)
res.pca <- prcomp(iris[, -5],  scale = TRUE)

# Graph of individuals
# +++++++++++++++++++++

# Default plot
# Use repel = TRUE to avoid overplotting (slow if many points)
fviz_pca_ind(res.pca, col.ind = "#00AFBB",
   repel = TRUE)

# 1. Control automatically the color of individuals 
   # using the "cos2" or the contributions "contrib"
   # cos2 = the quality of the individuals on the factor map
# 2. To keep only point or text use geom = "point" or geom = "text".
# 3. Change themes using ggtheme: http://www.sthda.com/english/wiki/ggplot2-themes

fviz_pca_ind(res.pca, col.ind="cos2", geom = "point",
   gradient.cols = c("white", "#2E9FDF", "#FC4E07" ))

# Color individuals by groups, add concentration ellipses
# Change group colors using RColorBrewer color palettes
# Read more: http://www.sthda.com/english/wiki/ggplot2-colors
# Remove labels: label = "none".
fviz_pca_ind(res.pca, label="none", habillage=iris$Species,
     addEllipses=TRUE, ellipse.level=0.95, palette = "Dark2")

# Change group colors manually
# Read more: http://www.sthda.com/english/wiki/ggplot2-colors
fviz_pca_ind(res.pca, label="none", habillage=iris$Species,
     addEllipses=TRUE, ellipse.level=0.95,
     palette = c("#999999", "#E69F00", "#56B4E9"))

# Select and visualize some individuals (ind) with select.ind argument.
 # - ind with cos2 >= 0.96: select.ind = list(cos2 = 0.96)
 # - Top 20 ind according to the cos2: select.ind = list(cos2 = 20)
 # - Top 20 contributing individuals: select.ind = list(contrib = 20)
 # - Select ind by names: select.ind = list(name = c("23", "42", "119") )
 
 # Example: Select the top 40 according to the cos2
fviz_pca_ind(res.pca, select.ind = list(cos2 = 40))

# Graph of variables
# ++++++++++++++++++++++++++++
  
# Default plot
fviz_pca_var(res.pca, col.var = "steelblue")

# Control variable colors using their contributions
fviz_pca_var(res.pca, col.var = "contrib", 
   gradient.cols = c("white", "blue", "red"),
   ggtheme = theme_minimal())

# Biplot of individuals and variables
# ++++++++++++++++++++++++++
# Keep only the labels for variables
# Change the color by groups, add ellipses
fviz_pca_biplot(res.pca, label = "var", habillage=iris$Species,
               addEllipses=TRUE, ellipse.level=0.95,
               ggtheme = theme_minimal())

8.9 节6.4案例:主成分综合分析

P.88 节6.4案例:主成分综合分析。 数据为某市工业部门13个行业8项重要经济指标数据。

  • x1为年末固定资产净值,
  • x2为职工人数(单位:人),
  • x3为工业总产值,
  • x4为全员劳动生产率(单位:元/年),
  • x5为百元固定资产原值实现产值,
  • x6为资金利税率(%),
  • x7为标准燃料消费量(单位:吨),
  • x8为能源利用效率(单位:万元/吨)。
industryPCA <- read_csv(
  "data/十三行业主成分.csv",
  locale=locale(encoding = "GB18030"),
  show_col_types = FALSE)
## New names:
## • `` -> `...1`
names(industryPCA)[1] <- "行业"
d <- industryPCA[,-1]
knitr::kable(industryPCA)
行业 x1 x2 x3 x4 x5 x6 x7 x8
冶金 90342 52455 101091 19272 82.0 16.1 197435 0.172
电力 4903 1973 2035 10313 34.2 7.1 592077 0.003
煤炭 6735 21139 3767 1780 36.1 8.2 726396 0.003
化学 49454 36241 81557 22504 98.1 25.9 348226 0.985
机器 139190 203505 215898 10609 93.2 12.6 139572 0.628
建材 12215 16219 10351 6382 62.5 8.7 145818 0.066
森工 2372 6572 8103 12329 184.4 22.2 20921 0.152
食品 11062 23078 54935 23804 370.4 41.0 65486 0.263
纺织 17111 23907 52108 21796 221.5 21.5 63806 0.276
缝纫 1206 3930 6126 15586 330.4 29.5 1840 0.437
皮革 2150 5704 6200 10870 184.2 12.0 8913 0.274
造纸 5251 6155 10383 16875 146.4 27.5 78796 0.151
文教 14341 13203 19396 14691 94.6 17.8 6354 1.574

各列不可比,用样本相关阵。 样本相关阵:

print(round( cor(d), 3))
##        x1     x2     x3     x4     x5     x6     x7     x8
## x1  1.000  0.920  0.962  0.109 -0.289 -0.166  0.007  0.214
## x2  0.920  1.000  0.947 -0.055 -0.197 -0.171 -0.015  0.186
## x3  0.962  0.947  1.000  0.233 -0.104  0.004 -0.078  0.247
## x4  0.109 -0.055  0.233  1.000  0.560  0.781 -0.450  0.301
## x5 -0.289 -0.197 -0.104  0.560  1.000  0.827 -0.609 -0.030
## x6 -0.166 -0.171  0.004  0.781  0.827  1.000 -0.492  0.174
## x7  0.007 -0.015 -0.078 -0.450 -0.609 -0.492  1.000 -0.300
## x8  0.214  0.186  0.247  0.301 -0.030  0.174 -0.300  1.000

前三个指标相关性强,代表规模。

用相关阵作主成分分析:

pca1 <- princomp(d, cor=TRUE)
print(pca1)
## Call:
## princomp(x = d, cor = TRUE)
## 
## Standard deviations:
##     Comp.1     Comp.2     Comp.3     Comp.4     Comp.5     Comp.6     Comp.7 
## 1.76207620 1.70218731 0.96447683 0.80132532 0.55143824 0.29427497 0.17940006 
##     Comp.8 
## 0.04941432 
## 
##  8  variables and  13 observations.
print(summary(pca1, loadings=TRUE))
## Importance of components:
##                           Comp.1    Comp.2    Comp.3     Comp.4     Comp.5
## Standard deviation     1.7620762 1.7021873 0.9644768 0.80132532 0.55143824
## Proportion of Variance 0.3881141 0.3621802 0.1162769 0.08026528 0.03801052
## Cumulative Proportion  0.3881141 0.7502943 0.8665712 0.94683649 0.98484701
##                            Comp.6      Comp.7       Comp.8
## Standard deviation     0.29427497 0.179400062 0.0494143207
## Proportion of Variance 0.01082472 0.004023048 0.0003052219
## Cumulative Proportion  0.99567173 0.999694778 1.0000000000
## 
## Loadings:
##    Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 Comp.7 Comp.8
## x1  0.477  0.296  0.104         0.184         0.758  0.245
## x2  0.473  0.278  0.163 -0.174 -0.305        -0.518  0.527
## x3  0.424  0.378  0.156                      -0.174 -0.781
## x4 -0.213  0.451         0.516  0.539 -0.288 -0.249  0.220
## x5 -0.388  0.331  0.321 -0.199 -0.450 -0.582  0.233       
## x6 -0.352  0.403  0.145  0.279 -0.317  0.714              
## x7  0.215 -0.377  0.140  0.758 -0.418 -0.194              
## x8         0.273 -0.891        -0.322 -0.122

要三个主成分才能达到累积贡献率86%, 两个只有75%。

  • 第一主成分在x1,x2,x3,x7上为正,这四个指标代表了规模, 在x4, x5, x6上为负,这三个指标代表了效率, 所以第一主成分可以认为是效率与规模的对比因素。 第一主成分为负,是效率好而规模小的行业; 第一主成分为正,是规模大而效率差的行业。
  • 第二主成分在除x7之外所有指标上载荷为正,在x7上为负, x7是燃料消费量,第二主成分没有太好的解释。
  • 第三主成分在除了x8之外的指标上载荷为小的正数, 在x8(能源利用效率)上是较大负数, 可认为主要代表了能源利用效率反面。

为了得到更好的解释,可以用带因子旋转的因子分析。

loadings()计算载荷:

print(loadings(pca1))
## 
## Loadings:
##    Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 Comp.7 Comp.8
## x1  0.477  0.296  0.104         0.184         0.758  0.245
## x2  0.473  0.278  0.163 -0.174 -0.305        -0.518  0.527
## x3  0.424  0.378  0.156                      -0.174 -0.781
## x4 -0.213  0.451         0.516  0.539 -0.288 -0.249  0.220
## x5 -0.388  0.331  0.321 -0.199 -0.450 -0.582  0.233       
## x6 -0.352  0.403  0.145  0.279 -0.317  0.714              
## x7  0.215 -0.377  0.140  0.758 -0.418 -0.194              
## x8         0.273 -0.891        -0.322 -0.122              
## 
##                Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 Comp.7 Comp.8
## SS loadings     1.000  1.000  1.000  1.000  1.000  1.000  1.000  1.000
## Proportion Var  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125
## Cumulative Var  0.125  0.250  0.375  0.500  0.625  0.750  0.875  1.000

screeplot()帮助确定主成分个数:

screeplot(pca1, type='lines')

前三个主成分得分:

scores <- predict(pca1)
knitr::kable(cbind(industryPCA, round( scores[,1:3], 3)))
行业 x1 x2 x3 x4 x5 x6 x7 x8 Comp.1 Comp.2 Comp.3
冶金 90342 52455 101091 19272 82.0 16.1 197435 0.172 1.535 0.790 0.560
电力 4903 1973 2035 10313 34.2 7.1 592077 0.003 0.519 -2.697 0.238
煤炭 6735 21139 3767 1780 36.1 8.2 726396 0.003 1.100 -3.357 0.426
化学 49454 36241 81557 22504 98.1 25.9 348226 0.985 0.479 1.232 -1.038
机器 139190 203505 215898 10609 93.2 12.6 139572 0.628 4.713 2.355 0.487
建材 12215 16219 10351 6382 62.5 8.7 145818 0.066 0.343 -1.846 0.032
森工 2372 6572 8103 12329 184.4 22.2 20921 0.152 -1.148 -0.331 0.293
食品 11062 23078 54935 23804 370.4 41.0 65486 0.263 -2.285 2.336 1.144
纺织 17111 23907 52108 21796 221.5 21.5 63806 0.276 -0.876 0.932 0.367
缝纫 1206 3930 6126 15586 330.4 29.5 1840 0.437 -2.115 0.859 0.240
皮革 2150 5704 6200 10870 184.2 12.0 8913 0.274 -0.742 -0.786 -0.128
造纸 5251 6155 10383 16875 146.4 27.5 78796 0.151 -1.250 0.032 0.299
文教 14341 13203 19396 14691 94.6 17.8 6354 1.574 -0.274 0.483 -2.921

食品、缝纫是典型的效率高、规模小的行业; 机器是典型的规模大、效率低的行业。

8.10 瑞士银行真假钞数据主成分分析

load('bankNotes.Rdata')
str(bankNotes)
## tibble [200 × 7] (S3: tbl_df/tbl/data.frame)
##  $ length : num [1:200] 215 215 215 215 215 ...
##  $ left   : num [1:200] 131 130 130 130 130 ...
##  $ right  : num [1:200] 131 130 130 130 130 ...
##  $ bottom : num [1:200] 9 8.1 8.7 7.5 10.4 9 7.9 7.2 8.2 9.2 ...
##  $ top    : num [1:200] 9.7 9.5 9.6 10.4 7.7 10.1 9.6 10.7 11 10 ...
##  $ diag   : num [1:200] 141 142 142 142 142 ...
##  $ genuine: Factor w/ 2 levels "conterfeit","genuine": 2 2 2 2 2 2 2 2 2 2 ...
d <- bankNotes[,1:6]

pca结果:

pca1 <- princomp(d, cor=TRUE)
print(summary(pca1))
## Importance of components:
##                           Comp.1    Comp.2    Comp.3     Comp.4     Comp.5
## Standard deviation     1.7162629 1.1305237 0.9322192 0.67064796 0.51834053
## Proportion of Variance 0.4909264 0.2130140 0.1448388 0.07496145 0.04477948
## Cumulative Proportion  0.4909264 0.7039403 0.8487791 0.92374054 0.96852002
##                            Comp.6
## Standard deviation     0.43460313
## Proportion of Variance 0.03147998
## Cumulative Proportion  1.00000000
print(loadings(pca1))
## 
## Loadings:
##        Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6
## length         0.815         0.575              
## left   -0.468  0.342 -0.103 -0.395 -0.639 -0.298
## right  -0.487  0.252 -0.123 -0.430  0.614  0.349
## bottom -0.407 -0.266 -0.584  0.404  0.215 -0.462
## top    -0.368         0.788  0.110  0.220 -0.419
## diag    0.493  0.274 -0.114 -0.392  0.340 -0.632
## 
##                Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6
## SS loadings     1.000  1.000  1.000  1.000  1.000  1.000
## Proportion Var  0.167  0.167  0.167  0.167  0.167  0.167
## Cumulative Var  0.167  0.333  0.500  0.667  0.833  1.000

主成分得分:

res1 <- predict(pca1)[,1:3]
d2 <- cbind(bankNotes, res1)
knitr::kable(head(cbind(bankNotes, round(res1,3)), 5))
length left right bottom top diag genuine Comp.1 Comp.2 Comp.3
214.8 131.0 131.1 9.0 9.7 141.0 genuine -1.747 1.651 -1.424
214.6 129.7 129.7 8.1 9.5 141.7 genuine 2.274 -0.539 -0.533
214.8 129.7 129.7 8.7 9.6 142.2 genuine 2.277 -0.108 -0.717
214.8 129.7 129.6 7.5 10.4 142.0 genuine 2.284 -0.088 0.606
215.0 129.6 129.7 10.4 7.7 141.8 genuine 2.632 0.039 -3.196

用前两个主成分作图,区分真假钞:

ggplot(d2, aes(
  x = Comp.1, y = Comp.2, color = genuine)) +
  geom_point()