Analyse en composantes principales (PCA) en Python

J'ai un tableau (26424 x 144) et je veux effectuer PCA dessus en utilisant Python. Cependant, il n'y a pas de place particulière sur le web qui explique comment réaliser cette tâche (il y a des sites qui font juste PCA selon leur propre - il n'y a pas de façon généralisée de le faire que je peux trouver). N'importe qui avec n'importe quelle sorte d'aide fera grand.

54
demandé sur seralouk 2012-11-05 04:10:11

9 réponses

Vous pouvez trouver une fonction PCA dans le module matplotlib:

import numpy as np
from matplotlib.mlab import PCA

data = np.array(np.random.randint(10,size=(10,3)))
results = PCA(data)

Les résultats stockeront les différents paramètres de la PCA. Il provient de la partie mlab de matplotlib, qui est la couche de compatibilité avec la syntaxe MATLAB

Modifier: sur le blog nextgenetics j'ai trouvé une merveilleuse démonstration de la façon d'effectuer et d'afficher un PCA avec le module matplotlib mlab, amusez-vous et vérifiez ce blog!

45
répondu EnricoGiampieri 2017-07-21 09:20:34

J'ai posté ma réponse même si une autre réponse a déjà été acceptée; la réponse acceptée repose sur une fonction obsolète ; de plus, Cette fonction obsolète est basée sur décomposition de la valeur singulière (SVD), qui (bien que parfaitement valide) est la plus gourmande en mémoire et en Processeur des deux techniques générales de calcul de PCA. Ceci est particulièrement pertinent ici en raison de la taille du tableau de données dans L'OP. en utilisant PCA basé sur la covariance, le tableau utilisé dans le flux de calcul est juste 144 x 144 , plutôt que 26424 x 144 (Les dimensions du tableau de données d'origine).

Voici une implémentation de travail simple de PCA en utilisant le linalg module de SciPy . Comme cette implémentation calcule d'abord la matrice de covariance, puis effectue tous les calculs suivants sur ce tableau, elle utilise beaucoup moins de mémoire que le PCA basé sur SVD.

(le module linalg dans NumPy peut également être utilisé sans changement dans le code ci-dessous en dehors de l'instruction import, qui serait de numpy import linalg as LA.)

Les deux étapes clés de cette mise en œuvre de L'APC sont:

  • Le Calcul de la matrice de covariance; et

  • La Prise de la eivenvectors & les valeurs propres de cette cov de la matrice

Dans la fonction ci-dessous, le paramètre dims_rescaled_data fait référence au nombre de dimensions souhaité dans la matrice de donnéesredimensionnée ; ce paramètre a une valeur par défaut de seulement deux dimensions, mais le code ci-dessous n'est pas limité à deux, mais il peut être n'importe quelle valeur inférieure au numéro de colonne du tableau de données d'origine.


def PCA(data, dims_rescaled_data=2):
    """
    returns: data transformed in 2 dims/columns + regenerated original data
    pass in: data as 2D NumPy array
    """
    import numpy as NP
    from scipy import linalg as LA
    m, n = data.shape
    # mean center the data
    data -= data.mean(axis=0)
    # calculate the covariance matrix
    R = NP.cov(data, rowvar=False)
    # calculate eigenvectors & eigenvalues of the covariance matrix
    # use 'eigh' rather than 'eig' since R is symmetric, 
    # the performance gain is substantial
    evals, evecs = LA.eigh(R)
    # sort eigenvalue in decreasing order
    idx = NP.argsort(evals)[::-1]
    evecs = evecs[:,idx]
    # sort eigenvectors according to same index
    evals = evals[idx]
    # select the first n eigenvectors (n is desired dimension
    # of rescaled data array, or dims_rescaled_data)
    evecs = evecs[:, :dims_rescaled_data]
    # carry out the transformation on the data using eigenvectors
    # and return the re-scaled data, eigenvalues, and eigenvectors
    return NP.dot(evecs.T, data.T).T, evals, evecs

def test_PCA(data, dims_rescaled_data=2):
    '''
    test by attempting to recover original data array from
    the eigenvectors of its covariance matrix & comparing that
    'recovered' array with the original data
    '''
    _ , _ , eigenvectors = PCA(data, dim_rescaled_data=2)
    data_recovered = NP.dot(eigenvectors, m).T
    data_recovered += data_recovered.mean(axis=0)
    assert NP.allclose(data, data_recovered)


def plot_pca(data):
    from matplotlib import pyplot as MPL
    clr1 =  '#2026B2'
    fig = MPL.figure()
    ax1 = fig.add_subplot(111)
    data_resc, data_orig = PCA(data)
    ax1.plot(data_resc[:, 0], data_resc[:, 1], '.', mfc=clr1, mec=clr1)
    MPL.show()

>>> # iris, probably the most widely used reference data set in ML
>>> df = "~/iris.csv"
>>> data = NP.loadtxt(df, delimiter=',')
>>> # remove class labels
>>> data = data[:,:-1]
>>> plot_pca(data)

Le graphique ci-dessous est une représentation visuelle de cette fonction PCA sur les données iris. Comme vous pouvez le voir, une transformation 2D sépare proprement la classe I de la classe II et de la classe III (mais pas la classe II de la classe III, qui nécessite en fait une autre dimension).

entrez la description de l'image ici

68
répondu doug 2016-05-10 06:35:34

Un autre PCA Python utilisant numpy. La même idée que @ doug mais celle-là n'a pas couru.

from numpy import array, dot, mean, std, empty, argsort
from numpy.linalg import eigh, solve
from numpy.random import randn
from matplotlib.pyplot import subplots, show

def cov(data):
    """
    Covariance matrix
    note: specifically for mean-centered data
    note: numpy's `cov` uses N-1 as normalization
    """
    return dot(X.T, X) / X.shape[0]
    # N = data.shape[1]
    # C = empty((N, N))
    # for j in range(N):
    #   C[j, j] = mean(data[:, j] * data[:, j])
    #   for k in range(j + 1, N):
    #       C[j, k] = C[k, j] = mean(data[:, j] * data[:, k])
    # return C

def pca(data, pc_count = None):
    """
    Principal component analysis using eigenvalues
    note: this mean-centers and auto-scales the data (in-place)
    """
    data -= mean(data, 0)
    data /= std(data, 0)
    C = cov(data)
    E, V = eigh(C)
    key = argsort(E)[::-1][:pc_count]
    E, V = E[key], V[:, key]
    U = dot(data, V)  # used to be dot(V.T, data.T).T
    return U, E, V

""" test data """
data = array([randn(8) for k in range(150)])
data[:50, 2:4] += 5
data[50:, 2:5] += 5

""" visualize """
trans = pca(data, 3)[0]
fig, (ax1, ax2) = subplots(1, 2)
ax1.scatter(data[:50, 0], data[:50, 1], c = 'r')
ax1.scatter(data[50:, 0], data[50:, 1], c = 'b')
ax2.scatter(trans[:50, 0], trans[:50, 1], c = 'r')
ax2.scatter(trans[50:, 0], trans[50:, 1], c = 'b')
show()

Ce qui donne la même chose que la beaucoup plus courte

from sklearn.decomposition import PCA

def pca2(data, pc_count = None):
    return PCA(n_components = 4).fit_transform(data)

Si je comprends bien, l'utilisation de valeurs propres (première façon) est préférable pour les données à haute dimension et moins d'échantillons, alors que l'utilisation de la décomposition des valeurs singulières est préférable si vous avez plus d'échantillons que de dimensions.

14
répondu Mark 2017-08-18 16:09:52

Ceci est un travail pour numpy.

Et voici un tutoriel montrant comment l'analyse des composants pincipaux peut être effectuée en utilisant les modules intégrés de numpy comme mean,cov,double,cumsum,dot,linalg,array,rank.

Http://glowingpython.blogspot.sg/2011/07/principal-component-analysis-with-numpy.html

Notez que scipy a aussi une longue explication ici - https://github.com/scikit-learn/scikit-learn/blob/babe4a5d0637ca172d47e1dfdd2f6f3c3ecb28db/scikits/learn/utils/extmath.py#L105

Avec le scikit-learn bibliothèque ayant plus d'exemples de code - https://github.com/scikit-learn/scikit-learn/blob/babe4a5d0637ca172d47e1dfdd2f6f3c3ecb28db/scikits/learn/utils/extmath.py#L105

11
répondu Calvin Cheng 2012-11-05 00:45:47

Voici les options scikit-learn. Avec les deux méthodes, StandardScaler a été utilisé parce que PCA est effectuée par scale

Méthode 1: Demandez à scikit-learn de choisir le nombre minimum {[8] } de composants principaux tels qu'au moins x% (90% dans l'exemple ci-dessous) de la variance est conservé.

from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

iris = load_iris()

# mean-centers and auto-scales the data
standardizedData = StandardScaler().fit_transform(iris.data)

pca = PCA(.90)

principalComponents = pca.fit_transform(X = standardizedData)

# To get how many principal components was chosen
print(pca.n_components_)

Méthode 2: Choisissez le nombre de composants principaux (dans ce cas, 2 a été choisi)

from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

iris = load_iris()

standardizedData = StandardScaler().fit_transform(iris.data)

pca = PCA(n_components=2)

principalComponents = pca.fit_transform(X = standardizedData)

# to get how much variance was retained
print(pca.explained_variance_ratio_.sum())

Source: https://towardsdatascience.com/pca-using-python-scikit-learn-e653f8989e60

4
répondu Michael James Kali Galarnyk 2018-01-02 22:29:58

Mise À JOUR: matplotlib.mlab.PCA est depuis la version 2.2 (2018-03-06) en effet obsolète .

La bibliothèque matplotlib.mlab.PCA (utilisé dans cette réponse) est pas obsolète. Donc, pour tous les gens arrivant ici via Google, je vais poster un exemple de travail complet testé avec Python 2.7.

Utilisez le code suivant avec précaution car il utilise une bibliothèque maintenant obsolète!

from matplotlib.mlab import PCA
import numpy
data = numpy.array( [[3,2,5], [-2,1,6], [-1,0,4], [4,3,4], [10,-5,-6]] )
pca = PCA(data)

Maintenant dans ' pca.Y ' est la matrice de données d'origine en termes de base des composants principaux vecteur. Plus de détails sur L'objet PCA peuvent être trouvés ici .

>>> pca.Y
array([[ 0.67629162, -0.49384752,  0.14489202],
   [ 1.26314784,  0.60164795,  0.02858026],
   [ 0.64937611,  0.69057287, -0.06833576],
   [ 0.60697227, -0.90088738, -0.11194732],
   [-3.19578784,  0.10251408,  0.00681079]])

Vous pouvez utiliser matplotlib.pyplot pour dessiner ces données, juste pour vous convaincre que le PCA donne de "bons" résultats. La liste names est juste utilisée pour annoter nos cinq vecteurs.

import matplotlib.pyplot
names = [ "A", "B", "C", "D", "E" ]
matplotlib.pyplot.scatter(pca.Y[:,0], pca.Y[:,1])
for label, x, y in zip(names, pca.Y[:,0], pca.Y[:,1]):
    matplotlib.pyplot.annotate( label, xy=(x, y), xytext=(-2, 2), textcoords='offset points', ha='right', va='bottom' )
matplotlib.pyplot.show()

En regardant nos vecteurs originaux, nous verrons que data[0] ("A") et data [3] ("D") sont plutôt similaires à data[1] ("B") et data[2] ("C"). Cela se reflète dans le tracé 2D de notre PCA transformé données.

Graphique des résultats PCA

4
répondu z80crew 2018-04-26 13:36:54

J'ai fait un petit script pour comparer les différents PCA est apparu comme une réponse ici:

import numpy as np
from scipy.linalg import svd

shape = (26424, 144)
repeat = 20
pca_components = 2

data = np.array(np.random.randint(255, size=shape)).astype('float64')

# data normalization
# data.dot(data.T)
# (U, s, Va) = svd(data, full_matrices=False)
# data = data / s[0]

from fbpca import diffsnorm
from timeit import default_timer as timer

from scipy.linalg import svd
start = timer()
for i in range(repeat):
    (U, s, Va) = svd(data, full_matrices=False)
time = timer() - start
err = diffsnorm(data, U, s, Va)
print('svd time: %.3fms, error: %E' % (time*1000/repeat, err))


from matplotlib.mlab import PCA
start = timer()
_pca = PCA(data)
for i in range(repeat):
    U = _pca.project(data)
time = timer() - start
err = diffsnorm(data, U, _pca.fracs, _pca.Wt)
print('matplotlib PCA time: %.3fms, error: %E' % (time*1000/repeat, err))

from fbpca import pca
start = timer()
for i in range(repeat):
    (U, s, Va) = pca(data, pca_components, True)
time = timer() - start
err = diffsnorm(data, U, s, Va)
print('facebook pca time: %.3fms, error: %E' % (time*1000/repeat, err))


from sklearn.decomposition import PCA
start = timer()
_pca = PCA(n_components = pca_components)
_pca.fit(data)
for i in range(repeat):
    U = _pca.transform(data)
time = timer() - start
err = diffsnorm(data, U, _pca.explained_variance_, _pca.components_)
print('sklearn PCA time: %.3fms, error: %E' % (time*1000/repeat, err))

start = timer()
for i in range(repeat):
    (U, s, Va) = pca_mark(data, pca_components)
time = timer() - start
err = diffsnorm(data, U, s, Va.T)
print('pca by Mark time: %.3fms, error: %E' % (time*1000/repeat, err))

start = timer()
for i in range(repeat):
    (U, s, Va) = pca_doug(data, pca_components)
time = timer() - start
err = diffsnorm(data, U, s[:pca_components], Va.T)
print('pca by doug time: %.3fms, error: %E' % (time*1000/repeat, err))

Pca_mark est le pca dans la réponse de Mark .

Pca_doug est le pca dans la réponse de doug .

Voici un exemple de sortie (mais le résultat dépend beaucoup de la taille des données et de pca_components, donc je recommande d'exécuter votre propre test avec vos propres données. En outre, le pca de facebook est optimisé pour les données normalisées, il sera donc plus rapide et plus précis cas):

svd time: 3212.228ms, error: 1.907320E-10
matplotlib PCA time: 879.210ms, error: 2.478853E+05
facebook pca time: 485.483ms, error: 1.260335E+04
sklearn PCA time: 169.832ms, error: 7.469847E+07
pca by Mark time: 293.758ms, error: 1.713129E+02
pca by doug time: 300.326ms, error: 1.707492E+02

Modifier:

La fonction diffsnorm de fbpca calcule l'erreur de norme spectrale d'une décomposition de Schur.

1
répondu bendaf 2018-09-04 12:55:43

En plus de toutes les autres réponses, voici du code pour tracer le biplot en utilisant sklearn et matplotlib.

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.decomposition import PCA
import pandas as pd
from sklearn.preprocessing import StandardScaler

iris = datasets.load_iris()
X = iris.data
y = iris.target
#In general a good idea is to scale the data
scaler = StandardScaler()
scaler.fit(X)
X=scaler.transform(X)    

pca = PCA()
x_new = pca.fit_transform(X)

def myplot(score,coeff,labels=None):
    xs = score[:,0]
    ys = score[:,1]
    n = coeff.shape[0]
    scalex = 1.0/(xs.max() - xs.min())
    scaley = 1.0/(ys.max() - ys.min())
    plt.scatter(xs * scalex,ys * scaley, c = y)
    for i in range(n):
        plt.arrow(0, 0, coeff[i,0], coeff[i,1],color = 'r',alpha = 0.5)
        if labels is None:
            plt.text(coeff[i,0]* 1.15, coeff[i,1] * 1.15, "Var"+str(i+1), color = 'g', ha = 'center', va = 'center')
        else:
            plt.text(coeff[i,0]* 1.15, coeff[i,1] * 1.15, labels[i], color = 'g', ha = 'center', va = 'center')
plt.xlim(-1,1)
plt.ylim(-1,1)
plt.xlabel("PC{}".format(1))
plt.ylabel("PC{}".format(2))
plt.grid()

#Call the function. Use only the 2 PCs.
myplot(x_new[:,0:2],np.transpose(pca.components_[0:2, :]))
plt.show()

entrez la description de l'image ici

1
répondu seralouk 2018-10-04 08:26:31

Pour l'amour def plot_pca(data): fonctionne, il est nécessaire de remplacer les lignes

data_resc, data_orig = PCA(data)
ax1.plot(data_resc[:, 0], data_resc[:, 1], '.', mfc=clr1, mec=clr1)

Avec des lignes

newData, data_resc, data_orig = PCA(data)
ax1.plot(newData[:, 0], newData[:, 1], '.', mfc=clr1, mec=clr1)
0
répondu Edson 2016-11-17 19:06:48