Analyse et régression des composants principaux en Python

11

J'essaie de comprendre comment reproduire en Python certains travaux que j'ai faits en SAS. En utilisant cet ensemble de données , où la multicollinéarité est un problème, je voudrais effectuer une analyse des composants principaux en Python. J'ai regardé scikit-learn et les modèles de statistiques, mais je ne sais pas comment prendre leur sortie et la convertir dans la même structure de résultats que SAS. D'une part, SAS semble exécuter PCA sur la matrice de corrélation lorsque vous utilisez PROC PRINCOMP, mais la plupart (toutes?) Des bibliothèques Python semblent utiliser SVD.

Dans l'ensemble de données , la première colonne est la variable de réponse et les 5 suivantes sont des variables prédictives, appelées pred1-pred5.

Dans SAS, le workflow général est le suivant:

/* Get the PCs */
proc princomp data=indata out=pcdata;
    var pred1 pred2 pred3 pred4 pred5;
run;

/* Standardize the response variable */
proc standard data=pcdata mean=0 std=1 out=pcdata2;
    var response;
run;

/* Compare some models */
proc reg data=pcdata2;
    Reg:     model response = pred1 pred2 pred3 pred4 pred5 / vif;
    PCa:     model response = prin1-prin5 / vif;
    PCfinal: model response = prin1 prin2 / vif;
run;
quit;

/* Use Proc PLS to to PCR Replacement - dropping pred5 */
/* This gets me my parameter estimates for the original data */
proc pls data=indata method=pcr nfac=2;
    model response = pred1 pred2 pred3 pred4 / solution;
run;
quit;

Je sais que la dernière étape ne fonctionne que parce que je ne choisis que PC1 et PC2, dans l'ordre.

Donc, en Python, c'est à peu près autant que j'ai obtenu:

import pandas as pd
import numpy  as np
from sklearn.decomposition.pca import PCA

source = pd.read_csv('C:/sourcedata.csv')

# Create a pandas DataFrame object
frame = pd.DataFrame(source)

# Make sure we are working with the proper data -- drop the response variable
cols = [col for col in frame.columns if col not in ['response']]
frame2 = frame[cols]

pca = PCA(n_components=5)
pca.fit(frame2)

La quantité d'écart que chaque PC explique?

print pca.explained_variance_ratio_

Out[190]:
array([  9.99997603e-01,   2.01265023e-06,   2.70712663e-07,
         1.11512302e-07,   2.40310191e-09])

Qu'est-ce que c'est? Vecteurs propres?

print pca.components_

Out[179]:
array([[ -4.32840645e-04,  -7.18123771e-04,  -9.99989955e-01,
         -4.40303223e-03,  -2.46115129e-05],
       [  1.00991662e-01,   8.75383248e-02,  -4.46418880e-03,
          9.89353169e-01,   5.74291257e-02],
       [ -1.04223303e-02,   9.96159390e-01,  -3.28435046e-04,
         -8.68305757e-02,  -4.26467920e-03],
       [ -7.04377522e-03,   7.60168675e-04,  -2.30933755e-04,
          5.85966587e-02,  -9.98256573e-01],
       [ -9.94807648e-01,  -1.55477793e-03,  -1.30274879e-05,
          1.00934650e-01,   1.29430210e-02]])

S'agit-il des valeurs propres?

print pca.explained_variance_

Out[180]:
array([  8.07640319e+09,   1.62550137e+04,   2.18638986e+03,
         9.00620474e+02,   1.94084664e+01])

Je ne sais pas trop comment passer des résultats Python à la régression effective des composants principaux (en Python). L'une des bibliothèques Python remplit-elle les espaces de manière similaire à SAS?

Tous les conseils sont appréciés. Je suis un peu gâté par l'utilisation d'étiquettes dans la sortie SAS et je ne connais pas très bien les pandas, numpy, scipy ou scikit-learn.


Éditer:

Il semble donc que sklearn ne fonctionnera pas directement sur une trame de données pandas. Disons que je le convertis en un tableau numpy:

npa = frame2.values
npa

Voici ce que j'obtiens:

Out[52]:
array([[  8.45300000e+01,   4.20730000e+02,   1.99443000e+05,
          7.94000000e+02,   1.21100000e+02],
       [  2.12500000e+01,   2.73810000e+02,   4.31180000e+04,
          1.69000000e+02,   6.28500000e+01],
       [  3.38200000e+01,   3.73870000e+02,   7.07290000e+04,
          2.79000000e+02,   3.53600000e+01],
       ..., 
       [  4.71400000e+01,   3.55890000e+02,   1.02597000e+05,
          4.07000000e+02,   3.25200000e+01],
       [  1.40100000e+01,   3.04970000e+02,   2.56270000e+04,
          9.90000000e+01,   7.32200000e+01],
       [  3.85300000e+01,   3.73230000e+02,   8.02200000e+04,
          3.17000000e+02,   4.32300000e+01]])

