引入

主成分分析(PCA)是一种常见的数据分析方法,通过该方法我们可以对数据进行降维操作,并且保留方差较大(信息量大)的维度。
为了引入相关概念,我们先看一组数据:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# 总共十个样本 每个样本有两个特征值
data = pd.read_csv('principal.csv') # 该数据文件可自己准备
data

java根据主成分分析法得到指标权重 主成分分析的代码_机器学习


该数据中有10个样本,每个样本有两个特征值。为了更为直观,我们把该数据画成散点图呈现出来:

plt.xlim((-3, 3))
plt.ylim((-3, 3))
plt.scatter(x, y)

java根据主成分分析法得到指标权重 主成分分析的代码_大数据_02


数据呈现二维分布,现要求通过主成分分析提取出方差最大的一个维度。

实现过程与理论分析

首先可以看到数据点分布的很不规律,我们要对数据进行中心化操作,使得样本点具有一定相关性。

# 首先中心化
x_mean = np.mean(x)
y_mean = np.mean(y)
x_n = x - x_mean
y_n = y - y_mean
# 中心化结果
plt.xlim((-3, 3))
plt.ylim((-3, 3))
plt.scatter(x_n, y_n)

java根据主成分分析法得到指标权重 主成分分析的代码_数据分析_03


结果展示如图所示,可以看出样本点围绕原点分布。

其次,我们要计算出协方差矩阵(对角矩阵)。

# 竖直方向上合并为两行
# 计算出每个维度上的协方差对角矩阵
martix = np.cov(np.vstack((x_n.reshape(1, -1), y_n.reshape(1, -1))))
martix

结果为:

array([[0.61655556, 0.61544444],
[0.61544444, 0.71655556]])

由于每个样本有2个特征取值,所以协方差矩阵为2*2的。对角线上分别是x和y的方差,非对角线上是协方差。协方差大于0表示x和y若有一个增,另一个也增;小于0表示一个增,一个减;协方差为0时,两者独立。协方差绝对值越大,两者对彼此的影响越大,反之越小。

如果样本是三维的,那么协方差矩阵为:(可以看出协方差矩阵属于对称矩阵)

java根据主成分分析法得到指标权重 主成分分析的代码_java根据主成分分析法得到指标权重_04


第三,求协方差矩阵的特征值、特征向量。

# 求协方差矩阵的特征值、特征向量
lamt = np.linalg.eig(martix)
lamt

结果为:

(array([0.0490834 , 1.28402771]), array([[-0.73517866, -0.6778734 ],
[ 0.6778734 , -0.73517866]]))

可分别表示为:

java根据主成分分析法得到指标权重 主成分分析的代码_主成分分析_05


上面是两个特征值,下面是对应的特征向量,特征值0.0490833989对应特征向量为

java根据主成分分析法得到指标权重 主成分分析的代码_java根据主成分分析法得到指标权重_06


这里的特征向量都归一化为单位向量。

特征值与特征向量到底代表了什么??
虽然我系统学习了线性代数,但是印象中课本上并没有详细讲述特征值与特征向量的实际意义。
首先我们回顾一下相关概念:对一个n*n的实对称矩阵进行分解,我们能够求出它的特征值和特征向量,就会产生n个n维的正交基(在特征值不同的情况下),每一个正交基会相应一个特征值。

然后把矩阵投影到这N个基上,此时特征值的模就表示矩阵在该基的投影长度,也是方差的大小。

特征值越大。说明矩阵在相应的特征向量上的方差越大。样本点越离散。越easy区分,信息量也就越多。因此。特征值最大的相应的特征向量方向上所包括的信息量就越多,假设某几个特征值非常小。那么就说明在该方向的信息量非常少,我们就能够删除小特征值相应方向的数据,仅仅保留大特征值方向相应的数据,这样做以后数据量减小。但实用的信息量都保留下来了。PCA就是这个原理。

第四,将特征值按照从大到小的顺序排序,选择其中最大的k个,然后将其对应的k个特征向量分别作为列向量组成特征向量矩阵。这里特征值只有两个,我们选择其中最大的那个.

即保留最重要的k个特征(通常k要小于n)。也能够自己制定。也能够选择一个阈值,然后通过前k个特征值之和减去后面n-k个特征值之和大于这个阈值,则选择这个k。

最后进行投影,将样本数据与特征值相乘,即投影在选择的较大特征值所对应的特征向量上。

data_n = np.hstack((x_n.reshape(1, -1).T, y_n.reshape(1, -1).T))

final_x = np.dot(data_n, max_)

final_x

结果为:

array([[-0.82797019],
[ 1.77758033],
[-0.99219749],
[-0.27421042],
[-1.67580142],
[-0.9129491 ],
[ 0.09910944],
[ 1.14457216],
[ 0.43804614],
[ 1.22382056]])

这是投影过后的一维数据(方差最大),且k=1.如若k=2,则对于方差较小的数据投影如下:

final_y = np.dot(data_n, lamt[1][: , 0].reshape(1, -1).T)
final_y

结果如下:

array([[-0.17511531],
[ 0.14285723],
[ 0.38437499],
[ 0.13041721],
[-0.20949846],
[ 0.17528244],
[-0.3498247 ],
[ 0.04641726],
[ 0.01776463],
[-0.16267529]])

我们把这两类数据再次画在坐标轴上。

final = np.hstack((final_x, final_y))
plt.xlim((-3, 3))
plt.ylim((-3, 3))
plt.scatter(final[: , 0], final[: , 1])

结果如下:

java根据主成分分析法得到指标权重 主成分分析的代码_主成分分析_07


图中的x轴、y轴实际上是上面计算出的两个特征向量!并且我们可以明显看出投影在x轴(方差最大的特征向量)上,数据分散程度较高,更能获取有用信息,也就是我们所需要的主成分!