主成分分析(PCA)

一、PCA简介

主成分分(Principal Component Analysis,PCA), 是一种统计方法。通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量,转换后的这组变量叫主成分。

二、PCA提出的背景

在许多领域的研究与应用中,往往需要对反映事物的多个变量进行大量的观测,收集大量数据以便进行分析寻找规律。多变量大样本无疑会为研究和应用提供了丰富的信息,但也在一定程度上增加了数据采集的工作量,更重要的是在多数情况下,许多变量之间可能存在相关性,从而增加了问题分析的复杂性,同时对分析带来不便。如果分别对每个指标进行分析,分析往往是孤立的,而不是综合的。盲目减少指标会损失很多信息,容易产生错误的结论。

因此需要找到一个合理的方法,在减少需要分析的指标同时,尽量减少原指标包含信息的损失,以达到对所收集数据进行全面分析的目的。由于各变量间存在一定的相关关系,因此有可能用较少的综合指标分别综合存在于各变量中的各类信息。主成分分析与因子分析就属于这类降维的方法。

三、PCA的推导

主成分的意思就是数据的主要成分,对于给定的一些数据,我们一般来说关心的是数据中变化的部分,变化的越多,我们得到的信息也就越多,而数据中不变的地方,我们能收集到的信息非常有限,所以,数据中变化较多的部分就构成了数据的主成分。

为了说明什么是数据的主成分,先从数据降维说起。数据降维是怎么回事儿?假设三维空间中有一系列点,这些点分布在一个过原点的斜面上,如果你用自然坐标系x,y,z这三个轴来表示这组数据的话,需要使用三个维度,而事实上,这些点的分布仅仅是在一个二维的平面上,那么,问题出在哪里?如果你再仔细想想,能不能把x,y,z坐标系旋转一下,使数据所在平面与x,y平面重合?这就对了!如果把旋转后的坐标系记为x’,y’,z’,那么这组数据的表示只用x’和y’两个维度表示即可!当然了,如果想恢复原来的表示方式,那就得把这两个坐标之间的变换矩阵存下来。这样就能把数据维度降下来了!但是,我们要看到这个过程的本质,如果把这些数据按行或者按列排成一个矩阵,那么这个矩阵的秩就是2!这些数据之间是有相关性的,这些数据构成的过原点的向量的最大线性无关组包含2个向量,这就是为什么一开始就假设平面过原点的原因!那么如果平面不过原点呢?这就是数据中心化的缘故!将坐标原点平移到数据中心,这样原本不相关的数据在这个新坐标系中就有相关性了!有趣的是,三点一定共面,也就是说三维空间中任意三点中心化后都是线性相关的,一般来讲n维空间中的n个点一定能在一个n-1维子空间中分析!

上一段文字中,认为把数据降维后并没有丢弃任何东西,因为这些数据在平面以外的第三个维度的分量都为0。现在,假设这些数据在z’轴有一个很小的抖动,那么我们仍然用上述的二维表示这些数据,理由是我们可以认为这两个轴的信息是数据的主成分,而这些信息对于我们的分析已经足够了,z’轴上的抖动很有可能是噪声,也就是说本来这组数据是有相关性的,噪声的引入,导致了数据不完全相关,但是,这些数据在z’轴上的分布与原点构成的夹角非常小,也就是说在z’轴上有很大的相关性,综合这些考虑,就可以认为数据在x’,y’ 轴上的投影构成了数据的主成分。

PCA的思想是将n维特征映射到k维上(k<n),这k维是全新的正交特征。这k维特征称为主成分,是重新构造出来的k维特征,而不是简单地从n维特征中去除其余n-k维特征。

PCA的主要数学推导,参考下面的一篇文章:

点击打开链接

总结一下,做PCA的主要步骤如下:

设有m条n维数据。

1)将原始数据按照m行n列排列的,如matlab和Python中都是这样(每一列代表一个特征)

2)将X的每一列(代表一个属性字段)进行零均值化,即减去这一列的均值

3)求出协方差矩阵

4)求出协方差矩阵的特征值及对应的特征向量

5)将上一步得到的特征向量组成矩阵V