Si je change ensuite le copyparamètre du PCA de sklearn pour False,qu'il opère directement sur le tableau, selon le commentaire ci-dessous.

pca = PCA(n_components=5,copy=False)
pca.fit(npa)

npa

Par la sortie, il semble avoir remplacé toutes les valeurs au npalieu d'ajouter quoi que ce soit au tableau. Quelles sont les valeurs npaactuelles? Les scores des composants principaux pour le tableau d'origine?

Out[64]:
array([[  3.91846649e+01,   5.32456568e+01,   1.03614689e+05,
          4.06726542e+02,   6.59830027e+01],
       [ -2.40953351e+01,  -9.36743432e+01,  -5.27103110e+04,
         -2.18273458e+02,   7.73300268e+00],
       [ -1.15253351e+01,   6.38565684e+00,  -2.50993110e+04,
         -1.08273458e+02,  -1.97569973e+01],
       ..., 
       [  1.79466488e+00,  -1.15943432e+01,   6.76868901e+03,
          1.97265416e+01,  -2.25969973e+01],
       [ -3.13353351e+01,  -6.25143432e+01,  -7.02013110e+04,
         -2.88273458e+02,   1.81030027e+01],
       [ -6.81533512e+00,   5.74565684e+00,  -1.56083110e+04,
         -7.02734584e+01,  -1.18869973e+01]])
Argile
la source
1
Dans scikit-learn, chaque échantillon est stocké sous forme de ligne dans votre matrice de données. La classe PCA opère directement sur la matrice de données, c'est-à-dire qu'elle s'occupe du calcul de la matrice de covariance , puis de ses vecteurs propres. Concernant vos 3 dernières questions, oui, les composants_ sont les vecteurs propres de la matrice de covariance, la variance_expliquée_ratio_ est la variance expliquée par chaque PC, et la variance expliquée doit correspondre aux valeurs propres.
lightalchemist
@lightalchemist Merci pour la clarification. Avec sklearn, est-il approprié de créer une nouvelle trame de données avant d'effectuer l'ACP, ou est-il possible d'envoyer la trame de données pandas `` complète '' et de ne pas la faire fonctionner dans la colonne (réponse) la plus à gauche?
Clay
J'ai ajouté un peu plus d'informations. Si je convertis d'abord en un tableau numpy puis que j'exécute PCA avec copy=False, j'obtiens de nouvelles valeurs. S'agit-il des scores des composantes principales?
Clay
Je ne connais pas très bien les Pandas, je n'ai donc pas de réponse à cette partie de votre question. En ce qui concerne la deuxième partie, je ne pense pas qu'ils soient le principal élément. Je pense que ce sont les échantillons de données d'origine mais avec la moyenne soustraite. Cependant, je ne peux pas vraiment en être sûr.
lightalchemist

Réponses:

15

Scikit-learn n'a pas d'implémentation combinée de PCA et de régression comme par exemple le package pls dans R. Mais je pense que l'on peut faire comme ci-dessous ou choisir la régression PLS.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from sklearn.preprocessing import scale
from sklearn.decomposition import PCA
from sklearn import cross_validation
from sklearn.linear_model import LinearRegression

%matplotlib inline

import seaborn as sns
sns.set_style('darkgrid')

df = pd.read_csv('multicollinearity.csv')
X = df.iloc[:,1:6]
y = df.response

Scikit-learn PCA

pca = PCA()

Mettez à l'échelle et transformez les données pour obtenir les principaux composants

X_reduced = pca.fit_transform(scale(X))

Écart (% cumulé) expliqué par les principales composantes

np.cumsum(np.round(pca.explained_variance_ratio_, decimals=4)*100)

array([  73.39,   93.1 ,   98.63,   99.89,  100.  ])

Il semble que les deux premières composantes expliquent en effet la majeure partie de la variance des données.

CV 10 fois, avec mélange

n = len(X_reduced)
kf_10 = cross_validation.KFold(n, n_folds=10, shuffle=True, random_state=2)

regr = LinearRegression()
mse = []

Faire un CV pour obtenir MSE juste pour l'interception (pas de composants principaux dans la régression)

score = -1*cross_validation.cross_val_score(regr, np.ones((n,1)), y.ravel(), cv=kf_10, scoring='mean_squared_error').mean()    
mse.append(score) 

Faire un CV pour les 5 composants principaux, en ajoutant un composant à la régression à la fois

for i in np.arange(1,6):
    score = -1*cross_validation.cross_val_score(regr, X_reduced[:,:i], y.ravel(), cv=kf_10, scoring='mean_squared_error').mean()
    mse.append(score)

fig, (ax1, ax2) = plt.subplots(1,2, figsize=(12,5))
ax1.plot(mse, '-v')
ax2.plot([1,2,3,4,5], mse[1:6], '-v')
ax2.set_title('Intercept excluded from plot')

for ax in fig.axes:
    ax.set_xlabel('Number of principal components in regression')
    ax.set_ylabel('MSE')
    ax.set_xlim((-0.2,5.2))

