tutoriales.com

Análisis de Componentes Principales (PCA) con NumPy y Pandas: Reducción de Dimensionalidad para Datos Tabulares 📊

Este tutorial te guiará paso a paso a través de la implementación del Análisis de Componentes Principales (PCA) utilizando las librerías NumPy y Pandas. Aprenderás a reducir la dimensionalidad de tus conjuntos de datos, facilitando la visualización y mejorando la eficiencia de tus modelos de Machine Learning. Cubriremos desde la teoría básica hasta ejemplos prácticos con código.

Intermedio20 min de lectura7 views
Reportar error

Introducción al Análisis de Componentes Principales (PCA) ✨

En el vasto universo de la ciencia de datos, a menudo nos enfrentamos a conjuntos de datos con una cantidad abrumadora de características o dimensiones. Trabajar con alta dimensionalidad puede ser un desafío: puede ralentizar los algoritmos, dificultar la visualización y, en ocasiones, introducir ruido o redundancia en los datos. Aquí es donde entra en juego el Análisis de Componentes Principales (PCA), una técnica fundamental de reducción de dimensionalidad.

PCA es una técnica estadística no paramétrica utilizada para transformar un conjunto de variables posiblemente correlacionadas en un conjunto (más pequeño) de variables no correlacionadas llamadas componentes principales. La idea central es encontrar las direcciones (vectores) a lo largo de las cuales los datos varían más, proyectando así los datos en un nuevo subespacio de menor dimensión mientras se conserva la mayor cantidad de varianza posible.

¿Por qué es importante la Reducción de Dimensionalidad? 💡

La reducción de dimensionalidad es crucial por varias razones clave:

  • Visualización: Es difícil visualizar datos en más de tres dimensiones. PCA permite proyectar datos de alta dimensión en 2D o 3D, haciéndolos interpretables.
  • Eficiencia Computacional: Menos características significan menos cálculos, lo que acelera el entrenamiento y la predicción de modelos.
  • Reducción del Ruido: Al centrarse en las direcciones de mayor varianza, PCA puede ayudar a filtrar el ruido inherente a las características menos informativas.
  • Mitigación de la Maldición de la Dimensionalidad: Evita problemas asociados con espacios de alta dimensión donde los datos se vuelven dispersos, lo que dificulta la detección de patrones.
  • Preprocesamiento para Machine Learning: Mejora el rendimiento de algunos algoritmos de aprendizaje automático al proporcionarles un conjunto de características más compacto y menos correlacionado.
📌 Nota: PCA es una técnica de aprendizaje no supervisado, lo que significa que no utiliza etiquetas de clase para realizar la reducción de dimensionalidad.

Fundamentos Teóricos del PCA 📖

Antes de sumergirnos en el código, es fundamental comprender los conceptos matemáticos detrás de PCA. No te preocupes, lo haremos de forma accesible.

1. Centrado de los Datos

El primer paso en PCA es centrar los datos. Esto significa restar la media de cada característica (columna) a cada una de sus observaciones. Esto asegura que el origen del nuevo sistema de coordenadas esté en el centro de los datos.

$$X_{centrado} = X - \mu$$

Donde $X$ es la matriz de datos y $\mu$ es el vector de medias de las columnas.

2. Cálculo de la Matriz de Covarianza

La matriz de covarianza es crucial porque mide cómo se relacionan entre sí las diferentes características. Una covarianza positiva indica que dos características tienden a aumentar o disminuir juntas, mientras que una covarianza negativa indica que una aumenta mientras la otra disminuye. La covarianza cero sugiere que no hay una relación lineal.

Para un conjunto de datos centrado $X_{centrado}$ con $n$ muestras y $p$ características:

$$\text{Matriz de Covarianza} = \frac{1}{n-1} X_{centrado}^T X_{centrado}$$

3. Cálculo de Eigenvalores y Eigenvectores

Aquí es donde reside el corazón de PCA. Los eigenvectores de la matriz de covarianza son las direcciones (o componentes principales) a lo largo de las cuales los datos varían más. Los eigenvalores asociados a cada eigenvector nos dicen cuánta varianza hay en esa dirección.

  • El eigenvector con el mayor eigenvalor es el primer componente principal, que captura la mayor cantidad de varianza.
  • El segundo eigenvector con el segundo mayor eigenvalor es el segundo componente principal, ortogonal al primero, y así sucesivamente.

$$A \mathbf{v} = \lambda \mathbf{v}$$

