web-dev-qa-db-fra.com

Récupération des noms de fonctionnalités de expliqué_variance_ratio_ dans PCA avec sklearn

J'essaie de récupérer à partir d'un PCA fait avec scikit-learn, qui les fonctionnalités sont sélectionnées comme pertinentes .

Un exemple classique avec l'ensemble de données IRIS.

import pandas as pd
import pylab as pl
from sklearn import datasets
from sklearn.decomposition import PCA

# load dataset
iris = datasets.load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

# normalize data
df_norm = (df - df.mean()) / df.std()

# PCA
pca = PCA(n_components=2)
pca.fit_transform(df_norm.values)
print pca.explained_variance_ratio_

Cela revient

In [42]: pca.explained_variance_ratio_
Out[42]: array([ 0.72770452,  0.23030523])

Comment puis-je récupérer les deux fonctionnalités permettant ces deux variances expliquées parmi l'ensemble de données? Dit différemment, comment puis-je obtenir l'index de ces fonctionnalités dans iris.feature_names?

In [47]: print iris.feature_names
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

Merci d'avance pour votre aide.

53
mazieres

Ces informations sont incluses dans l'attribut pca: components_. Comme décrit dans la documentation , pca.components_ affiche un tableau de [n_components, n_features], pour savoir comment les composants sont liés linéairement aux différentes fonctionnalités, vous devez:

Remarque : chaque coefficient représente la corrélation entre une paire particulière de composant et de caractéristique

import pandas as pd
import pylab as pl
from sklearn import datasets
from sklearn.decomposition import PCA

# load dataset
iris = datasets.load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

# normalize data
from sklearn import preprocessing
data_scaled = pd.DataFrame(preprocessing.scale(df),columns = df.columns) 

# PCA
pca = PCA(n_components=2)
pca.fit_transform(data_scaled)

# Dump components relations with features:
print pd.DataFrame(pca.components_,columns=data_scaled.columns,index = ['PC-1','PC-2'])

      sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
PC-1           0.522372         -0.263355           0.581254          0.565611
PC-2          -0.372318         -0.925556          -0.021095         -0.065416

IMPORTANT: Comme commentaire secondaire, notez que le signe PCA n'affecte pas son interprétation puisque le signe n'affecte pas la variance contenue dans chaque composant. Seuls les signes relatifs des caractéristiques formant la dimension PCA sont importants. En fait, si vous exécutez à nouveau le code PCA, vous pourriez obtenir les dimensions PCA avec les signes inversés. Pour une intuition à ce sujet, pensez à un vecteur et à son négatif dans l'espace 3D - les deux représentent essentiellement la même direction dans l'espace. Vérifiez cet article pour plus de référence.

63
Rafa

Modifier: comme d'autres l'ont commenté, vous pouvez obtenir les mêmes valeurs de .components_ attribut.


Chaque composant principal est une combinaison linéaire des variables d'origine:

pca-coef

X_is sont les variables d'origine et Beta_is sont les poids correspondants ou ce que l'on appelle les coefficients.

Pour obtenir les poids, vous pouvez simplement passer la matrice d'identité à la méthode transform:

>>> i = np.identity(df.shape[1])  # identity matrix
>>> i
array([[ 1.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]])

>>> coef = pca.transform(i)
>>> coef
array([[ 0.5224, -0.3723],
       [-0.2634, -0.9256],
       [ 0.5813, -0.0211],
       [ 0.5656, -0.0654]])

Chaque colonne de la matrice coef ci-dessus montre les poids dans la combinaison linéaire qui obtient la composante principale correspondante:

>>> pd.DataFrame(coef, columns=['PC-1', 'PC-2'], index=df.columns)
                    PC-1   PC-2
sepal length (cm)  0.522 -0.372
sepal width (cm)  -0.263 -0.926
petal length (cm)  0.581 -0.021
petal width (cm)   0.566 -0.065

[4 rows x 2 columns]

Par exemple, ci-dessus montre que le deuxième composant principal (PC-2) est principalement aligné avec sepal width, qui a le poids le plus élevé de 0.926 en valeur absolue;

Étant donné que les données ont été normalisées, vous pouvez confirmer que les principales composantes présentent une variance 1.0 ce qui équivaut à chaque vecteur de coefficient ayant la norme 1.0:

>>> np.linalg.norm(coef,axis=0)
array([ 1.,  1.])

On peut également confirmer que les principales composantes peuvent être calculées comme le produit scalaire des coefficients ci-dessus et des variables d'origine:

>>> np.allclose(df_norm.values.dot(coef), pca.fit_transform(df_norm.values))
True