entrez la description de l'image ici

Régression PLS Scikit-learn

mse = []

kf_10 = cross_validation.KFold(n, n_folds=10, shuffle=True, random_state=2)

for i in np.arange(1, 6):
    pls = PLSRegression(n_components=i, scale=False)
    pls.fit(scale(X_reduced),y)
    score = cross_validation.cross_val_score(pls, X_reduced, y, cv=kf_10, scoring='mean_squared_error').mean()
    mse.append(-score)

plt.plot(np.arange(1, 6), np.array(mse), '-v')
plt.xlabel('Number of principal components in PLS regression')
plt.ylabel('MSE')
plt.xlim((-0.2, 5.2))

entrez la description de l'image ici

Jordi
la source
7

Voici SVD en Python et NumPy uniquement (des années plus tard).
(Cela ne répond pas du tout à vos questions sur SSA / sklearn / pandas, mais peut aider un pythoniste un jour.)

#!/usr/bin/env python2
""" SVD straight up """
# geometry: see http://www.ams.org/samplings/feature-column/fcarc-svd

from __future__ import division
import sys
import numpy as np

__version__ = "2015-06-15 jun  denis-bz-py t-online de"

# from bz.etc import numpyutil as nu
def ints( x ):
    return np.round(x).astype(int)  # NaN Inf -> - maxint

def quantiles( x ):
    return "quantiles %s" % ints( np.percentile( x, [0, 25, 50, 75, 100] ))


#...........................................................................
csvin = "ccheaton-multicollinearity.csv"  # https://gist.github.com/ccheaton/8393329
plot = 0

    # to change these vars in sh or ipython, run this.py  csvin=\"...\"  plot=1  ...
for arg in sys.argv[1:]:
    exec( arg )

np.set_printoptions( threshold=10, edgeitems=10, linewidth=120,
    formatter = dict( float = lambda x: "%.2g" % x ))  # float arrays %.2g

#...........................................................................
yX = np.loadtxt( csvin, delimiter="," )
y = yX[:,0]
X = yX[:,1:]
print "read %s" % csvin
print "y %d  %s" % (len(y), quantiles(y))
print "X %s  %s" % (X.shape, quantiles(X))
print ""

#...........................................................................
U, sing, Vt = np.linalg.svd( X, full_matrices=False )
#...........................................................................

print "SVD: %s -> U %s . sing diagonal . Vt %s" % (
        X.shape, U.shape, Vt.shape )
print "singular values:", ints( sing )
    # % variance (sigma^2) explained != % sigma explained, e.g. 10 1 1 1 1

var = sing**2
var *= 100 / var.sum()
print "% variance ~ sing^2:", var

print "Vt, the right singular vectors  * 100:\n", ints( Vt * 100 )
    # multicollinear: near +- 100 in each row / col

yU = y.dot( U )
yU *= 100 / yU.sum()
print "y ~ these percentages of U, the left singular vectors:", yU


-> journal

# from: test-pca.py
# run: 15 Jun 2015 16:45  in ~bz/py/etc/data/etc  Denis-iMac 10.8.3
# versions: numpy 1.9.2  scipy 0.15.1   python 2.7.6   mac 10.8.3

read ccheaton-multicollinearity.csv
y 373  quantiles [  2823  60336  96392 147324 928560]
X (373, 5)  quantiles [     7     47    247    573 512055]

SVD: (373, 5) -> U (373, 5) . sing diagonal . Vt (5, 5)
singular values: [2537297    4132    2462     592      87]
% variance ~ sing^2: [1e+02 0.00027 9.4e-05 5.4e-06 1.2e-07]
Vt, the right singular vectors  * 100:
[[  0   0 100   0   0]
 [  1  98   0 -12  17]
 [-10 -11   0 -99  -6]
 [  1 -17   0  -4  98]
 [-99   2   0  10   2]]
y ~ these percentages of U, the left singular vectors: [1e+02 15 -18 0.88 -0.57]
denis
la source
Je suis un peu en retard à la fête mais bonne réponse
plumbus_bouquet
3

Essayez d'utiliser un pipeline pour combiner l'analyse des composants principaux et la régression linéaire:

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline

# Principle components regression
steps = [
    ('scale', StandardScaler()),
    ('pca', PCA()),
    ('estimator', LinearRegression())
]
pipe = Pipeline(steps)
pca = pipe.set_params(pca__n_components=3)
pca.fit(X, y)
Joe
la source
3

Ma réponse arrive presque cinq ans en retard et il y a de fortes chances que vous n'ayez plus besoin d'aide pour faire de la PCR en Python. Nous avons développé un package Python nommé hoggorm qui fait exactement ce dont vous aviez besoin à l'époque. Veuillez consulter les exemples de PCR ici . Il existe également un package de traçage complémentaire nommé hoggormplot pour la visualisation des résultats calculés avec hoggorm.

oli
la source