Donde $A$ es la matriz de covarianza, $\mathbf{v}$ es un eigenvector y $\lambda$ es su eigenvalor correspondiente.

4. Selección de Componentes Principales

Una vez que tenemos los eigenvalores y eigenvectores, los ordenamos de mayor a menor eigenvalor. Luego, seleccionamos los $k$ primeros eigenvectores que corresponden a los $k$ mayores eigenvalores. Estos $k$ eigenvectores formarán la nueva base para nuestros datos.

💡 Consejo: Un método común para seleccionar el número de componentes es analizar la *varianza explicada acumulada* o utilizar el 'codo' en un gráfico de eigenvalores (scree plot).

5. Proyección de los Datos

Finalmente, proyectamos nuestros datos centrados originales sobre el subespacio definido por los $k$ eigenvectores seleccionados. Esto da como resultado un nuevo conjunto de datos de menor dimensión, donde cada columna es un componente principal.

$$X_{PCA} = X_{centrado} \times \text{Eigenvectores seleccionados}$$

Datos Originales (Alta Dimensión) Centrado de Datos Cálculo Matriz Covarianza Cálculo Eigenvalores/vectores Selección de k Componentes Proyección de Datos Datos Reducidos (Baja Dimensión) Procesamiento PCA

Preparando el Entorno 🛠️

Para este tutorial, necesitaremos las librerías NumPy para operaciones numéricas eficientes y Pandas para la manipulación de datos tabulares. Matplotlib y Seaborn serán útiles para la visualización.

Primero, asegúrate de tenerlas instaladas:

pip install numpy pandas matplotlib seaborn scikit-learn

Luego, importa las librerías necesarias en tu script o cuaderno Jupyter:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler

%matplotlib inline

sns.set_style("whitegrid")

Generando Datos de Ejemplo 🧪

Para ilustrar el PCA, crearemos un conjunto de datos sintético con varias características y cierta correlación entre ellas. Esto nos permitirá observar cómo PCA identifica las direcciones de mayor varianza.

Supongamos que estamos analizando características de productos, como longitud, ancho, altura, peso y volumen, donde algunas de ellas están inherentemente correlacionadas.

# Establecer una semilla para reproducibilidad
np.random.seed(42)

# Número de muestras
n_samples = 100

# Generar datos base
longitud = np.random.normal(10, 2, n_samples)
ancho = np.random.normal(5, 1, n_samples)
altura = np.random.normal(3, 0.5, n_samples)

# Crear correlaciones para peso y volumen
peso = 0.8 * longitud + 0.5 * ancho + np.random.normal(0, 0.5, n_samples)
volumen = 0.6 * longitud + 0.7 * altura + np.random.normal(0, 0.3, n_samples)

# Crear un DataFrame de Pandas
data = pd.DataFrame({
    'Longitud': longitud,
    'Ancho': ancho,
    'Altura': altura,
    'Peso': peso,
    'Volumen': volumen
})

print("Primeras 5 filas del dataset:")
print(data.head())
print("\nEstadísticas descriptivas:")
print(data.describe())

Observa las primeras filas y las estadísticas descriptivas para entender la distribución inicial de nuestros datos. Puedes ver que las escalas de las características son diferentes, lo cual es un factor importante a considerar.

Visualización de Correlaciones Iniciales 📉

Antes de aplicar PCA, es útil visualizar las correlaciones entre nuestras características. Esto nos dará una idea de qué características podrían ser redundantes o capturar información similar.

plt.figure(figsize=(8, 6))
sns.heatmap(data.corr(), annot=True, cmap='coolwarm', fmt=".2f")
plt.title('Matriz de Correlación de Datos Originales')
plt.show()

En el mapa de calor, verás que Longitud y Peso, así como Longitud y Volumen, y Altura y Volumen tienen correlaciones significativas. Esto sugiere que PCA podría ser muy efectivo aquí.


Implementación de PCA desde Cero con NumPy y Pandas 🚀

Ahora, implementaremos PCA paso a paso utilizando solo NumPy y Pandas, siguiendo los fundamentos teóricos que hemos discutido.

Paso 1: Escalar los Datos ⚖️

PCA es sensible a la escala de las características. Si una característica tiene un rango de valores mucho mayor que otra, dominará el cálculo de la varianza. Por lo tanto, es crucial estandarizar los datos antes de aplicar PCA. Usaremos StandardScaler de scikit-learn para este propósito, que centra los datos (media 0) y los escala (desviación estándar 1).

