jjzjj

PCA(主成分分析法)的Python代码实现(numpy,sklearn)

Cloudy_Nebula 2023-04-19 原文

PCA(主成分分析法)的Python代码实现(numpy,sklearn)

语言描述

PCA设法将原来众多具有一定相关性的属性(比如p个属性),重新组合成一组相互无关的综合属性来代替原属性。通常数学上的处理就是将原来p个属性做线性组合,作为新的综合属性。

PCA 中的线性变换等价于坐标变换,变换的目的是使 n n n 个样本点在新坐标轴 y 1 y_1 y1 上的离散程度(方差)最大,这样变量 y 1 y_1 y1 就代表了原始数据的绝大部分信息,即使忽略 y 2 y_2 y2 也无损大局,从而把两个指标压缩成一个指标。从几何上看,找主成分的问题就是找出 N N N 维空间中椭球体的主轴问题。从数学上也可以证明,它们分别是相关矩阵的 k k k 个较大的特征值所对应的特征向量。

算法描述

输入: n n n m m m ( n × m ) (n \times m) (n×m) 样本集 X = ( x 1 , x 2 , ⋯   , x m ) X = (x_1,x_2,\cdots,x_m) X=(x1,x2,,xm),低维空间维数 k k k

主成分的计算步骤如下:

  1. 对所有样本进行中心化: x i ← x i − 1 n ∑ i = 1 n x i x_i \leftarrow x_i-\frac{1}{n}\sum_{i=1}^{n}x_i xixin1i=1nxi

  2. 计算样本的协方差矩阵 X T X X^TX XTX ( m × m ) (m \times m) (m×m)

  3. 计算特征值与特征向量

    • 解特征方程 ∣ λ E − X T X ∣ = 0 |\lambda E-X^TX|=0 λEXTX=0,常用雅可比 ( Jacobi ) 法求出特征值,并使其按大小顺序排列,即 λ 1 ≥ λ 2 ≥ ⋯ ≥ λ m ≥ 0 \lambda_1 \geq \lambda_2 \geq \cdots \geq \lambda_m \geq 0 λ1λ2λm0
    • 分别求出对应于特征值 λ i \lambda_i λi 的特征向量 e i ( i = 1 , 2 , ⋯   , m ) e_i(i=1,2,\cdots,m) ei(i=1,2,,m),要求 ∣ ∣ e i ∣ ∣ = 1 ||e_i|| = 1 ∣∣ei∣∣=1,即 ∑ j = 1 m e i j 2 = 1 \sum_{j=1}^{m}e_{ij}^2=1 j=1meij2=1,其中 e i j e_{ij} eij 表示向量 e i e_i ei 的第 j j j 个分量。
    • 计算主成分贡献率及累计贡献率。
      • 贡献率的公式: f i = λ i ∑ i = 1 m λ i f_i=\frac{\lambda_i}{\sum_{i=1}^{m}\lambda_i} fi=i=1mλiλi
      • 累计贡献率: α k = ∑ i = 1 k f i \alpha_k=\sum_{i=1}^{k}f_i αk=i=1kfi
      • 一般取累计贡献率达 85% ~ 95% 的特征值 λ 1 , λ 2 , ⋯   , λ l \lambda_1,\lambda_2,\cdots,\lambda_l λ1,λ2,,λl 所对应的第一、第二、第 l ( l ≤ m ) l(l \leq m) l(lm)个主成分。
  4. 计算主成分值

    k k k 个主成分值 z = ( X e 1 , X e 2 , ⋯   , X e k ) = ( z 1 , z 2 , ⋯   , z k ) z = (Xe_1,Xe_2,\cdots,Xe_k)=(z_1,z_2,\cdots,z_k) z=(Xe1,Xe2,,Xek)=(z1,z2,,zk) ( n × k  即  n  个  k  维 ) (n \times k\ 即\ n\ 个\ k\ 维) (n×k  n  k )

与通过保留原属性集的一个子集来减少属性集的大小不同,PCA通过创建一个能替换的、较小的变量集“组合”属性的基本要素。原数据可以投影到较小的集合中。PCA常常能够揭示先前未被察觉的联系,并允许解释不寻常的结果。

示例

学号语文数学物理化学英语历史
1846561727981
2647777765570
3656763495767
4748069756374
5847470807482

1 使用numpy降维

>>> import numpy as np

# 输入待降维数据 (5 * 6) 矩阵,6个维度,5个样本值
>>> A = np.array([[84,65,61,72,79,81],[64,77,77,76,55,70],[65,67,63,49,57,67],[74,80,69,75,63,74],[84,74,70,80,74,82]])
>>> print(A)
[[84 65 61 72 79 81]
 [64 77 77 76 55 70]
 [65 67 63 49 57 67]
 [74 80 69 75 63 74]
 [84 74 70 80 74 82]]

# 对每一个属性的样本求均值
>>> MEAN = np.mean(A, axis=0) # 沿轴0调用mean函数
>>> print(MEAN)
[74.2 72.6 68.  70.4 65.6 74.8]

# 去中心化
>>> X = np.subtract(A, MEAN)
>>> print(X)
[[  9.8  -7.6  -7.    1.6  13.4   6.2]
 [-10.2   4.4   9.    5.6 -10.6  -4.8]
 [ -9.2  -5.6  -5.  -21.4  -8.6  -7.8]
 [ -0.2   7.4   1.    4.6  -2.6  -0.8]
 [  9.8   1.4   2.    9.6   8.4   7.2]]