6)求Y=XV,再取Y的前k列得到Y'即为要求的矩阵


这里注释一下以上矩阵的维数便于理解,在编程时防止矩阵相乘时的维数错误:

矩阵X:m x n (n维m个数据)

矩阵V:n x n

矩阵Y:m x n(n维m个数据)

矩阵Y':m x k(k维m个数据)

四、PCA的算法仿真

1.使用matlab中自带的pca函数
代码如下:
k=2;                            %将样本降到k维参数设置  
X=[1 2 1 1;                     %样本矩阵  
      3 3 1 2;   
      3 5 4 3;   
      5 4 5 4;  
      5 6 1 5;   
      6 5 2 6;  
      8 7 1 2;  
      9 8 3 7];  
[COEFF,SCORE,latent]=pca(X)
pcaData1=SCORE(:,1:k)

结果:

COEFF =

    0.7084   -0.2826   -0.2766   -0.5846
    0.5157   -0.2114   -0.1776    0.8111
    0.0894    0.7882   -0.6086    0.0153
    0.4735    0.5041    0.7222   -0.0116


SCORE =

   -5.7947   -0.6071    0.4140   -0.0823
   -3.3886   -0.8795    0.4054   -0.4519
   -1.6155    1.5665   -1.0535    1.2047
   -0.1513    2.5051   -1.3157   -0.7718
    0.9958   -0.5665    1.4859    0.7775
    1.7515    0.6546    1.5004   -0.6144
    2.2162   -3.1381   -1.6879   -0.1305
    5.9867    0.4650    0.2514    0.0689


latent =

   13.2151
    2.9550
    1.5069
    0.4660


pcaData1 =

   -5.7947   -0.6071
   -3.3886   -0.8795
   -1.6155    1.5665
   -0.1513    2.5051
    0.9958   -0.5665
    1.7515    0.6546
    2.2162   -3.1381
    5.9867    0.4650
其中,SCORE是按照主成分的大小排列成的列矩阵,LATENT为各个主成分的系数,pcaData1为提取出来的前两个主成分


2.在matlab中自己编写pca函数

方法已经解释过了,代码如下:

k=2;                            %将样本降到k维参数设置  
X=[1 2 1 1;                     %样本矩阵  
      3 3 1 2;   
      3 5 4 3;   
      5 4 5 4;  
      5 6 1 5;   
      6 5 2 6;  
      8 7 1 2;  
      9 8 3 7];  
[Row, Col]=size(X);  
covX=cov(X);                                  %求样本的协方差矩阵(散步矩阵除以(n-1)即为协方差矩阵)  
[V, D]=eigs(covX);                            %求协方差矩阵的特征值D和特征向量V  
meanX=mean(X);                                %样本均值m  
tempX= repmat(meanX,Row,1);                   %所有样本X减去样本均值m,再乘以协方差矩阵(散步矩阵)的特征向量V,即为样本的主成份SCORE                          
SCORE2=(X-tempX)*fliplr(V)                    %主成份:SCORE2,V矩阵是按照特征值从小到大的顺序排列的,所以需要翻转一下 
pcaData2=SCORE2(:,1:k)
结果如下:
SCORE2 =

   -5.7947    0.6071   -0.4140    0.0823
   -3.3886    0.8795   -0.4054    0.4519
   -1.6155   -1.5665    1.0535   -1.2047
   -0.1513   -2.5051    1.3157    0.7718
    0.9958    0.5665   -1.4859   -0.7775
    1.7515   -0.6546   -1.5004    0.6144
    2.2162    3.1381    1.6879    0.1305
    5.9867   -0.4650   -0.2514   -0.0689


pcaData2 =

   -5.7947    0.6071
   -3.3886    0.8795
   -1.6155   -1.5665
   -0.1513   -2.5051
    0.9958    0.5665
    1.7515   -0.6546
    2.2162    3.1381
    5.9867   -0.4650
可见于matlab内置的函数得到的结果是一致的


3.使用Python仿真pca
可以参考这个库函数的说明:
点击打开链接

文中说明了在sklearn.decomposition这个库中pca方法的使用以及例子