# Inicializar el StandardScaler
scaler = StandardScaler()

# Ajustar y transformar los datos
data_scaled = scaler.fit_transform(data)

# Convertir los datos escalados de nuevo a un DataFrame para facilitar la manipulación
data_scaled_df = pd.DataFrame(data_scaled, columns=data.columns)

print("Primeras 5 filas del dataset escalado:")
print(data_scaled_df.head())
print("\nMedias de las columnas escaladas (deberían ser cercanas a 0):")
print(data_scaled_df.mean())
print("\nDesviaciones estándar de las columnas escaladas (deberían ser cercanas a 1):")
print(data_scaled_df.std())

Paso 2: Calcular la Matriz de Covarianza 📈

Con los datos escalados, podemos calcular la matriz de covarianza. NumPy tiene una función np.cov() que puede hacer esto por nosotros. Recuerda que la función espera que las variables estén en las filas, por lo que transpondremos el DataFrame.

# Calcular la matriz de covarianza de los datos escalados
cov_matrix = np.cov(data_scaled_df.T)

# Convertir a DataFrame para mejor visualización
cov_matrix_df = pd.DataFrame(cov_matrix, columns=data.columns, index=data.columns)

print("\nMatriz de Covarianza de los datos escalados:")
print(cov_matrix_df)
⚠️ Advertencia: Si no escalas tus datos, características con grandes rangos pueden inflar desproporcionadamente los valores de covarianza, llevando a componentes principales sesgados.

Paso 3: Calcular Eigenvalores y Eigenvectores 🔮

Ahora, obtenemos los eigenvalores y eigenvectores de la matriz de covarianza. Estos nos darán la magnitud de la varianza y las direcciones de los componentes principales, respectivamente.

# Calcular eigenvalores y eigenvectores
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)

print("\nEigenvalores:")
print(eigenvalues)
print("\nEigenvectores (cada columna es un eigenvector):")
print(eigenvectors)

Paso 4: Ordenar Eigenpares y Calcular Varianza Explicada 📊

Los eigenvalores nos dicen cuánta varianza explica cada componente principal. Los ordenaremos de mayor a menor y calcularemos la varianza explicada y la varianza explicada acumulada para decidir cuántos componentes conservar.

# Crear una lista de pares (eigenvalor, eigenvector)
eigen_pairs = [(np.abs(eigenvalues[i]), eigenvectors[:, i]) for i in range(len(eigenvalues))]

# Ordenar los pares de mayor a menor eigenvalor
eigen_pairs.sort(key=lambda x: x[0], reverse=True)

print("\nEigenvalores en orden descendente:")
for i in eigen_pairs:
    print(i[0])

# Calcular la varianza explicada
total_variance = sum(eigenvalues)
explained_variance = [(i[0] / total_variance) * 100 for i in eigen_pairs]
explained_variance_ratio = [i[0] / total_variance for i in eigen_pairs]

cum_explained_variance = np.cumsum(explained_variance_ratio)

print("\nVarianza explicada por cada componente (%):")
for i, var in enumerate(explained_variance):
    print(f"Componente {i+1}: {var:.2f}%")

print("\nVarianza explicada acumulada (%):")
for i, cum_var in enumerate(cum_explained_variance):
    print(f"Hasta componente {i+1}: {cum_var*100:.2f}%")

Visualización de la Varianza Explicada (Scree Plot) 📈

Un scree plot es una herramienta visual excelente para determinar el número óptimo de componentes principales. Buscamos un 'codo' donde la curva de varianza explicada se aplane significativamente.

plt.figure(figsize=(10, 6))
plt.bar(range(1, len(explained_variance) + 1), explained_variance, alpha=0.7, align='center',
        label='Varianza Individual Explicada')
plt.step(range(1, len(cum_explained_variance) + 1), cum_explained_variance * 100, where='mid',
         label='Varianza Acumulada Explicada', color='red', marker='o')
plt.ylabel('Porcentaje de Varianza Explicada')
plt.xlabel('Componente Principal')
plt.title('Scree Plot: Varianza Explicada por Componentes Principales')
plt.legend(loc='best')
plt.grid(True)
plt.show()

En este gráfico, podemos ver cuánta información (varianza) captura cada componente. Generalmente, elegimos el número de componentes que explican un umbral deseado de varianza (por ejemplo, 95%) o donde la curva se aplana.

Paso 5: Selección de Componentes y Proyección 🎯

