resueltos principales paso mano español ejercicios ejemplo con componentes component calcular analisis python numpy scipy pca

principales - pca python



Análisis de componentes principales en Python (10)

Me gustaría usar el análisis de componentes principales (PCA) para la reducción de dimensionalidad. ¿Lo tiene nupey o scipy, o tengo que hacer mi propio uso de numpy.linalg.eigh ?

No solo quiero usar la descomposición de valores singulares (SVD) porque mis datos de entrada son bastante dimensionales (~ 460 dimensiones), así que creo que SVD será más lento que el cálculo de los vectores propios de la matriz de covarianza.

Tenía la esperanza de encontrar una implementación prefabricada y depurada que ya tomara las decisiones correctas sobre cuándo usar qué método, y que quizás otras optimizaciones que yo desconozca.


Acabo de leer el libro Machine Learning: An Algorithmic Perspective . Todos los ejemplos de código en el libro fueron escritos por Python (y casi con Numpy). El fragmento de código de chatper10.2 Análisis de componentes principales quizás merezca la pena leerlo. Utiliza numpy.linalg.eig.
Por cierto, creo que SVD puede manejar muy bien las dimensiones 460 * 460. He calculado un 6500 * 6500 SVD con numpy / scipy.linalg.svd en una PC muy antigua: Pentium III 733mHz. Para ser honesto, el script necesita mucha memoria (aproximadamente 1.xG) y mucho tiempo (aproximadamente 30 minutos) para obtener el resultado SVD. Pero creo que 460 * 460 en una PC moderna no será un gran problema a menos que necesite SVD una gran cantidad de veces.


La PCA que usa numpy.linalg.svd es muy fácil. Aquí hay una demostración simple:

import numpy as np import matplotlib.pyplot as plt from scipy.misc import lena # the underlying signal is a sinusoidally modulated image img = lena() t = np.arange(100) time = np.sin(0.1*t) real = time[:,np.newaxis,np.newaxis] * img[np.newaxis,...] # we add some noise noisy = real + np.random.randn(*real.shape)*255 # (observations, features) matrix M = noisy.reshape(noisy.shape[0],-1) # singular value decomposition factorises your data matrix such that: # # M = U*S*V.T (where ''*'' is matrix multiplication) # # * U and V are the singular matrices, containing orthogonal vectors of # unit length in their rows and columns respectively. # # * S is a diagonal matrix containing the singular values of M - these # values squared divided by the number of observations will give the # variance explained by each PC. # # * if M is considered to be an (observations, features) matrix, the PCs # themselves would correspond to the rows of S^(1/2)*V.T. if M is # (features, observations) then the PCs would be the columns of # U*S^(1/2). # # * since U and V both contain orthonormal vectors, U*V.T is equivalent # to a whitened version of M. U, s, Vt = np.linalg.svd(M, full_matrices=False) V = Vt.T # PCs are already sorted by descending order # of the singular values (i.e. by the # proportion of total variance they explain) # if we use all of the PCs we can reconstruct the noisy signal perfectly S = np.diag(s) Mhat = np.dot(U, np.dot(S, V.T)) print "Using all PCs, MSE = %.6G" %(np.mean((M - Mhat)**2)) # if we use only the first 20 PCs the reconstruction is less accurate Mhat2 = np.dot(U[:, :20], np.dot(S[:20, :20], V[:,:20].T)) print "Using first 20 PCs, MSE = %.6G" %(np.mean((M - Mhat2)**2)) fig, [ax1, ax2, ax3] = plt.subplots(1, 3) ax1.imshow(img) ax1.set_title(''true image'') ax2.imshow(noisy.mean(0)) ax2.set_title(''mean of noisy images'') ax3.imshow((s[0]**(1./2) * V[:,0]).reshape(img.shape)) ax3.set_title(''first spatial PC'') plt.show()


Meses más tarde, aquí hay una pequeña PCA de clase, y una foto:

