PCA com Python

Neste post eu vou resumir alguns aprendizados que obtive ao implementar em Python uma classe de Análise de Componentes Principais ou, para os íntimos, PCA (do inglês, Principal Components Analysis).

Antes de começar, vale esclarecer:

  • Neste post eu não vou explicar a teoria por trás do PCA e nem para serve, eu vou discutir apenas sua implementação usando Python. Existem muitas referências na Internet sobre este tema mas, apenas deixando uma sugestão para quem está tendo o primeiro contato com PCA, um dos melhores textos que já li sobre este assunto por ser objetivo e utilizar explicações intuitivas é o artigo “A Tutorial on Principal Component Analysis”, de Jonathon Shlens.
  • Se você chegou aqui porque está desesperado(a) e quer entender de uma vez por todas este tal de PCA, eu deixo algumas palavras de encorajamento: Eu também já estive nesta situação. Persevere e continue estudando pois quando você dominar esta técnica, você perceberá que ela é tão poderosa quanto simples. Durante o processo, não se sinta deprimido se as coisas parecerem confusas demais. Isso é normal para qualquer um que se aventure pelo mundo das análises estatisticas. Recomendo o livro “O Andar do Bêbado” (Leonard Mlodinow), para que você se sinta um pouco melhor em relação a esse sentimento.

A discussão será guiada pela afirmação:

Os componentes principais de um conjunto de dados num espaço multidimensional 
podem ser determinados pelos autovetores da matriz de covariância dos dados.

Definindo os dados

Os dados de entrada do PCA, ou conjunto de treinamento, é um conjunto de N amostras (samples/trials/observations) com M dimensões (variables).

Vamos supor, por exemplo, que tenhamos 15 amostras (N=15) de duas variáveis diferentes (M=2), como mostra a tabela abaixo.

Amostra n° Variável X1 Variável X2
1 10.0 10.7
2 10.4 9.8
3 9.7 10.0
4 9.7 10.1
5 11.7 11.5
6 11.0 10.8
7 8.7 8.8
8 9.5 9.3
9 10.1 9.4
10 9.6 9.6
11 10.5 10.4
12 9.2 9.0
13 11.3 11.6
14 10.1 9.8
15 8.5 9.2

O primeiro passo é definir o array que representará estes dados.
A convenção mais comum de entrada dos dados é que as linhas do array representem diferentes amostras, enquanto as colunas representem diferentes variáveis. Assim:

>>> import numpy as np
>>> data=np.asarray(
    [[10.0,10.7],
     [10.4,9.8],
     [9.7,10.0],
     [9.7,10.1],
     [11.7,11.5],
     [11.0,10.8],
     [8.7,8.8],
     [9.5,9.3],
     [10.1,9.4],
     [9.6,9.6],
     [10.5,10.4],
     [9.2,9.0],
     [11.3,11.6],
     [10.1,9.8],
     [8.5,9.2]])

Calculando a matriz de covariância

O próximo passo no caminho para se determinar os componentes principais dos dados é calcular a matriz de covariância dos dados.

O caminho mais direto para se calcular a matriz de covariância dos dados é a utilização da função numpy.cov

No entanto, note que a função numpy.cov recebe uma array onde linhas=>variáveis e colunas=>amostras, sendo necessário transpor a matriz criada anteriormente:

>>> np.cov(np.transpose(data))
array([[ 0.79857143,  0.67928571],
       [ 0.67928571,  0.73428571]])

 

Calculando os autovetores da matriz de covariância

De posse da matriz de covariância, o próximo passo é calcular os autovetores e autovalores dessa matriz.

Para isso, pode-se utilizar a função numpy.linalg.eig

>>> cov_data=np.cov(np.transpose(data)) # Obtém matriz de covariância dos dados
>>> import numpy.linalg as la           # Importa pacote numpy.linalg
>>> w,v=la.eig(cov_data)                # Obtém autovalores e autovetores
>>> w
array([ 1.44647434,  0.0863828 ])
>>> v
array([[ 0.72362481, -0.69019355],
       [ 0.69019355,  0.72362481]])