Basándonos en el scree plot o un umbral de varianza, seleccionamos el número de componentes principales. Para nuestros datos de ejemplo, parece que los dos primeros componentes capturan una gran parte de la varianza total.

# Elegir el número de componentes (por ejemplo, los 2 primeros)
num_components = 2

# Crear la matriz de proyección (matriz de pesos W)
# Consiste en los k eigenvectores con los eigenvalores más grandes
projection_matrix = np.hstack([eigen_pairs[i][1].reshape(len(data.columns), 1) for i in range(num_components)])

print("\nMatriz de Proyección (Eigenvectores seleccionados):")
print(projection_matrix)

# Proyectar los datos escalados sobre la nueva base de componentes principales
pca_data = data_scaled_df.dot(projection_matrix)

# Nombrar las columnas del DataFrame resultante
pca_df = pd.DataFrame(data=pca_data, columns=[f'PC{i+1}' for i in range(num_components)])

print("\nDatos transformados por PCA (Primeras 5 filas):")
print(pca_df.head())

¡Y ahí lo tienes! Hemos transformado nuestros datos originales de 5 dimensiones a 2 dimensiones, conservando la mayor parte de la varianza.


Visualización de los Componentes Principales 🖼️

Una de las ventajas clave de reducir la dimensionalidad a 2 o 3 componentes es la capacidad de visualizar los datos fácilmente. Grafiquemos nuestros datos proyectados en un espacio 2D.

plt.figure(figsize=(10, 8))
sns.scatterplot(x='PC1', y='PC2', data=pca_df, s=100, alpha=0.7)
plt.title('Datos Reducidos en 2 Componentes Principales')
plt.xlabel(f'Componente Principal 1 ({explained_variance[0]:.2f}% varianza explicada)')
plt.ylabel(f'Componente Principal 2 ({explained_variance[1]:.2f}% varianza explicada)')
plt.grid(True)
plt.show()

Si bien nuestro dataset de ejemplo no tenía clases, si las tuviera, podrías colorear los puntos según sus clases para ver si PCA ayuda a separarlas. En este gráfico, cada punto representa una observación en su nueva representación de baja dimensión.

Interpretar los Componentes Principales (Loading Scores) 🤔

Los componentes principales son combinaciones lineales de las características originales. Para entender qué significa cada PC, podemos examinar los loading scores, que son los elementos de los eigenvectores. Nos indican la contribución de cada característica original a cada componente principal.

# Cargar los componentes principales (eigenvectores seleccionados)
# Cada columna es un PC, cada fila es una característica original
loadings = pd.DataFrame(projection_matrix, index=data.columns, columns=[f'PC{i+1}' for i in range(num_components)])

print("\nLoading Scores (Contribución de características a cada PC):")
print(loadings)

plt.figure(figsize=(10, 7))
sns.heatmap(loadings, annot=True, cmap='viridis', fmt=".3f")
plt.title('Loading Scores de Componentes Principales')
plt.xlabel('Componentes Principales')
plt.ylabel('Características Originales')
plt.show()

Del mapa de calor de loading scores, podemos inferir lo siguiente:

  • PC1: Si miras la primera columna (PC1), verás que Longitud, Peso y Volumen tienen valores positivos altos. Esto sugiere que el primer componente principal es una medida general del 'tamaño' o 'escala' del producto.
  • PC2: La segunda columna (PC2) muestra que Ancho tiene un valor positivo alto, mientras que Altura tiene un valor negativo alto. Longitud, Peso y Volumen tienen contribuciones menores. Esto indica que PC2 podría estar diferenciando productos en función de su 'proporción' o 'forma' relativa, contrastando el ancho con la altura.
🔥 Importante: La interpretación de los componentes principales es subjetiva y depende en gran medida del contexto del dominio de los datos.

Comparación con scikit-learn PCA ✅

Aunque es educativo construir PCA desde cero, en la práctica, generalmente usamos implementaciones optimizadas como la de scikit-learn. Aquí te mostramos cómo usarla para validar nuestros resultados.

from sklearn.decomposition import PCA

# Creamos una nueva instancia del escalador para asegurar la limpieza
scaler_sklearn = StandardScaler()
data_scaled_sklearn = scaler_sklearn.fit_transform(data)

# Inicializar PCA con el número deseado de componentes
pca_sklearn = PCA(n_components=num_components)