#!/usr/bin/env python """ a small class for Principal Component Analysis Usage: p = PCA( A, fraction=0.90 ) In: A: an array of e.g. 1000 observations x 20 variables, 1000 rows x 20 columns fraction: use principal components that account for e.g. 90 % of the total variance Out: p.U, p.d, p.Vt: from numpy.linalg.svd, A = U . d . Vt p.dinv: 1/d or 0, see NR p.eigen: the eigenvalues of A*A, in decreasing order (p.d**2). eigen[j] / eigen.sum() is variable j''s fraction of the total variance; look at the first few eigen[] to see how many PCs get to 90 %, 95 % ... p.npc: number of principal components, e.g. 2 if the top 2 eigenvalues are >= `fraction` of the total. It''s ok to change this; methods use the current value. Methods: The methods of class PCA transform vectors or arrays of e.g. 20 variables, 2 principal components and 1000 observations, using partial matrices U'' d'' Vt'', parts of the full U d Vt: A ~ U'' . d'' . Vt'' where e.g. U'' is 1000 x 2 d'' is diag([ d0, d1 ]), the 2 largest singular values Vt'' is 2 x 20. Dropping the primes, d . Vt 2 principal vars = p.vars_pc( 20 vars ) U 1000 obs = p.pc_obs( 2 principal vars ) U . d . Vt 1000 obs, p.obs( 20 vars ) = pc_obs( vars_pc( vars )) fast approximate A . vars, using the `npc` principal components Ut 2 pcs = p.obs_pc( 1000 obs ) V . dinv 20 vars = p.pc_vars( 2 principal vars ) V . dinv . Ut 20 vars, p.vars( 1000 obs ) = pc_vars( obs_pc( obs )), fast approximate Ainverse . obs: vars that give ~ those obs. Notes: PCA does not center or scale A; you usually want to first A -= A.mean(A, axis=0) A /= A.std(A, axis=0) with the little class Center or the like, below. See also: http://en.wikipedia.org/wiki/Principal_component_analysis http://en.wikipedia.org/wiki/Singular_value_decomposition Press et al., Numerical Recipes (2 or 3 ed), SVD PCA micro-tutorial iris-pca .py .png """ from __future__ import division import numpy as np dot = np.dot # import bz.numpyutil as nu # dot = nu.pdot __version__ = "2010-04-14 apr" __author_email__ = "denis-bz-py at t-online dot de" #............................................................................... class PCA: def __init__( self, A, fraction=0.90 ): assert 0 <= fraction <= 1 # A = U . diag(d) . Vt, O( m n^2 ), lapack_lite -- self.U, self.d, self.Vt = np.linalg.svd( A, full_matrices=False ) assert np.all( self.d[:-1] >= self.d[1:] ) # sorted self.eigen = self.d**2 self.sumvariance = np.cumsum(self.eigen) self.sumvariance /= self.sumvariance[-1] self.npc = np.searchsorted( self.sumvariance, fraction ) + 1 self.dinv = np.array([ 1/d if d > self.d[0] * 1e-6 else 0 for d in self.d ]) def pc( self ): """ e.g. 1000 x 2 U[:, :npc] * d[:npc], to plot etc. """ n = self.npc return self.U[:, :n] * self.d[:n] # These 1-line methods may not be worth the bother; # then use U d Vt directly -- def vars_pc( self, x ): n = self.npc return self.d[:n] * dot( self.Vt[:n], x.T ).T # 20 vars -> 2 principal def pc_vars( self, p ): n = self.npc return dot( self.Vt[:n].T, (self.dinv[:n] * p).T ) .T # 2 PC -> 20 vars def pc_obs( self, p ): n = self.npc return dot( self.U[:, :n], p.T ) # 2 principal -> 1000 obs def obs_pc( self, obs ): n = self.npc return dot( self.U[:, :n].T, obs ) .T # 1000 obs -> 2 principal def obs( self, x ): return self.pc_obs( self.vars_pc(x) ) # 20 vars -> 2 principal -> 1000 obs def vars( self, obs ): return self.pc_vars( self.obs_pc(obs) ) # 1000 obs -> 2 principal -> 20 vars class Center: """ A -= A.mean() /= A.std(), inplace -- use A.copy() if need be uncenter(x) == original A . x """ # mttiw def __init__( self, A, axis=0, scale=True, verbose=1 ): self.mean = A.mean(axis=axis) if verbose: print "Center -= A.mean:", self.mean A -= self.mean if scale: std = A.std(axis=axis) self.std = np.where( std, std, 1. ) if verbose: print "Center /= A.std:", self.std A /= self.std else: self.std = np.ones( A.shape[-1] ) self.A = A def uncenter( self, x ): return np.dot( self.A, x * self.std ) + np.dot( x, self.mean ) #............................................................................... if __name__ == "__main__": import sys csv = "iris4.csv" # wikipedia Iris_flower_data_set # 5.1,3.5,1.4,0.2 # ,Iris-setosa ... N = 1000 K = 20 fraction = .90 seed = 1 exec "/n".join( sys.argv[1:] ) # N= ... np.random.seed(seed) np.set_printoptions( 1, threshold=100, suppress=True ) # .1f try: A = np.genfromtxt( csv, delimiter="," ) N, K = A.shape except IOError: A = np.random.normal( size=(N, K) ) # gen correlated ? print "csv: %s N: %d K: %d fraction: %.2g" % (csv, N, K, fraction) Center(A) print "A:", A print "PCA ..." , p = PCA( A, fraction=fraction ) print "npc:", p.npc print "% variance:", p.sumvariance * 100 print "Vt[0], weights that give PC 0:", p.Vt[0] print "A . Vt[0]:", dot( A, p.Vt[0] ) print "pc:", p.pc() print "/nobs <-> pc <-> x: with fraction=1, diffs should be ~ 0" x = np.ones(K) # x = np.ones(( 3, K )) print "x:", x pc = p.vars_pc(x) # d'' Vt'' x print "vars_pc(x):", pc print "back to ~ x:", p.pc_vars(pc) Ax = dot( A, x.T ) pcx = p.obs(x) # U'' d'' Vt'' x print "Ax:", Ax print "A''x:", pcx print "max |Ax - A''x|: %.2g" % np.linalg.norm( Ax - pcx, np.inf ) b = Ax # ~ back to original x, Ainv A x back = p.vars(b) print "~ back again:", back print "max |back - x|: %.2g" % np.linalg.norm( back - x, np.inf ) # end pca.py


