主成分分析PCA的分析及实现

1 原理分析

1.1 必要的数学概念

1.1.1 方差

方差 为离均差平方的算术平方值,用于描述一组数据的离散程度,计算公式为: \(s^2=\frac{{\mathrm{\Sigma}_{i=1}^n(X_i-\bar{X})}^2}{n-1}\)

1.1.2 协方差

方差和标准差中都只有X,是针对一维数据的,描述多维数据的离散程度可以使用协方差,二维数据的协方差 ,计算公式为: \(cov(X,Y)=\frac{\mathrm{\Sigma}_{i=1}^n(X_i-\bar{X})(Y_i-\bar{Y})}{n-1}\) 大于0,表示其中一维数据增大,另一维数据也会增大;

小于0 ,表示其中一维数据增大,另一维数据会减小;

=0,表示这二维数据相互独立。

容易知道: \(cov(X,X)=s^2\)

\[cov\left(X,Y\right)=cov\left(Y,X\right)\]

1.1.3 协方差矩阵

对于多维数据,两两计算其协方差,协方差数量比较多,可以使用一个矩阵来保存这些结果,比如,二维数据的协方差矩阵为: \(C=\left(\begin{matrix}cov(X,X)&cov(X,Y)\\cov(Y,X)&cov(Y,Y)\\\end{matrix}\right)\) 三维数据的协方差矩阵为: \(C=\left(\begin{matrix}cov(X,X)&cov(X,Y)&cov(X,Z)\\cov(Y,X)&cov(Y,Y)&cov(Y,Z)\\cov(Z,X)&cov(Z,Y)&cov(Z,Z)\\\end{matrix}\right)\) 由前面的性质描述可以知道,协方差矩阵是对称矩阵。下图给出了二维数据X/Y相对独立、正相关、负相关时的三个例子:

image-20211201005157401

其中,白数据的协方差C0很明显为: \(C_0=\left(\begin{matrix}1&0\\0&1\\\end{matrix}\right)\)

1.1.4 特征值和特征向量

​ 对于一个给定矩阵A,它的特征向量v经过一个线性变化之后,得到的新向量Av与原来的v保持在同一条直线上,但是其长度或者方向也许会改变,即: \(Av=\lambda v\) 其中lambda为标量,是特征向量的长度在该线性变化下缩放的比例,则称lambda为其特征值, v是A的特征向量。

特征值和特征向量很重要的一个特点是,不同特征值lambda对应的特征向量v是线性无关的,且对阵矩阵的特征向量两两正交。

1.2 PCA原理概述

PCA本质上是旋转坐标轴,找出使数据分散程度最大、保留信息最多的那个坐标轴。

以二维数据为例,二维数据D0经过中心化之后得到D,则D的协方差矩阵C还可以写为: \(C=\left(\begin{matrix}cov\left(X,X\right)&cov\left(X,Y\right)\\cov\left(Y,X\right)&cov\left(Y,Y\right)\\\end{matrix}\right)=\left(\begin{matrix}\frac{{\mathrm{\Sigma}_{i=1}^n\left(X_i-\bar{X}\right)}^2}{n-1}&\frac{\mathrm{\Sigma}_{i=1}^n\left(X_i-\bar{X}\right)\left(Y_i-\bar{Y}\right)}{n-1}\\\frac{\mathrm{\Sigma}_{i=1}^n\left(X_i-\bar{X}\right)\left(Y_i-\bar{Y}\right)}{n-1}&\frac{{\mathrm{\Sigma}_{i=1}^n\left(Y_i-\bar{Y}\right)}^2}{n-1}\\\end{matrix}\right) =\frac{1}{n-1}\left(\begin{matrix}X_1&X_2&X_3\\Y_1&Y_2&Y_3\\\end{matrix}\right)\left(\begin{matrix}X_1&Y_1\\X_2&Y_2\\X_3&Y_3\\\end{matrix}\right)=\frac{1}{n-1}D{D}^T\) D可由白数据D0经过拉伸S和旋转R变换得到,即: \(D=RSD_0\) D的协方差C可写为: \(C=\frac{1}{n-1}{DD}^T=\frac{1}{n-1}\ RSD_0{D_0}^TS^TR^T=\ RS\left({\frac{1}{n-1}D_0}{D_0}^T\right)S^TR^T=RSC_0S^TR^T =RS\left(\begin{matrix}1&0\\0&1\\\end{matrix}\right)S^TR^T=RSS^TR^T\) 其中C0是白数据D0的协方差。对于拉伸操作S和旋转操作R,有: \(S=S^T\)

