从0自学数学建模算法日记1.4--评价决策类——主成分分析法(PCA)

本文介绍了数据降维中的PCA方法,包括内积和基的概念,如何通过PCA找到相关向量并进行降维,以及方差和协方差在降维过程中的作用。通过计算特征值、特征向量和贡献率来确定最佳降维维度,最后给出了Python代码示例。
摘要由CSDN通过智能技术生成

1.问题引出:

在生产衣服的过程中,衣服的尺寸要参考的指标很多,例如人的身高,体重,臂长,腰围,胸围等,但若考虑的因素过多,生产就会变得十分麻烦,因为不可能每一个指标都对应生产一套衣服。哪该如何做呢?便是通过数据降维。

2.数据降维:

事实上呢,这些数据是具有相关性的,例如一个人的身高越高,其体重也太小(正常情况下),换成更通俗的讲,在三维坐标中,有A,B,C三个点,这三个点形成一个平面记作P,在三维坐标中去表达这三个点,你要写出其对应的x,y,z轴坐标才能表示他,但事实上,若使用这三个点形成的平面P,便可以只用x,y去表示A,B,C三个点,这便是数据降维。

但若此时加上一个点D(不在P上),此时,若是将D用P去表示,则便会丢失一些数据,简单讲,若,P为x,y轴所围成的平面,若D不在P上,此时,讲D用P表示,则D所拥有的Z轴数据就丢失了。所以我们说,在降维的时候,总会带来一些数据的丢失,但由于在实际中数据总会有一些相关性,所以我们需要通过一些方法将丢失的数据尽量减小

3.PCA的基本原理:

1.知识铺垫:

内积定义:

内积定义便是为相同维数的A,B里相对应位置的数相乘的到的一个数,那在几何表示为什么呢?

若我们将B的模设为1,此时,在几何上便表示为A在B上的投影长度,这个十分有用,有了这个我们便可以表示,若将B看做X轴,A对应的X坐标,这十分的不错,有利于我们去降维,为什么呢?。

1.基的定义:

简单来说,基便在n维空间中n个不能相互表达的向量(线性无关),但其余向量都可用这n个向量来进行表示,例如在二维空间中(1,0),(0,1)便是基,其余的向量显然可以写成x*(1,0)+y*(0,1)来进行表示,当然在一个空间中基有无数个,但为了方便后面的操作,也我们统一将它们的摸都化为1。

降维操作:

知道了基的概念后,那么此时若我们在n维空间中,找到k个基,这k个基便形成了一个k维空间,我们将n维空间的向量都投影到k维空间中,便实现了降维!

所以要实现降维,首先第一点,找k个基向量,要求这k个基向量线性无关(最好是正交),那么接下来便是将其余向量投影到这个k个基向量所形成的空间K中,此时便利用到了前面说的内积,例如拿出k个基向量中的一个向量记作K1吧,我们要将一个向量L投影到K中,此时,K1*L就得到了在L向量在K中K1轴对于的坐标,此时,我们将L都乘上每一个基并且相加就能得到L在K空间中的投影了。

例:有一个三维空间P,向量C(2,3,2)

此时C在K空间的表达为,

那么我们要将其写成矩阵的形式

则m个向量降维到p维空间可以写成

2.方差:

这就要说到我们前面提过因为降维而引起的数据丢失,数据丢失我们无法避免,只能控制在最小,而一般来理解,当降维数据的越分散便可以尽可能的保留原始数据,而描述分散便需要用到方差。

方差是形容一组随机变量的离散程度,离散程度越大,则其的方差就越大

公式为

我们可以知道当一组数据的方差越大,离散程度就越大了,在直觉上这样保留的信息是最多的,则我们现在的任务就变成了:寻找一个基令原始数据向量在其上投影的方差最大(请记住每一个原始数据在该基上的投影是一个点)

但此时就有一个问题了,我们可以找到一个基,其可以令其原始数据向量的投影方差最大,那剩下的基呢?

我们可以这样理解,第二个基要保留除第一个基以外的数据,则原始数据在第二个基的投影与在第一个基的投影所得的数据线性无关,而这个时候我们去追求在第二个基上的投影方差最大就可以了,即可以最大限度的保留数据,又不会重复保留从而导致数据出问题。

而形容两组数据无关变需要用到协方差了

协方差便是用于形容两个随机变量的相关性,公式为

当协方差为0时两个随机变量无关,为什么呢?深刻的理解需要通过几何的方式来,这里只做简单的文字解释

我们知道当两个变量的增减性不相干时,我们便可以说它们之间无关,因为你并不能通过X的增加或则减少来预测Y的变化趋势,而当协方差大于0时,X与Y的趋势是更接近于相同的,小于则反过来,等于0的时候则我们便不能确定了(若想通过几何理解可以去看b站up马同学图解数学关于协方差的视频)

而我们知道若X与Y正交,其协方差自然为0,因此我们便引出了协方差矩阵.

但在介绍协方差矩阵之前,我们还得先对原始数据进行处理,减去每行的均值。

例:我们有5个2维向量减去每行减去其的均值

减去均值后

则我们的运算就变得方便了起来,因为减去均值后的我们的协方差公式就变成了