注意:在pca的很多算法中,都采用的SVD代替特征值法来求解对角矩阵那么SVD与PCA有什么关系呢?我们考虑\textbf{S}_X的SVD表示方式:\textbf{S}_X=\frac{1}{n-1} \textbf{V}\Sigma \textbf{U}^T\textbf{U}\Sigma\textbf{V}^T=\frac{1}{n-1}\textbf{V}\Sigma^2\textbf{V}^T,所以到这里答案就很明显了,我们只需要取另一个投影矩阵\textbf{P}=\textbf{V}^T就可以将\textbf{S}_Y对角化,即\textbf{V}的列是principal components。顺便,我们得到了一个副产品奇异值和特征值的关系:\lambda_i=\frac{1}{n-1}s_i^2,其中\lambda_i,s_i\textbf{S}_X\Sigma相应的特征值和奇异值。因此,我们得到了SVD是PCA的另一种algebraic formulation。而这也提供了另外一种算法来计算PCA,实际上,平时我就是用SVD定义的这套算法来做PCA的。因为很方便,计算一次就可以了。

上面为引用,链接:https://www.zhihu.com/question/38319536/answer/131029607

Python代码如下:

import numpy as np
from sklearn.decomposition import PCA
import sys
#returns choosing how many main factors
def index_lst(lst, component=0, rate=0):
  #component: numbers of main factors
  #rate: rate of sum(main factors)/sum(all factors)
  #rate range suggest: (0.8,1)
  #if you choose rate parameter, return index = 0 or less than len(lst)
  if component and rate:
    print('Component and rate must choose only one!')
    sys.exit(0)
  if not component and not rate:
    print('Invalid parameter for numbers of components!')
    sys.exit(0)
  elif component:
    print('Choosing by component, components are %s......'%component)
    return component
  else:
    print('Choosing by rate, rate is %s ......'%rate)
    for i in range(1, len(lst)):
      if sum(lst[:i])/sum(lst) >= rate:
        return i
    return 0
 
def main():
  # test data
  mat = [[1,2,1,1],[3,3,1,2],[3,5,4,3],[5,4,5,4],[5,6,1,5],[6,5,2,6],[8,7,1,2],[9,8,3,7]]
   
  # simple transform of test data
  Mat = np.array(mat, dtype='float64')
  print('Before PCA transforMation, data is:\n', Mat)
  print('\nMethod 1: PCA by original algorithm:')
  p,n = np.shape(Mat) # shape of Mat 
  t = np.mean(Mat, 0) # mean of each column
   
  # substract the mean of each column
  for i in range(p):
    for j in range(n):
      Mat[i,j] = float(Mat[i,j]-t[j])
       
  # covariance Matrix
  cov_Mat = np.dot(Mat.T, Mat)/(p-1)
   
  # PCA by original algorithm
  # eigvalues and eigenvectors of covariance Matrix with eigvalues descending
  U,V = np.linalg.eigh(cov_Mat) 
  # Rearrange the eigenvectors and eigenvalues
  U = U[::-1]
  for i in range(n):
    V[i,:] = V[i,:][::-1]
  # choose eigenvalue by component or rate, not both of them euqal to 0
  Index = index_lst(U, component=2) # choose how many main factors
  if Index:
    v = V[:,:Index] # subset of Unitary matrix
  else: # improper rate choice may return Index=0
    print('Invalid rate choice.\nPlease adjust the rate.')
    print('Rate distribute follows:')
    print([sum(U[:i])/sum(U) for i in range(1, len(U)+1)])
    sys.exit(0)
  # data transformation
  T1 = np.dot(Mat, v)
  # print the transformed data
  print('We choose %d main factors.'%Index)
  print('After PCA transformation, data becomes:\n',T1)
   
  # PCA by original algorithm using SVD
  print('\nMethod 2: PCA by original algorithm using SVD:')
  # u: Unitary matrix, eigenvectors in columns 
  # d: list of the singular values, sorted in descending order
  u,d,v = np.linalg.svd(cov_Mat)
  Index = index_lst(d, rate=0.95) # choose how many main factors
  T2 = np.dot(Mat, u[:,:Index]) # transformed data
  print('We choose %d main factors.'%Index)
  print('After PCA transformation, data becomes:\n',T2)
   
  # PCA by Scikit-learn
  pca = PCA(n_components=2) # n_components can be integer or float in (0,1)
  pca.fit(mat) # fit the model
  print('\nMethod 3: PCA by Scikit-learn:')
  print('After PCA transformation, data becomes:')
  print(pca.fit_transform(mat)) # transformed data   