No necesita una Descomposición de Valor Singular (SVD) completa al computar todos los valores propios y vectores propios y puede ser prohibitiva para matrices grandes. scipy y su escaso módulo proporcionan funciones de algrebra lineales genéricas que trabajan tanto en matrices dispersas como en densas, entre las que se encuentra la familia de funciones eig *:

http://docs.scipy.org/doc/scipy/reference/sparse.linalg.html#matrix-factorizations

Scikit-learn proporciona una implementación Python PCA que solo admite matrices densas por el momento.

Tiempos:

In [1]: A = np.random.randn(1000, 1000) In [2]: %timeit scipy.sparse.linalg.eigsh(A) 1 loops, best of 3: 802 ms per loop In [3]: %timeit np.linalg.svd(A) 1 loops, best of 3: 5.91 s per loop


Puede "rodar" fácilmente su propio uso de scipy.linalg (suponiendo datos de un conjunto de data scipy.linalg ):

covmat = data.dot(data.T) evs, evmat = scipy.linalg.eig(covmat)

Entonces evs son sus valores propios, y evmat es su matriz de proyección.

Si desea mantener d dimensiones, use los primeros d valores propios y los primeros vectores propios.

Dado que scipy.linalg tiene la descomposición y nubla las multiplicaciones de la matriz, ¿qué más necesitas?


Puede echarle un vistazo a MDP .

No he tenido la oportunidad de probarlo yo mismo, pero lo he marcado exactamente para la funcionalidad PCA.


Puedes usar sklearn:

import sklearn.decomposition as deco import numpy as np x = (x - np.mean(x, 0)) / np.std(x, 0) # You need to normalize your data first pca = deco.PCA(n_components) # n_components is the components number after reduction x_r = pca.fit(x).transform(x) print (''explained variance (first %d components): %.2f''%(n_components, sum(pca.explained_variance_ratio_)))


SVD debería funcionar bien con 460 dimensiones. Tarda unos 7 segundos en mi netbook Atom. El método eig () lleva más tiempo (como debería ser, usa más operaciones de coma flotante) y casi siempre será menos preciso.

Si tiene menos de 460 ejemplos, entonces lo que quiere hacer es diagonalizar la matriz de dispersión (x - datamean) ^ T (x - mean), suponiendo que sus puntos de datos son columnas, y luego multiplicando a la izquierda por (x - datamean). Eso podría ser más rápido en el caso de que tenga más dimensiones que datos.


Here hay otra implementación de un módulo de PCA para python usando numpy, scipy y C-extensions. El módulo lleva a cabo PCA utilizando un algoritmo SVD o NIPALS (Mínimos cuadrados parciales iterativos no lineales) que se implementa en C.