Notez que nous devons utiliser numpy.allclose au lieu de l'opérateur d'égalité normal, en raison d'une erreur de précision en virgule flottante.

45
behzad.nouri

La façon dont cette question est formulée me rappelle un malentendu sur l'analyse en composantes principales lorsque j'essayais de la comprendre pour la première fois. Je voudrais le parcourir ici dans l'espoir que d'autres ne passeront pas autant de temps sur une route vers nulle part comme je l'ai fait avant que le sou ne soit finalement tombé.

La notion de "récupération" des noms d'entités suggère que PCA identifie les entités les plus importantes dans un ensemble de données. Ce n'est pas strictement vrai.

L'ACP, si je comprends bien, identifie les entités présentant la plus grande variance dans un ensemble de données, et peut ensuite utiliser cette qualité de l'ensemble de données pour créer un ensemble de données plus petit avec une perte minimale de puissance descriptive. Les avantages d'un ensemble de données plus petit est qu'il nécessite moins de puissance de traitement et devrait avoir moins de bruit dans les données. Mais les caractéristiques les plus variées ne sont pas les caractéristiques "meilleures" ou "les plus importantes" d'un ensemble de données, dans la mesure où de tels concepts peuvent être considérés comme existant du tout.

Pour intégrer cette théorie dans les aspects pratiques de l'exemple de code de @ Rafa ci-dessus:

# load dataset
iris = datasets.load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

# normalize data
from sklearn import preprocessing
data_scaled = pd.DataFrame(preprocessing.scale(df),columns = df.columns) 

# PCA
pca = PCA(n_components=2)
pca.fit_transform(data_scaled)

considérer ce qui suit:

post_pca_array = pca.fit_transform(data_scaled)

print data_scaled.shape
(150, 4)

print post_pca_array.shape
(150, 2)

Dans ce cas, post_pca_array A les mêmes 150 lignes de données que data_scaled, Mais les quatre colonnes de data_scaled Ont été réduites de quatre à deux.

Le point critique ici est que les deux colonnes - ou composants, pour être cohérents terminologiquement - de post_pca_array Ne sont pas les deux "meilleures" colonnes de data_scaled. Ce sont deux nouvelles colonnes, déterminées par l'algorithme derrière le module PCA de sklearn.decomposition. La deuxième colonne, PC-2 Dans l'exemple de @ Rafa, est informée par sepal_width Plus que toute autre colonne, mais les valeurs dans PC-2 Et data_scaled['sepal_width'] Ne sont pas les même.

En tant que tel, bien qu'il soit intéressant de savoir dans quelle mesure chaque colonne des données originales a contribué aux composants d'un ensemble de données post-PCA, la notion de "récupération" des noms de colonne est un peu trompeuse et m'a certainement induit en erreur pendant longtemps. La seule situation où il y aurait une correspondance entre les colonnes post-PCA et originales serait si le nombre de composants principaux était fixé au même nombre que les colonnes dans l'original. Cependant, il serait inutile d'utiliser le même nombre de colonnes car les données n'auraient pas changé. Vous n'y seriez allé que pour revenir, pour ainsi dire.

19
amunnelly

Étant donné votre estimateur ajusté pca, les composants se trouvent dans pca.components_, qui représentent les directions de la variance la plus élevée dans l'ensemble de données.

4
eickenberg

Les caractéristiques importantes sont celles qui influencent le plus les composants et ont donc une grande valeur absolue/coefficient/charge sur le composant.

Obtenez the most important feature name sur les PC:

from sklearn.decomposition import PCA
import pandas as pd
import numpy as np
np.random.seed(0)

# 10 samples with 5 features
train_features = np.random.Rand(10,5)

model = PCA(n_components=2).fit(train_features)
X_pc = model.transform(train_features)

# number of components
n_pcs= model.components_.shape[0]

# get the index of the most important feature on EACH component i.e. largest absolute value
# using LIST COMPREHENSION HERE
most_important = [np.abs(model.components_[i]).argmax() for i in range(n_pcs)]

initial_feature_names = ['a','b','c','d','e']

# get the names
most_important_names = [initial_feature_names[most_important[i]] for i in range(n_pcs)]

# using LIST COMPREHENSION HERE AGAIN
dic = {'PC{}'.format(i+1): most_important_names[i] for i in range(n_pcs)}

# build the dataframe
df = pd.DataFrame(sorted(dic.items()))

Ceci imprime:

     0  1
 0  PC1  e
 1  PC2  d

Conclusion/Explication:

Donc, sur PC1, la fonction nommée e est la plus importante et sur PC2 la d.

0
serafeim