>>> print(X.T) #矩阵的转置
[[  9.8 -10.2  -9.2  -0.2   9.8]
 [ -7.6   4.4  -5.6   7.4   1.4]
 [ -7.    9.   -5.    1.    2. ]
 [  1.6   5.6 -21.4   4.6   9.6]
 [ 13.4 -10.6  -8.6  -2.6   8.4]
 [  6.2  -4.8  -7.8  -0.8   7.2]]
 
# 计算协方差矩阵
>>> COV = np.dot(X.T, X)
>>> print(COV)
[[ 380.8  -55.6  -95.   248.6  401.4  252.2]
 [ -55.6  165.2  131.   179.8 -107.8  -20.4]
 [ -95.   131.   160.   170.  -132.   -34. ]
 [ 248.6  179.8  170.   605.2  214.8  215.4]
 [ 401.4 -107.8 -132.   214.8  443.2  263.6]
 [ 252.2  -20.4  -34.   215.4  263.6  174.8]]
 
# 计算特征值和特征向量
>>> W, V = np.linalg.eig(COV)
>>> print(W) # 特征值
[1.22517276e+03 6.54041238e+02 3.95721181e+01 1.04138814e+01
 1.50877843e-14 5.51899893e-14]
>>> print(V) # 特征向量
[[-0.53264253  0.20279107 -0.34433806  0.39437042 -0.61869481 -0.55543331]
 [ 0.00876193 -0.46059524 -0.81597078  0.02185232  0.25842516  0.34848844]
 [ 0.04593605 -0.47328385  0.37877077  0.70892582 -0.03144886  0.21014772]
 [-0.51955599 -0.64238594  0.24891406 -0.45230979 -0.15412561 -0.22434743]
 [-0.55131936  0.32775478  0.09651389 -0.13044526  0.29446728  0.67491022]
 [-0.37445103  0.05145202  0.0297077   0.34614812  0.66255449  0.14160509]]
 
# 计算主成分贡献率以及累计贡献率
>>> sum_lambda = np.sum(W) # 特征值的和
>>> print(sum_lambda)
1929.1999999999994
>>>f = np.divide(W, sum_lambda) # 每个特征值的贡献率(特征值 / 总和)
>>> print(f)
[6.35067780e-01 3.39021998e-01 2.05121906e-02 5.39803100e-03
 7.82074656e-18 2.86077075e-17]
>>> f[0]+f[1] # 前两大的特征值的累计贡献率
0.974089778403108
>>> f[0]+f[1]+f[2] # 前三大的特征值的累计贡献率
0.9946019690025047
# 0.97 > 0.85,只需要选取前两大特征值即可,以从6维降到2维
# 前两大特征值对应的特征向量为:
>>> e1 = V.T[0]
>>> print(e1)
[-0.53264253  0.00876193  0.04593605 -0.51955599 -0.55131936 -0.37445103]
>>> e2 = V.T[1]
>>> print(e2)
[ 0.20279107 -0.46059524 -0.47328385 -0.64238594  0.32775478  0.05145202]

# 计算主成分值(已去中心化)
>>> z1 = np.dot(X, e1)
>>> print(z1)
[-16.14860528  10.61676743  23.40212697  -0.43966353 -17.43062559]
>>> z2 = np.dot(X, e2)
>>> print(z2)
[ 12.48396235 -15.67317428  13.607117    -7.77054621  -2.64735885]

# 输出降维后的结果(已去中心化)
>>> RES = np.array([z1,z2])
>>> print(RES)
[[-16.14860528  10.61676743  23.40212697  -0.43966353 -17.43062559]
 [ 12.48396235 -15.67317428  13.607117    -7.77054621  -2.64735885]]
>>> print(RES.T)
[[-16.14860528  12.48396235]
 [ 10.61676743 -15.67317428]
 [ 23.40212697  13.607117  ]
 [ -0.43966353  -7.77054621]
 [-17.43062559  -2.64735885]]

2 直接使用sklearn中的PCA进行降维

>>> import numpy as np
>>> from sklearn.decomposition import PCA

# 输入待降维数据 (5 * 6) 矩阵,6个维度,5个样本值
>>> A = np.array([[84,65,61,72,79,81],[64,77,77,76,55,70],[65,67,63,49,57,67],[74,80,69,75,63,74],[84,74,70,80,74,82]])
>>> print(A)
[[84 65 61 72 79 81]
 [64 77 77 76 55 70]
 [65 67 63 49 57 67]
 [74 80 69 75 63 74]
 [84 74 70 80 74 82]]
 
# 直接使用PCA进行降维
>>> pca = PCA(n_components=2) #降到 2 维
>>> pca.fit(A)
PCA(n_components=2)
>>> pca.transform(A) # 降维后的结果
array([[-16.14860528, -12.48396235],
       [ 10.61676743,  15.67317428],
       [ 23.40212697, -13.607117  ],
       [ -0.43966353,   7.77054621],
       [-17.43062559,   2.64735885]])
>>> pca.explained_variance_ratio_ # 降维后的各主成分的方差值占总方差值的比例,即方差贡献率
array([0.63506778, 0.339022  ])
>>> pca.explained_variance_ # 降维后的各主成分的方差值
array([306.29319053, 163.51030959])

有关PCA(主成分分析法)的Python代码实现(numpy,sklearn)的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  4. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  5. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  6. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  7. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  8. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  9. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  10. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

随机推荐