13

我试图matplotlib.mlab.PCA用类的属性做一个简单的主成分分析,但我无法为我的问题找到一个干净的解决方案。这是一个例子:

获取一些 2D 虚拟数据并启动 PCA:

from matplotlib.mlab import PCA
import numpy as np

N     = 1000
xTrue = np.linspace(0,1000,N)
yTrue = 3*xTrue

xData = xTrue + np.random.normal(0, 100, N)
yData = yTrue + np.random.normal(0, 100, N)
xData = np.reshape(xData, (N, 1))
yData = np.reshape(yData, (N, 1))
data  = np.hstack((xData, yData))
test2PCA = PCA(data)

现在,我只想将主成分作为原始坐标中的向量,并将它们作为箭头绘制到我的数据上。

什么是到达那里的快速和干净的方式?

谢谢,泰拉克斯

4

2 回答 2

29

我认为这mlab.PCA门课不适合你想做的事情。特别是,PCA该类在找到特征向量之前重新调整数据:

a = self.center(a)
U, s, Vh = np.linalg.svd(a, full_matrices=False)

center方法除以sigma

def center(self, x):
    'center the data using the mean and sigma from training set a'
    return (x - self.mu)/self.sigma

这导致特征向量 ,pca.Wt,如下所示:

[[-0.70710678 -0.70710678]
 [-0.70710678  0.70710678]]

它们是垂直的,但与原始数据的主轴没有直接关系。它们是关于按摩数据的主轴。

也许直接编写你想要的代码可能更容易(不使用mlab.PCA类):

import numpy as np
import matplotlib.pyplot as plt

N = 1000
xTrue = np.linspace(0, 1000, N)
yTrue = 3 * xTrue
xData = xTrue + np.random.normal(0, 100, N)
yData = yTrue + np.random.normal(0, 100, N)
xData = np.reshape(xData, (N, 1))
yData = np.reshape(yData, (N, 1))
data = np.hstack((xData, yData))

mu = data.mean(axis=0)
data = data - mu
# data = (data - mu)/data.std(axis=0)  # Uncommenting this reproduces mlab.PCA results
eigenvectors, eigenvalues, V = np.linalg.svd(data.T, full_matrices=False)
projected_data = np.dot(data, eigenvectors)
sigma = projected_data.std(axis=0).mean()
print(eigenvectors)

fig, ax = plt.subplots()
ax.scatter(xData, yData)
for axis in eigenvectors:
    start, end = mu, mu + sigma * axis
    ax.annotate(
        '', xy=end, xycoords='data',
        xytext=start, textcoords='data',
        arrowprops=dict(facecolor='red', width=2.0))
ax.set_aspect('equal')
plt.show()

在此处输入图像描述

于 2013-08-18T13:56:30.640 回答
1

请注意,matplotlib.mlab.PCA在 3.1 中删除

下面是三种可选的 PCA 实现,一种基于最后一种matplotlib.mlab.PCA 实现,一种基于unutbu 的回答,一种基于doug 对另一个问题的回答

前两种使用奇异值分解(svd)来获得特征值和特征向量,后者使用协方差矩阵(cov)方法。

在这里svd可以找到对和cov方法之间关系的精彩解释。

为了便于比较,对实现进行了简化和重构。

def pca_svd(data):
    """ based on matplotlib.mlab.PCA with standardize=False """
    data -= data.mean(axis=0)
    __, singular_values, eigenvectors_transposed = numpy.linalg.svd(
        data, full_matrices=False)
    eigenvalues = singular_values ** 2 / (data.shape[0] - 1)
    eigenvectors = eigenvectors_transposed.T
    transformed_data = numpy.dot(data, eigenvectors)
    return transformed_data, eigenvalues, eigenvectors


def pca_svd_transposed(data):
    """ based on unutbu's answer """
    data -= data.mean(axis=0)
    eigenvectors, singular_values, __ = numpy.linalg.svd(
        data.T, full_matrices=False)  # note data transposed
    eigenvalues = singular_values ** 2 / (data.shape[0] - 1)
    transformed_data = numpy.dot(data, eigenvectors)
    return transformed_data, eigenvalues, eigenvectors
    
    
def pca_cov(data):
    """ based on doug's answer """
    data -= data.mean(axis=0)
    covariance_matrix = numpy.cov(data, rowvar=False)
    eigenvalues, eigenvectors = scipy.linalg.eigh(covariance_matrix)
    decreasing_order = numpy.argsort(eigenvalues)[::-1]
    eigenvalues = eigenvalues[decreasing_order]
    eigenvectors = eigenvectors[:, decreasing_order]
    transformed_data = numpy.dot(data, eigenvectors)
    return transformed_data, eigenvalues, eigenvectors

eigenvalues表示数据沿主轴的方差,即 的方差transformed_data

计时使用timeit在我的系统上显示以下内容:

array shape:  (15000, 4)
iterations:  1000
pca_svd_transposed: 4.32 s (average 4.32 ms)
pca_svd:            1.87 s (average 1.87 ms)
pca_cov:            1.41 s (average 1.41 ms)

请注意,svd对于这种数组形状,转置输入数组的速度相对较慢。

于 2021-06-24T20:45:52.820 回答