# Ajustar PCA a los datos escalados y transformarlos
pca_data_sklearn = pca_sklearn.fit_transform(data_scaled_sklearn)

# Convertir a DataFrame
pca_df_sklearn = pd.DataFrame(data=pca_data_sklearn, columns=[f'PC{i+1}' for i in range(num_components)])

print("\nDatos transformados por PCA con scikit-learn (Primeras 5 filas):")
print(pca_df_sklearn.head())

print("\nVarianza explicada por componente (scikit-learn):")
print(pca_sklearn.explained_variance_ratio_ * 100)
print("\nVarianza explicada acumulada (scikit-learn):")
print(np.cumsum(pca_sklearn.explained_variance_ratio_) * 100)

# Los eigenvectores de scikit-learn se acceden a través de components_
# Nota: Los signos pueden invertirse, pero la dirección es la misma.
print("\nLoading Scores (scikit-learn - components_):")
loadings_sklearn = pd.DataFrame(pca_sklearn.components_.T, index=data.columns, columns=[f'PC{i+1}' for i in range(num_components)])
print(loadings_sklearn)

Observarás que los resultados de scikit-learn son muy similares a nuestra implementación manual. Las únicas diferencias podrían ser el signo de los componentes principales (ya que los eigenvectores pueden apuntar en direcciones opuestas pero seguir representando la misma dirección de varianza), pero la varianza explicada y la estructura general son idénticas. Esto confirma la validez de nuestra implementación.


Casos de Uso Comunes de PCA 🌐

PCA es una herramienta versátil con aplicaciones en diversas áreas:

  • Reducción de ruido: Al desechar componentes con baja varianza, a menudo se elimina el ruido de los datos.
  • Visualización: Como hemos visto, permite visualizar datos de alta dimensión en 2D o 3D.
  • Preprocesamiento para Machine Learning: Puede mejorar el rendimiento de modelos al eliminar la multicolinealidad y reducir el número de características.
  • Compresión de imágenes: Reduce la cantidad de datos necesarios para representar una imagen.
  • Finanzas: Análisis de carteras y riesgos, identificando factores subyacentes.
  • Bioinformática: Análisis de expresiones génicas o datos genómicos.
💡 Consejo: Considera aplicar PCA cuando tengas un gran número de características numéricas, sospeches de correlaciones entre ellas y busques simplificar tu conjunto de datos sin perder información crítica.

Limitaciones de PCA ⚠️

Aunque PCA es potente, tiene algunas limitaciones:

  • Linealidad: PCA es un método lineal. No puede capturar relaciones no lineales en los datos. Para esto, se necesitan técnicas como Kernel PCA o t-SNE.
  • Interpretación: La interpretación de los componentes principales puede ser un desafío, especialmente cuando se trata de combinaciones complejas de muchas características originales.
  • Escalado: Es sensible al escalado de los datos. Como vimos, la estandarización es crucial.
  • Pérdida de información: Al reducir la dimensionalidad, siempre hay una pérdida de información, aunque PCA intenta minimizarla al conservar la máxima varianza.
  • Outliers: PCA es sensible a los outliers, que pueden influir desproporcionadamente en las direcciones de los componentes principales.
¿Cuándo NO usar PCA? Si la interpretabilidad de las características originales es más importante que la reducción de dimensionalidad o si tus datos tienen estructuras no lineales complejas que la PCA lineal no puede capturar, es posible que PCA no sea la mejor opción. Además, si las características ya están en la misma escala y tienen una baja correlación, los beneficios de PCA podrían ser mínimos.

Conclusión ✨

El Análisis de Componentes Principales (PCA) es una herramienta indispensable en el arsenal de cualquier científico o analista de datos. Nos permite abordar el desafío de la alta dimensionalidad, transformando datos complejos en representaciones más simples y manejables, lo que facilita la visualización, mejora la eficiencia computacional y puede optimizar el rendimiento de los modelos de Machine Learning.

Al comprender los principios matemáticos subyacentes e implementarlo paso a paso con NumPy y Pandas, no solo adquieres una comprensión más profunda de la técnica, sino que también desarrollas una base sólida para explorar métodos de reducción de dimensionalidad más avanzados. Recuerda siempre escalar tus datos y evaluar la varianza explicada para tomar decisiones informadas sobre el número de componentes a retener.

Esperamos que este tutorial te haya proporcionado una guía clara y práctica para dominar PCA. ¡Ahora estás listo para aplicar esta poderosa técnica a tus propios conjuntos de datos!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!