main()
运行结果:
Before PCA transforMation, data is:
 [[ 1.  2.  1.  1.]
 [ 3.  3.  1.  2.]
 [ 3.  5.  4.  3.]
 [ 5.  4.  5.  4.]
 [ 5.  6.  1.  5.]
 [ 6.  5.  2.  6.]
 [ 8.  7.  1.  2.]
 [ 9.  8.  3.  7.]]

Method 1: PCA by original algorithm:
Choosing by component, components are 2......
We choose 2 main factors.
After PCA transformation, data becomes:
 [[ 5.79467821  0.60705487]
 [ 3.38863423  0.87952394]
 [ 1.61549833 -1.56652328]
 [ 0.15133075 -2.50507639]
 [-0.99576675  0.56654487]
 [-1.7515016  -0.65460481]
 [-2.21615035  3.13807448]
 [-5.98672282 -0.46499368]]

Method 2: PCA by original algorithm using SVD:
Choosing by rate, rate is 0.95 ......
We choose 3 main factors.
After PCA transformation, data becomes:
 [[ 5.79467821  0.60705487  0.41402357]
 [ 3.38863423  0.87952394  0.40538881]
 [ 1.61549833 -1.56652328 -1.05351894]
 [ 0.15133075 -2.50507639 -1.3156637 ]
 [-0.99576675  0.56654487  1.48593239]
 [-1.7515016  -0.65460481  1.50039959]
 [-2.21615035  3.13807448 -1.68793779]
 [-5.98672282 -0.46499368  0.25137607]]

Method 3: PCA by Scikit-learn:
After PCA transformation, data becomes:
[[-5.79467821  0.60705487]
 [-3.38863423  0.87952394]
 [-1.61549833 -1.56652328]
 [-0.15133075 -2.50507639]
 [ 0.99576675  0.56654487]
 [ 1.7515016  -0.65460481]
 [ 2.21615035  3.13807448]
 [ 5.98672282 -0.46499368]]

可见此结果与在matlab中的仿真相同

五、主成分分析的主要优缺点

优点

  ①可消除评估指标之间的相关影响。因为主成分分析法在对原始数据指标变量进行变换后形成了彼此相互独立的主成分,而且实践证明指标间相关程度越高,主成分分析效果越好。

  ②可减少指标选择的工作量,对于其他评估方法,由于难以消除评估指标间的相关影响,所以选择指标时要花费不少精力,而主成分分析法由于可以消除这种相关影响,所以在指标选择上相对容易些。

  ③主成分分析中各主成分是按方差大小依次排列顺序的,在分析问题时,可以舍弃一部分主成分,只取前面方差较大的几个主成分来代表原变量,从而减少了计算工作量。用主成分分析法作综合评估时,由于选择的原则是累计贡献率≥85%,不至于因为节省了工作量却把关键指标漏掉而影响评估结果。

缺点

  ①在主成分分析中,我们首先应保证所提取的前几个主成分的累计贡献率达到一个较高的水平(即变量降维后的信息量须保持在一个较高水平上),其次对这些被提取的主成分必须都能够给出符合实际背景和意义的解释(否则主成分将空有信息量而无实际含义)。

  ②主成分的解释其含义一般多少带有点模糊性,不像原始变量的含义那么清楚、确切,这是变量降维过程中不得不付出的代价。因此,提取的主成分个数m通常应明显小于原始变量个数p(除非p本身较小),否则维数降低的“利”可能抵不过主成分含义不如原始变量清楚的“弊”。

  ③当主成分的因子负荷的符号有正有负时,综合评价函数意义就不明确

猜你喜欢

转载自blog.csdn.net/sinat_38486449/article/details/79726287