Analisis Componentes Python
Analisis Componentes Python
Principales (PCA)
Qué es?
Cómo Funciona?
Supongamos que tenemos un dataset con sólo 2 variables (x1 y x2) y 3 observaciones (A, B y
C):
Observación x1 x2
A 2 4
B 0 0
C -2 -4
Vemos que x2 es el doble de x1 , es decir, ambas variables esta correlacionadas. PCA detectará
esta correlación.
Realizamos este proceso para que PCA no esté sesgado por las escalas de las variables.
𝑧 𝜎
Utilizamos la fórmula de la desviación estándar ( ) y de la varianza poblacional ( ) ya que son
pocos datos:
𝑧 = 𝑥 −𝜎 𝑥
⎯⎯⎯
⎯1⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
𝜎 = √ 𝑛 ∑(𝑥𝑖 − 𝑥) ⎯⎯
⎯ 2
Calculamos la media y desviación estándar de cada variable:
Para x1
(2-0)^2 = 4
(0-0)^2 = 0
(-2-0)^2 = 4
Promedia lo cuadrados dividiendo entre n
Para x2
(4-0)^2 = 16
(0-0)^2 = 0
(-4-0)^2 = 16
Promedia lo cuadrados dividiendo entre n
B 0 0
C -1.225 -1.225
Es una matriz cuadrada donde cada valor muestra cuánto varían dos variables juntas: Los
valores de la diagonal representan la varianza de cada variable, mientras que los valores que
estan fuera de la diagonal representan la covarianza entre pares de variables.
PCA busca encontrar las direcciones (componentes) donde los datos tienen mayor varianza y
menor redundancia. Para eso, necesita saber:
𝑍:
Tomamos los datos estandarizados y formamos la matriz
1.225 1.225
𝑍 = 0 0
−1.225 −1.225
La fórmula de la matriz de covarianza viene dada por:
𝐶𝑜𝑣 = 𝑛 −1 1 𝑍 𝑇 𝑍
donde:
𝑍 𝑇 : es la transpuesta de 𝑍 .
𝑛: es el número de observaciones (en este caso, 3).
Tranponemos la matriz 𝑍 , pasando filas a columnas y viceversa.
𝑍 𝑇 = [ 1.225 0 −1.225
1.225 0 −1.225 ]
Realizamos la operación de multiplicación de matrices
[1,1]: (1.225)(1.225)+0+(−1.225)(-1.225)=1.5+0+1.5=3
[1,2]: igual que [1,1] ⇒ 3
[2,1]: también 3 (simétrico)
[2,2]: también 3
Entonces,
𝑍 𝑇 ⋅ 𝑍 = [ 33 33 ]
Ahora, dividimos por 𝑛 = 3 y obtenemos la Matriz de Covarianza
𝐶𝑜𝑣 = 𝐶 = [ 11 11 ]
Paso 3: Calcular autovalores y autovectores
Los autovalores nos dicen cuánta varianza captura cada componente; Mientras que lo
autovectores indican la dirección de cada componente.
𝐶 = [ 11 11 ] ,𝐈 = [ 10 01 ]
Entonces,
|| 1 − 𝜆 1 || = (1 − 𝜆)2 − 1 = 0
|| 1 1 − 𝜆 ||
Resolviendo la matriz tenemos:
(1 − 𝜆)2 = 1 ⇒ 1 − 𝜆 = ±1
Por tanto, los autovalores son:
𝜆1 = 2, 𝜆2 = 0
Ahora, procedemos a calcular los autovectores 𝐯1 y 𝐯2 :
Para 𝜆1 = 2
(𝐂 − 𝜆1 𝐈)𝐯1 = 0
−1 1 𝐯1 0
[ 1 −1 ] ⋅ [ 𝐯 ] = [ 0 ]
2
nos lleva a la ecuación,
−𝑣 1 + 𝑣 2 = 0 ⇒ 𝑣 1 = 𝑣 2
Entonces,
𝐯1 = [ 11 ]
Normalizando, obtenemos el autorvector 𝐯1 ,
𝐯1 = √1⎯⎯2 [ 11 ] = [ 0.707
0.707 ]
Para 𝜆2 = 0
(𝐂 − 𝜆2 𝐈)𝐯2 = [ 11 11 ] ⋅ [ 𝐯𝐯12 ] = [ 00 ]
nos lleva a la ecuación,
𝑣 1 + 𝑣 2 = 0 ⇒ 𝑣 1 = −𝑣 2
Un vector solución sería,
𝐯2 = [ −11 ]
Normalizando, obtenemos el autorvector 𝐯2
𝐯2 = √1⎯⎯2 [ 11 ] = [ 0.707
0 707 ]
Paso 4: Tranformar los datos
Proyectamos los datos en los nuevos ejes dados por los componentes principales. Para ello,
utilizamos la siguiente fórmula:
Sólo la primer componente (𝑃𝐶1) tiene valores distinto de cero. Esto indica que toda la
información esta en 1 sola dimensión.
La segunda componente (𝑃𝐶2) no agrega nada útil: toda su varianza es cero.
Esto implica que podemos reducir de 2 a 1 dimensión sin perder información.
Vamos a trabaja con los datos de Iris que consta de cuatro características: longitud del sépalo,
ancho del sépalo, longitud del pétalo y ancho del pétalo. Utilizamos el análisis de componentes
principales (PCA) para proyectar este espacio de cuatro características en un espacio
tridimensional.
Cargar datos
In [2]: iris.frame['target']
Out[2]: 0 0
1 0
2 0
3 0
4 0
..
145 2
146 2
147 2
148 2
149 2
Name: target, Length: 150, dtype: int64
El target esta identificado con números enteros para cada especie. Vamos a renombrar el target
con el nombre real de las especies.
Out[3]: 0 setosa
1 setosa
2 setosa
3 setosa
4 setosa
...
145 virginica
146 virginica
147 virginica
148 virginica
149 virginica
Name: target, Length: 150, dtype: object
Considerando únicamente estas dos dimensiones (ancho y largo del sépalo), aún
existe una superposición entre los tipos Versicolor y Virginica.
Observamos que el ancho y la longitud del pétalo son las características más
discriminantes para los tres tipos.
Apliquemos un Análisis de Componentes Principales (PCA) al conjunto de datos del iris para
pasar de 4 a 3 dimensiones y luego grafiquemos los iris en las tres primeras dimensiones del
PCA. Esto nos permitirá diferenciar mejor los tres tipos.
In [5]: from sklearn.decomposition import PCA
# aplicar PCA para reducir a 3d
pca = PCA(n_components=3)
X_reduce = pca.fit_transform(iris.data)
# mostrar primeras 3 transformaciones
X_reduce[:3]
In [8]: pca.explained_variance_ratio_
Vemos que el 92% de la variabilidad de los datos es explicada por el primer PCs.
Esto sugiere que una sóla componente explica la variación del 92% de los datos.
Entendiendo PCA desde la intuición
Vamos a generar un pequeño conjunto de datos 3D, con forma ovalada, rotada y con puntos
distribuidos de forma irregular con bastante ruido.
𝑋 = 𝑈Σ𝑉 𝑇
donde
Vamos a obtener las componentes principales del conjunto de entrenamiento anterior con la
función svd() de Numpy. Para ello los datos deben esta centrados.
El objetivo es llevar los datos de 3 a 2 dimensiones, como se muestra en las graficas anteriores,
encontrando el plano de proyección que mejor se ajuste a los datos. Dicho plano viene definido
por las dos primeras PCs.
In [8]: import numpy as np
# resta la media de cada columna a todos los datos para centrarlos
X_centered = X - X.mean(axis=0)
# aplicamos SVD
U, s, Vt = np.linalg.svd(X_centered)
# obtenemos las dos primeras PCs
c1 = Vt[0]
c2 = Vt[1]
print('Primera componente principal: ', c1)
print('Segunda componente principal: ', c2)
𝑠 Σ 𝑠
La función svd() devuelve en vez de , donde ' ' es el vector que contiene todos los
Σ Σ
autovalores en la diagonal principal de las n primeras filas de . Dado que está lleno de ceros
𝑠
en el resto de la matriz, se puede reconstruir fácilmente a partir de , de la siguiente manera:
Esta librería implementa PCA centrando los datos automáticamente. Vamos a reducir la
dimensionalidad del conjunto de datos a 2 dimensiones, como hemos hecho en el pasado.
In [ ]: # importamos el metodo
from sklearn.decomposition import PCA
# instanciamos el objeto PCA
pca = PCA(n_components=2)
# reducir datos a 2 dimensiones
X2D = pca.fit_transform(X)
# mostrar primeros 5 datos transformados
print(X2D[:5])
[[-0.87323119 0.29459803]
[ 0.14888518 -0.51493557]
[ 1.35121872 0.39950155]
[ 0.45436676 0.1399845 ]
[-0.73438909 0.02289346]]
In [17]: pca.components_
Notemos que las componentes pricipales dadas por PCA son las misma
calculadas con la funcion SVD, son dos metodos para llegar al mismo resultado.
Indica cuánta proporción de la varianza total de los datos es explicada por cada componente
principal. Es una medida fundamental para comprender qué tan bien resumen los datos cada
componente en una reducción de dimensionalidad.
El índice de varianza explicada es un vector de proporciones
𝜆1 ,≥ 𝜆2 ,≥...≥ 𝜆𝑛
Y finalmente se procede a calcular el índice de varianza explicada, con la siguiente fórmula, para
cada componente:
In [18]: pca.explained_variance_ratio_
Out[19]: 0.09028309326742034
El restante 9% para la tercera PC, por lo que esta aporta poca información y no
se toma en cuenta ya que corresponde a la dimension que se reduce.
Primera Forma
Para elegir el número de dimensiones que mayor variabilidad capture de los datos, sumamos una
porcion suficientemente grande de la varianza explicada, suponga el 95% y se usa la cantidad de
componentes principales que correspondan.
En el siguiente ejemplo, trabajamos con los datos anteriores, implementamos PCA sin reducir la
dimensionalidad y luego se calcula el número mínimo de dimensiones necesarias para preservar
el 90% de la varianza.
Out[20]: 2
Nos indica que las 2 primeras componentes principales son las necesarias para
obtener el 90% de variabilidad.
Luego, podemos configurar ncomponents_ con el valor encontrado y ejecutar PCA nuevamente.
Segunda Forma
También podemos pasarle como valor flotante el porcentaje de varianza que requerimos
directamente en el parámetro ncomponentes_ de PCA. Esto hace que la cantidad de
componentes se almacene en el atributo ncomponents_.
Out[21]: 2
Otra opción es trazar la varianza explicada como función del número de dimensiones.
Generalmente habrá un codo en la curva, donde la varianza explicada deja de crecer
rápidamente. La siguiente gráfica corresponde a la varianza en función de las dimensiones para
el conjunto de datos MNIST que son imágenes constituidas por 784 píxeles (features). Se
evidencia que reducir la dimensionalidad a 154 dimensiones no peredería demasiada varianza
explicada.
Para ver de qué se trata la compresión de datos, volvamos al ejemplo de PCA en los datos de Iris
sabiendo que podemos reducirlos a 1 dimensión ya que un sólo PCs contiene más del 90% de la
variación.
In [24]: from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
import pandas as pd
import seaborn as sns
# as_frame, muetra los datos en formato df
iris = load_iris(as_frame=True)
# agregar al dataframe las etiquetas
iris.frame['target'] = iris.target_names[iris.target]
# visualizar la distribucion de cada par de variables
_ = sns.pairplot(iris.frame, hue='target')
Out[26]: array([[-2.68412563],
[-2.71414169],
[-2.88899057]])
Ahora, volvamos a los datos originales descomprimiendo los datos reducidos por PCA.
Otro ejemplo es aplicar PCA al conjunto de datos MNIST para pasar de 784 a 154 características.
Esta reducción preseva el 95% de varianza de los datos llegando a menos del 20% de su tamaño
original. Esta es una relación de compresion razonable lo cual aceleraría enormemente un
algoritmo de clasificación.
La siguiente imagen muestra algunos digitos de MNIST del conjunto de entrenamiento original y
los digitos después de la compresión y descompresión. Se nota una ligera pérdida de calidad de
la imagen, pero los dígitos se mantienen intactos y diferenciables.
PCA Incremental
IPCA se utiliza generalmente como sustituto del análisis de componentes principales (PCA)
cuando el conjunto de datos a descomponer es demasiado grande para caber en la memoria. Si
bien sigue dependiendo de las características de los datos de entrada, modificar el tamaño del
lote permite controlar el uso de la memoria.
PCA requiere que todos el conjunto de entrenamiento quepa en la memoria para que se pueda
ejecutar el algoritmo. Es por esto que surge IPCA, ya que permite dividir los datos en minilotes y
alimentar el algoritmo en un minilote a la vez.
PCA vs IPCA
El siguiente ejemplo muestra de forma visual que el IPCA es capaz de encontrar una proyección
de datos similar a la del PCA, procesando solo unas pocas muestras a la vez.
Vamos a utilizar los datos de Iris pero en este caso vamos a pasar de 4 a 2 dimensiones.
In [28]: import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA, IncrementalPCA
# dividimos los datos en features y target
X = iris.data
y = iris.target
# Aplicamos IPCA
n_componentes = 2
ipca = IncrementalPCA(n_components=n_componentes, batch_size=10)
X_ipca = ipca.fit_transform(X)
# Aplicamos PCA
pca = PCA(n_components=n_componentes)
X_pca = pca.fit_transform(X)
# lista de colores para cada clase
colores = ['navy', 'turquoise', 'darkorange']
# creacion de visualizaciones
for X_tranformed, title in [(X_ipca, 'IPCA'), (X_pca, 'PCA')]:
plt.figure(figsize=(5,5))
for color, i, target_name in zip(colores, [0,1,2], iris.target_names):
plt.scatter(
X_tranformed[y == i, 0],
X_tranformed[y == i, 1],
color = color,
lw = 2,
label = target_name
)
if 'IPCA' in title:
err = np.abs(np.abs(X_pca) - np.abs(X_ipca)).mean()
plt.title(title + ' del dataset Iris\nError Absoluto Medio sin signo %.6
else:
plt.title(title + ' del dataset Iris')
División en Minilotes
Este set de datos presenta 784 variables, por lo que se redujo de 154
componentes de forma exitosa sin saturar la memoria del computador.
Usando memmap
También podemos usar la clase memmap de Numpy, que permite manipular una matriz grande
almacenada en un archivo binario en el disco como si estuviera completamente en la memoria.
La clase carga sólo los datos que necesita en la memoria, cuando los necesita.
Procedemos a cargar el archivo memmap y usarlo como una matriz NumPy normal. Dado que
este algoritmo utiliza sólo una pequeña parte de la matriz en un momento dado, el uso de la
memoria permanece bajo control. Esto hace posible llamar al método fit() habitual en lugar de
partial_fit(), lo cual es bastante conveniente:
Kernel PCA
Es una extensión del Análisis de Componentes Principales (PCA) que permite aplicar la técnica a
datos no linealmente separables. Mientras que el PCA tradicional proyecta los datos en una base
lineal, el Kernel PCA transforma primero los datos a un espacio de mayor dimensión (no lineal) y
luego aplica PCA en ese nuevo espacio.
En qué consiste?
1. KPCA utiliza una función kernel para mapear los datos a un espacio de características de
mayor dimensión sin hacerlo explícitamente. Esto se conoce como el "truco del kernel".
2. Una vez en ese nuevo espacio (a través del kernel), aplica PCA como si fuera lineal, pero
ahora puede capturar patrones no lineales.
3. No necesita conocer la función de mapeo gracias al truco del kernel, sólo necesitas calcular
productos escalares entre puntos en el espacio transformado, lo cual se hace usando la
función kernel directamente.
El tipo de kernel a utilizar va a depender de la forma en que se distribuyen los datos y entre los
más comunes tenemos:
Lineal: equivalente a PCA estándar. En scikit-learn 'linear'. Útil en datos que sean
linealmente separables. Es el que utiliza por defecto PCA.
Polinomial: permite capturar relaciones polinómicas no lineales. En scikit-learn 'poly'.
RBF (Radial Basis Function o Gaussiano): muy útil para datos con separación no lineal. En
scikit-learn 'rbf'.
Sigmoide: funciona cuando los datos tienen transiciones suaves como las que modelan las
redes neuronales. En scikit-learn 'sigmoid'.
Kernel personalizado: Cuando necesitas un kernel específico para tu problema (por
ejemplo, kernels basados en texto, grafos, o series temporales).
Vamos a mostrar la ventaja de usar un kernel al proyectar datos mediante un PCA. Creamos un
conjunto de datos compuesto por dos circulos anidados en el que claramente se tratan de datos
no lineales.
Ahora, vamos a implementar PCA y Kernel PCA para ver su efectos. Para el kernel utilizamos 'rbf'
ya que concuerda con la forma que presentan los datos.
In [14]: from sklearn.decomposition import PCA, KernelPCA
# definimos PCA
pca = PCA(n_components=2)
# definimos KPCA
kpca = KernelPCA(
n_components=None,
kernel='rbf',
gamma=9, # controla la forma del kernel
fit_inverse_transform=True, # permite aroximar el mapeo inverso dese el espa
alpha=0.1 # regula la reconstruccion inversa
)
# obtener version reducida linealmente de los datos de prueba
X_test_pca = pca.fit(X_train).transform(X_test)
# obtener version reducida en un espacio no lineal transformado de los datos de
X_test_kpca = kpca.fit(X_train).transform(X_test)
The Kernel crashed while executing code in the current cell or a previous cell.
Please review the code in the cell(s) to identify a possible cause of the failu
re.