\[R^T=R^{-1}\]

则综上所述,令常数m为: \(m=SS^T=\left(\begin{matrix}a^2&0\\0&b^2\\\end{matrix}\right)\) 则C可写为: \(C=mRR^{-1}\) 即有: \(CR=mR\) 则m即为协方差矩阵C的特征值,R为其特征向量。同时m即为新坐标系下的协方差矩阵,其两个列向量分别表示在新坐标系下的方差。这样就相当于找到了将原数据转化为白数据的旋转坐标轴的办法。

​ 将上述例子一般化,对于多维数据,可以求出其特征值矩阵,按照降维要求,取相应个数的列向量组成新的矩阵即为我们降维任务所需要的矩阵。流程总结如下图:

image-20211201005828680

2 编程实现

import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 数据
x = np.array([9, 16, 25, 14, 10, 18, 0, 16, 5, 19, 16, 20])
y = np.array([39, 57, 93, 61, 50, 75, 32, 85, 42, 70, 66, 80])
z = np.array([7, 58, 73, 1, 0, 55, 72, 87, 4, 10, 86, 30])
D0 = np.zeros([12, 3])  # 创建一个12行x3列的零矩阵
D0[:, 0] = x
D0[:, 1] = y
D0[:, 2] = z
print("原数据:\n", D0)
print("原数据的维数: \n", D0.shape)

# 中心化
D0_Av=np.mean(D0, axis=0)
print("均值:",D0_Av)
D = D0 - D0_Av
print("中心化之后:\n", D)
print("中心化数据的维数: \n", D.shape)

# 绘制中心化对比图
ax = plt.subplot(111, projection='3d')
for i in range(0, 12):
    ax.scatter(D[i][0], D[i][1], D[i][2], c='b')
for i in range(0, 12):
    ax.scatter(D0[i][0], D0[i][1], D0[i][2], c='r')
plt.title("原数据和中心化之后的数据对比(三维)")
ax.set_zlabel('Z')
ax.set_ylabel('Y')
ax.set_xlabel('X')
plt.show()

# 绘制中心化对比图(投在二维上)
plt.scatter(D0[:, 0], D0[:, 1], label='原数据(XY方向投影)')
plt.scatter(D[:, 0], D[:, 1], label='中心化之后(XY方向投影)')
plt.xlabel('X')
plt.ylabel('Y')
plt.title("原数据和中心化之后的数据对比(二维)")
plt.legend()
plt.grid(True, linestyle='-.')
ax = plt.gca()  # 获取当前坐标的位置
ax.spines['right'].set_color('None')  # 去掉坐标图的上和右的黑边
ax.spines['top'].set_color('None')
ax.xaxis.set_ticks_position('bottom')  # 设置bottom为x轴
ax.yaxis.set_ticks_position('left')  # 设置left为y轴
ax.spines['bottom'].set_position(('data', 0))  # 这个位置的括号要注意
ax.spines['left'].set_position(('data', 0))
plt.show()

# 计算协方差矩阵
Cov1 = np.mat(D.T) * np.mat(D) / (len(x) - 1)
print("协方差矩阵:\n", Cov1)
print("协方差矩阵的维数: \n", Cov1.shape)

# 计算特征值、特征向量
Cov2 = np.cov(D, rowvar=False)
eigVals, eigVects = np.linalg.eig(np.mat(Cov2))
print("特征值: \n", eigVals)
print("特征向量的矩阵: \n", eigVects)
print("特征向量的矩阵的维数: \n", eigVects.shape)

# 统计哪些是主成分
tot = sum(eigVals)
eigVals_exp = [(i / tot) for i in sorted(eigVals, reverse=False)]  # 特征值的贡献率
cum_eigVals_exp = np.cumsum(eigVals_exp)  # 累积特征值的贡献率

# 绘制特征值的贡献率与累计贡献率,用来判断主成分
plt.bar(range(0, 3), eigVals_exp, alpha=0.25, label='贡献率')
plt.plot(range(0, 3), cum_eigVals_exp, marker='o', label='累计贡献率')
plt.ylabel('贡献率')
plt.xlabel('index')
plt.title("特征值的贡献率与累计贡献率")
plt.legend()
plt.grid(True, linestyle='-.')
plt.show()