且减去均值并不会影响数据的准确,只是将每一个数据都平移了相同的距离而已。

3.协方差矩阵:

假设我们有一个原始数据矩阵X(减去了均值)

然后这个矩阵乘上其的转置,我们便得到了协方差矩阵

从图中我们可以看出,主对角线为每行的方差,而其余为协方差,我们可以轻易的证明推广到n维矩阵依旧成立,而我们所寻求的降维后的数据矩阵每行不相关,则我们可以知道其形成的协方差矩阵为对角型矩阵。

ok,前置知识以全部完成,接下来便是PCA能实现的原因了

2.PCA的实现:

还记得我们一开始的目的吗?寻找一个降维矩阵,但我们好像并没有提到过这降维矩阵的性质,而是一直在说降维后的数据矩阵的性质。我们从不能一个个试,把降维矩阵试出来吧?

肯定不可能,但要如何做呢?我们先假设Y=PX,C为X对应的协方差矩阵,D为Y对应的协方差矩阵,P为行向量组成的矩阵,则我们可以得出一个证明。

这有什么用呢?我们一定可以找到一个P矩阵,将X进行变换成Y,Y的各行无关,而由我们前面所提到的协防差的定义,D则为对角型矩阵,这是我们便应该意识到我们找到的矩阵P与我们一直所找的降维矩阵息息相关,事实上,它只相差一点,即方差,我们需要找到的P是能令对角元素从大到小排列的P,然后若我们要将N维降到K维,我们便可以知道,我们取P的前K列作为降维矩阵就可以了。

那我们如何找P呢?

我们知道C是实对称矩阵,而实对称矩阵有着十分良好的性质。

实对称矩阵一定存在一个正交矩阵P使其对角化,且对角化后的矩阵主对角线上的元素为其的特征值,而P由其特征值对应的特征向量组成,需要注意的是Λ中对角元素排序次序与P中列向量排列次序相对应,此时,我们便已经能够初步实现PCA了

因为还存在几个问题,数据之间的数值差异与要将空间降到几维

首先来说说数值差异,对于此我们要将原始数据矩阵标准化,再进行我们前面说的

 此项解释的链接https://blog.csdn.net/lvla_juan/article/details/97134946

若我们的原始矩阵由行向量组成

Sj为每列的标准差,\bar{x}为每列的均值。

标准差计算公式:

以及降维,若我们随意的选择降维的维数,则可能会导致数据的大量丢失或则降维的维数不够低,这个时候我们引入贡献率,即每一个基对于整体贡献了多少数据。

贡献率:

累计贡献率:

其中\lambda为标准化处理后且减去行均值的矩阵的特征值,而当累计贡献率超过%80时我们便认为其保留了大部分数据,至于为什么可以用特征值来衡量贡献率,我们可以用前面的所讲的来理解,即由于我们是从方差大对应的特征向量选到方差小的,越往下,保留的数据越少,即特征值越小,而对应贡献率越少,即贡献率的增长与特征值的增长可粗略的看成线性正相关(不知道对不对,凑合着理解)

由此我们就可以正式开始PCA的代码实现了

4.代码实现:

步骤:

1.将原始数据组成矩阵X(由行向量组成)

2.计算每列的均值与标准差,并标准化矩阵

3.将标准化矩阵每列减去对应的均值

4.计算得到的矩阵的特征值与特征向量

5.找到特征向量后得到P,通过计算累计贡献率来判断,取前几行作为降维矩阵

6.令Y=PX得到降维后的矩阵

import numpy as np
import pandas as pd
from scipy import linalg

#导入鸢尾花excel文件
df = pd.read_excel("iris.xlsx")
print(df)
#将表格中的数据转化为数组
x = df.to_numpy()

#求列均值
x_mean = np.mean(x,axis=0)


#求列标准差,ddof为求标准差的公式,标准差一共有两个公式,取1为我们需要的
x_std = np.std(x,ddof=1,axis=0)

#将x标准化
X = (x-x_mean)/x_std

#计算X的协方差矩阵
C = np.cov(X.T)

#计算C的特征值和特征向量,使用linalg,eigh,自动将特征值从小到大排列
eigenvalue,eigenvectors = linalg.eigh(C)
print(eigenvalue)

#由于使用linalg,eigh自动将特征值从小到大排列,所以我们需要反转一下
eigenvalue=eigenvalue[::-1]
eigenvectors=eigenvectors[:,::-1]

#计算贡献率
contribution_rate = eigenvalue/sum(eigenvalue)

#np.cumsum,累计求和用于求累计贡献率
cum_con_rate = np.cumsum(contribution_rate)

print("特征值为:")
print(eigenvalue)
print("特征向量为:")
print(eigenvectors)
print("贡献率为:")
print(contribution_rate)
print("累计贡献率为:")
print(cum_con_rate)

代码实现部分并不算难,但隔了两个星期才更新呢,因为摆了,说实话,我是一个三分钟热度的人,很容易就间歇性努力,持续性摆烂,但这次我希望我能坚持下去,去参加比赛,也并不是说非要拿奖,但起码我想让我的大学生活能不在颓废中度过

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值