Análisis de componentes principales y regresión en Python

11

Estoy tratando de descubrir cómo reproducir en Python algunos trabajos que hice en SAS. Usando este conjunto de datos , donde la multicolinealidad es un problema, me gustaría realizar análisis de componentes principales en Python. He visto scikit-learn y statsmodels, pero no estoy seguro de cómo tomar su salida y convertirla a la misma estructura de resultados que SAS. Por un lado, SAS parece realizar PCA en la matriz de correlación cuando se usa PROC PRINCOMP, pero la mayoría (¿todas?) De las bibliotecas de Python parecen usar SVD.

En el conjunto de datos , la primera columna es la variable de respuesta y las siguientes 5 son variables predictivas, llamadas pred1-pred5.

En SAS, el flujo de trabajo general es:

/* 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;

Sé que el último paso solo funciona porque solo elijo PC1 y PC2, en orden.

Entonces, en Python, esto es lo más lejos que he llegado:

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 cantidad de variación que explica cada PC?

print pca.explained_variance_ratio_

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

¿Que son estos? Vectores propios?

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]])

¿Son estos los valores propios?

print pca.explained_variance_

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

No sé cómo pasar de los resultados de Python a realizar la Regresión de componentes principales (en Python). ¿Alguna de las bibliotecas de Python llena los espacios en blanco de manera similar a SAS?

Cualquier consejo es apreciado. Estoy un poco mimado por el uso de etiquetas en la salida SAS y no estoy muy familiarizado con pandas, numpy, scipy o scikit-learn.


Editar:

Entonces, parece que sklearn no funcionará directamente en un marco de datos de pandas. Digamos que lo convierto en una matriz numpy:

npa = frame2.values
npa

Esto es lo que obtengo:

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 luego cambio el copyparámetro de PCA de sklearn para False,que funcione directamente en la matriz, según el comentario a continuación.

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

npa

Según la salida, parece que reemplazó todos los valores en npalugar de agregar nada a la matriz. ¿Cuáles son los valores npaahora? ¿Las puntuaciones del componente principal para la matriz original?

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]])
Arcilla
fuente
1
En scikit-learn, cada muestra se almacena como una fila en su matriz de datos. La clase PCA opera directamente en la matriz de datos, es decir, se encarga de calcular la matriz de covarianza y luego sus vectores propios. Con respecto a sus 3 preguntas finales, sí, los componentes son los vectores propios de la matriz de covarianza, la razón de varianza explicada es la varianza que explica cada PC, y la varianza explicada debe corresponder a los valores propios.
lightalchemist
@lightalchemist Gracias por la aclaración. Con sklearn, ¿es apropiado crear un nuevo marco de datos antes de realizar el PCA, o es posible enviar el marco de datos de pandas 'completo' y no operar en la columna de la izquierda (respuesta)?
Clay el
Agregué un poco más de información. Si primero me convierto a una matriz numpy y luego ejecuto PCA con copy=False, obtengo nuevos valores. ¿Son esos los puntajes del componente principal?
Clay el
No estoy tan familiarizado con Pandas, así que no tengo una respuesta a esa parte de su pregunta. Con respecto a la segunda parte, no creo que sean el componente principal. Creo que son las muestras de datos originales pero con la media restada. Sin embargo, no puedo estar realmente seguro de eso.
lightalchemist

Respuestas:

15

Scikit-learn no tiene una implementación combinada de PCA y regresión como, por ejemplo, el paquete pls en R. Pero creo que uno puede hacer lo siguiente o elegir la regresión 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()

Escale y transforme datos para obtener Componentes principales

X_reduced = pca.fit_transform(scale(X))

Variación (% acumulativo) explicada por los componentes principales

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

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

Parece que los primeros dos componentes explican la mayor parte de la varianza en los datos.

CV 10 veces, con barajado

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

regr = LinearRegression()
mse = []

Haga un CV para obtener MSE solo para la intercepción (sin componentes principales en regresión)

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) 

Haga CV para los 5 componentes principales, agregando un componente a la regresión en el momento

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))

ingrese la descripción de la imagen aquí

Regresión de Scikit-learn PLS

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))

ingrese la descripción de la imagen aquí

Jordi
fuente
7

Aquí está SVD solo en Python y NumPy (años después).
(Esto no responde a sus preguntas sobre SSA / sklearn / pandas en absoluto, pero puede ayudar a un pitonista algún día).

#!/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


-> registro

# 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
fuente
Llego
3

Intente usar una tubería para combinar el análisis de componentes principales y la regresión lineal:

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
fuente
3

Mi respuesta llega casi cinco años tarde y hay una buena posibilidad de que ya no necesite ayuda para realizar la PCR en Python. Hemos desarrollado un paquete de Python llamado hoggorm que hace exactamente lo que necesitabas en ese momento. Por favor, eche un vistazo a los ejemplos de PCR aquí . También hay un paquete de trazado complementario llamado hoggormplot para la visualización de resultados calculados con hoggorm.

oli
fuente