# 截取出主成分
eigVects_main = eigVects[:, 1:3]  # 截取所有行,前1~3-1共2列,截取的后两维数据作为主成分
print("截取的特征向量的矩阵: \n", eigVects_main)
print("截取的特征向量的矩阵的维数: \n", eigVects_main.shape)
#使用截取的矩阵来降维中心化的数据
D_main = np.matmul(D, eigVects_main)
print("降维的中心化的矩阵: \n", D_main)
print("降维的中心化的矩阵的维数: \n", D_main.shape)
#中心化,恢复最原始的数据
D0_main = D_main + D0_Av[1:3]
print("降维的原矩阵: \n", D0_main)
print("降维的原矩阵的维数: \n", D0_main.shape)

# 绘制降维对比图
ax = plt.subplot(111, projection='3d')
for i in range(0, 12):
    ax.scatter(D0[i][0], D0[i][1], D0[i][2], c='b')
for i in range(0, 12):
    ax.scatter(0, D0_main[i][0], D0[i][1], c='r')
plt.title("降维对比(三维)")
ax.set_zlabel('Z')
ax.set_ylabel('Y')
ax.set_xlabel('X')
plt.show()

原数据:
 [[ 9. 39.  7.]
 [16. 57. 58.]
 [25. 93. 73.]
 [14. 61.  1.]
 [10. 50.  0.]
 [18. 75. 55.]
 [ 0. 32. 72.]
 [16. 85. 87.]
 [ 5. 42.  4.]
 [19. 70. 10.]
 [16. 66. 86.]
 [20. 80. 30.]]
原数据的维数: 
 (12, 3)
均值: [14.   62.5  40.25]
中心化之后:
 [[ -5.   -23.5  -33.25]
 [  2.    -5.5   17.75]
 [ 11.    30.5   32.75]
 [  0.    -1.5  -39.25]
 [ -4.   -12.5  -40.25]
 [  4.    12.5   14.75]
 [-14.   -30.5   31.75]
 [  2.    22.5   46.75]
 [ -9.   -20.5  -36.25]
 [  5.     7.5  -30.25]
 [  2.     3.5   45.75]
 [  6.    17.5  -10.25]]
中心化数据的维数: 
 (12, 3)
协方差矩阵:
 [[  48.          122.54545455   57.81818182]
 [ 122.54545455  369.          273.59090909]
 [  57.81818182  273.59090909 1226.56818182]]
协方差矩阵的维数: 
 (3, 3)
特征值: 
 [   5.51753909  325.20159797 1312.84904476]
特征向量的矩阵: 
 [[-0.93960903  0.33471716  0.07140937]
 [ 0.34076353  0.89551555  0.28623787]
 [-0.03186053 -0.29328539  0.9554939 ]]
特征向量的矩阵的维数: 
 (3, 3)
截取的特征向量的矩阵: 
 [[ 0.33471716  0.07140937]
 [ 0.89551555  0.28623787]
 [-0.29328539  0.9554939 ]]
截取的特征向量的矩阵的维数: 
 (3, 2)
降维的中心化的矩阵: 
 [[-12.96646198 -38.85380876]
 [ -9.4617169   15.52852713]
 [ 21.3900165   40.80818307]
 [ 10.16817828 -37.93249223]
 [ -0.72807603 -42.32224013]
 [  8.2068535   17.95714577]
 [-41.31107572  20.60694514]
 [  7.10744216  51.25251039]
 [-10.7389278  -41.14721431]
 [ 17.26183552 -26.39985953]
 [ -9.6140679   44.85849703]
 [ 20.68600036  -4.35619357]]
降维的中心化的矩阵的维数: 
 (12, 2)
降维的原矩阵: 
 [[49.53353802  1.39619124]
 [53.0382831  55.77852713]
 [83.8900165  81.05818307]
 [72.66817828  2.31750777]
 [61.77192397 -2.07224013]
 [70.7068535  58.20714577]
 [21.18892428 60.85694514]
 [69.60744216 91.50251039]
 [51.7610722  -0.89721431]
 [79.76183552 13.85014047]
 [52.8859321  85.10849703]
 [83.18600036 35.89380643]]
降维的原矩阵的维数: 
 (12, 2)

image-20211201010157899

image-20211201010205946

image-20211201010216074

image-20211201010225457