Observações importantes:

  • A função numpy.linalg.eig, retorna dois valores: um vetor de autovalores w (NÃO NECESSARIAMENTE ORDENADOS) e o segundo uma matriz v onde cada coluna v[:,i] corresponde ao autovetor correspondente ao autovalor w[i]. Assim, no exemplo dado, o autovetor v[:, 0]=[0.72362481, 0.69019355] corresponde ao autovalor w[0]=1.44647434
  • Na análise PCA, os maiores autovalores correspondem aos componentes principais mais importantes ou, em outras palavras, os autovetores associados aos maiores autovalores apontam na direção onde os dados sofrem maior variação. Por isso, tipicamente, após o cálculo de autovetores e autovalores da matriz de covariância, o primeiro passo é a ordenação dos autovalores e seus autovetores correspondentes em ordem decrescente.
>>> ind=np.argsort(w)[::-1]  # Obtém índices para ordenação decrescente dos autovalores
>>> w_dec=w[ind]
>>> v_dec=v[ind]
  • A função numpy.linalg.eig já retorna os autovetores com módulo unitário (eles são normalizados) e, como era de se esperar da análise PCA, os autovetores formam uma base ortogonal. Isso pode ser facilmente verificado no exemplo acima.
>>> la.norm(v[0])
1.0
>>> la.norm(v[1])
1.0
>>> np.dot(v[0],v[1])
0.0

O que representam os autovalores da matriz de covariância?

Os autovalores da matriz de covariância representam a variância que será observada quando os dados originais forem projetados no eixo dado pelo autovetor correspondente. Um determinado autovalor informa a “variância explicada” (Explained Variance) pelo seu componente principal (autovetor) correspondente.

Tipicamente, esta variância é apresentada como uma taxa (ratio). Para isso, divide-se cada autovalor pela soma de todos autovalores obtidos.

No exemplo acima, pode-se obter a taxa de variância explicada (Explained Variance Ratio – EVR) fazendo-se:

>>> EVR=w/np.sum(w)
>>> EVR
array([ 0.94364589,  0.05635411])

Note que no exemplo dado, 94% da variância dos dados está na direção do primeiro componente principal, cuja direção é definida pelo vetor v1=[0.72362481, 0.69019355].

Utilizando scikit.learn

Se você pretende utilizar a análise PCA como ferramenta vale à pena dar uma olhada na toolbox scikit-learn.

Esta toolbox conta com uma classe PCA com várias funções acessórias interessantes.

Se você quiser, é possível dar uma olhada na implementação dessa classe, já que o código do  scikit-learn é aberto. Lá você verá que para o cálculo dos componentes principais é utilizada a decomposição por valores singulares (Singular Value Decomposition – SVD) através da função numpy.linalg.svd

O argumento em vantagem deste método é que ele não exige a etapa de cálculo da matriz de covariância, que pode resultar em imprecisões em alguns casos (dê uma olhada nesta discussão).

Voltando ao nosso exemplo, veja como seria esse cálculo utilizando SVD:

  • Centraliza-se os dados em relação à média:
datac=data-np.mean(data,axis=0)
  • Aplica-se o SVD:
U,S,V=la.svd(data)
>>> V
array([[-0.72362481, -0.69019355],
       [-0.69019355,  0.72362481]])

Note que a matriz V retorna os mesmos autovetores (com sentido invertido, mas na mesma direção).

Para se realizar a mesma análise, utilizando-se a classe PCA do scikit-learn tem-se:

>>> from sklearn.decomposition import PCA
>>> pca=PCA()                                    # inicializa classe
>>> pca.fit(data)                                # submete os dados 
PCA(copy=True, n_components=None, whiten=False)
>>> pca.components_                              # componentes principais
array([[-0.72362481, -0.69019355],
       [-0.69019355,  0.72362481]])
>>> pca.explained_variance_ratio_                # EVR
array([ 0.94364589,  0.05635411])
>>> pca.explained_variance_
array([ 1.35004272,  0.08062395])

Por último, chamo atenção para a ressalva feita na descrição da classe PCA do scikit-learn: as implementações acima de PCA funcionarão para matrizes de dados densas (não-esparsas) e não são escaláveis para dados com elevado número de dimensões.

Caso seu problema seja um elevado número de dimensões, recomendo que você dê uma olhada nos trabalhos relacionados a Eigenfaces pois eles tratam de imagens de faces e têm um truque bem interessante para aplicar o PCA nestes casos (pretendo um dia falar sobre ele aqui…)

 

 

Anúncios

Deixe seu comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s