Sesión 5: Análisis Discriminante y Pipelines - Explicación Exhaustiva
Linear Discriminant Analysis (LDA)
Linear Discriminant Analysis (LDA) es un algoritmo de machine learning supervisado que combina dos funciones principales: clasificación y reducción de dimensionalidad. A diferencia de PCA, LDA utiliza las etiquetas de clase para encontrar las direcciones que mejor separan las diferentes clases en el espacio de características.
Fundamentos Matemáticos del LDA
LDA busca maximizar la separación entre clases minimizando la varianza dentro de cada clase. Matemáticamente, esto se logra encontrando los eigenvectores de la matriz S_W^(-1) × S_B, donde:
- S_W: Matriz de dispersión intra-clase (within-class scatter matrix)
- S_B: Matriz de dispersión inter-clase (between-class scatter matrix)
La fórmula matemática para LDA implica:
S_W = Σ(i=1 to c) Σ(x∈Ci) (x - μi)(x - μi)^T
S_B = Σ(i=1 to c) ni(μi - μ)(μi - μ)^T
Donde c es el número de clases, μi es la media de la clase i, y μ es la media global.
Implementación Práctica de LDA
# Ejemplo 1: Linear Discriminant Analysis (LDA) básico
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, accuracy_score
# Cargar dataset
iris = load_iris()
X = iris.data
y = iris.target
# Dividir datos
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# Escalar datos
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Aplicar LDA
lda = LinearDiscriminantAnalysis(n_components=2) # Reducir a 2 componentes
X_train_lda = lda.fit_transform(X_train_scaled, y_train)
X_test_lda = lda.transform(X_test_scaled)
print("Dimensiones originales:", X_train.shape)
print("Dimensiones después de LDA:", X_train_lda.shape)
print("Varianza explicada por componente:", lda.explained_variance_ratio_)
# Usar LDA para clasificación
lda_classifier = LinearDiscriminantAnalysis()
lda_classifier.fit(X_train_scaled, y_train)
y_pred = lda_classifier.predict(X_test_scaled)
print("\nAccuracy:", accuracy_score(y_test, y_pred))
print("\nReporte de clasificación:")
print(classification_report(y_test, y_pred, target_names=iris.target_names))
Limitaciones del LDA
Una limitación importante del LDA es que puede extraer como máximo c-1 componentes discriminantes, donde c es el número de clases. Esto significa que:
- Para 2 clases: máximo 1 componente discriminante
- Para 3 clases: máximo 2 componentes discriminantes
- Para k clases: máximo k-1 componentes discriminantes
Diferencias Fundamentales: PCA vs LDA
La distinción entre PCA y LDA es crucial para seleccionar la técnica apropiada:
PCA (Principal Component Analysis)
- Método no supervisado: No utiliza etiquetas de clase
- Objetivo: Maximizar la varianza total de los datos
- Uso: Exploración de datos, visualización, compresión
- Componentes: Basados en la matriz de covarianza de X
LDA (Linear Discriminant Analysis)
- Método supervisado: Requiere etiquetas de clase
- Objetivo: Maximizar la separación entre clases
- Uso: Clasificación, reducción dimensional supervisada
- Componentes: Basados en la separabilidad de clases
Ejemplo Comparativo PCA vs LDA
# Ejemplo 2: Comparación PCA vs LDA
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.model_selection import train_test_split
# Cargar dataset de vinos (3 clases)
wine = load_wine()
X = wine.data
y = wine.target
# Escalar datos
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Aplicar PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# Aplicar LDA
lda = LinearDiscriminantAnalysis(n_components=2)
X_lda = lda.fit_transform(X_scaled, y)
print("Dataset de vinos - Comparación PCA vs LDA")
print("=" * 50)
print(f"Dimensiones originales: {X.shape}")
print(f"Dimensiones después de PCA: {X_pca.shape}")
print(f"Dimensiones después de LDA: {X_lda.shape}")
print(f"\nPCA - Varianza explicada: {pca.explained_variance_ratio_}")
print(f"PCA - Varianza total explicada: {pca.explained_variance_ratio_.sum():.3f}")
print(f"\nLDA - Varianza explicada: {lda.explained_variance_ratio_}")
print(f"LDA - Varianza total explicada: {lda.explained_variance_ratio_.sum():.3f}")
Pipelines en Scikit-Learn
Los Pipelines son una herramienta fundamental para automatizar el flujo de trabajo de machine learning. Permiten encadenar múltiples pasos de preprocesamiento y modelado en una secuencia ordenada.
Ventajas de los Pipelines
- Prevención de Data Leakage: Garantiza que las transformaciones se ajusten solo en datos de entrenamiento
- Código Limpio: Organiza el flujo de trabajo en pasos claros
- Reutilización: Fácil aplicación a nuevos datos
- Validación Cruzada: Integración seamless con técnicas de evaluación
Implementación de Pipeline Básico
# Ejemplo 3: Pipeline básico con scikit-learn
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.datasets import load_digits
from sklearn.metrics import classification_report
# Cargar dataset (dígitos manuscritos)
digits = load_digits()
X = digits.data # 64 características (8x8 píxeles)
y = digits.target
# Dividir datos
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Crear pipeline - Automatización de pasos
pipeline = Pipeline([
('scaler', StandardScaler()), # Paso 1: Escalar datos
('pca', PCA(n_components=20)), # Paso 2: Reducción dimensional
('classifier', RandomForestClassifier(n_estimators=100, random_state=42)) # Paso 3: Clasificador
])
print("PIPELINE - Automatización del flujo de trabajo")
print("=" * 50)
print("Pasos del pipeline:")
for i, (name, transformer) in enumerate(pipeline.steps, 1):
print(f"{i}. {name}: {type(transformer).__name__}")
# Entrenar pipeline (todos los pasos se ejecutan automáticamente)
pipeline.fit(X_train, y_train)
# Hacer predicciones (preprocessing automático)
y_pred = pipeline.predict(X_test)
# Evaluación
accuracy = pipeline.score(X_test, y_test)
print(f"\nAccuracy: {accuracy:.3f}")
# Validación cruzada con pipeline
cv_scores = cross_val_score(pipeline, X, y, cv=5, scoring='accuracy')
print(f"CV Accuracy: {cv_scores.mean():.3f} (+/- {cv_scores.std()*2:.3f})")
ColumnTransformers: Manejo de Datos Mixtos
El ColumnTransformer permite aplicar diferentes transformaciones a diferentes subsets de columnas simultáneamente. Es especialmente útil cuando se trabaja con datos que contienen variables numéricas y categóricas.
Funcionalidad del ColumnTransformer
- Transformaciones Paralelas: Aplica diferentes preprocessors a diferentes columnas
- Integración con Pipelines: Se combina perfectamente con Pipeline
- Flexibilidad: Permite especificar transformaciones por tipo de dato
Ejemplo de ColumnTransformer
# Ejemplo 4: ColumnTransformer para diferentes tipos de variables
import pandas as pd
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
# Crear dataset sintético con variables mixtas
np.random.seed(42)
n_samples = 1000
data = {
'age': np.random.randint(18, 80, n_samples),
'income': np.random.normal(50000, 20000, n_samples),
'education': np.random.choice(['High School', 'Bachelor', 'Master', 'PhD'], n_samples),
'city': np.random.choice(['Madrid', 'Barcelona', 'Valencia', 'Sevilla'], n_samples),
'experience': np.random.randint(0, 40, n_samples),
}
# Variable objetivo (basada en algunas características)
data['approved'] = (
(data['age'] > 25) &
(data['income'] > 40000) &
(data['experience'] > 2)
).astype(int)
df = pd.DataFrame(data)
# Separar características y objetivo
X = df.drop('approved', axis=1)
y = df['approved']
# Identificar tipos de columnas
numerical_columns = ['age', 'income', 'experience']
categorical_columns = ['education', 'city']
# Crear ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), numerical_columns),
('cat', OneHotEncoder(drop='first', sparse_output=False), categorical_columns)
],
remainder='passthrough' # Mantener otras columnas sin cambios
)
# Pipeline completo
pipeline = Pipeline([
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(n_estimators=100, random_state=42))
])
# Entrenar y evaluar
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
pipeline.fit(X_train, y_train)
accuracy = pipeline.score(X_test, y_test)
Escaladores: Normalización de Características
StandardScaler
El StandardScaler implementa la normalización Z-score:
Fórmula: z = (x - μ) / σ
- Resultado: Media = 0, Desviación estándar = 1
- Uso: Datos con distribución aproximadamente normal
- Limitación: Sensible a outliers
MinMaxScaler
El MinMaxScaler escala las características a un rango específico:
Fórmula: X_scaled = (X - X_min) / (X_max - X_min)
- Resultado: Rango por defecto
- Uso: Cuando se requiere un rango específico
- Limitación: Muy sensible a outliers
RobustScaler
El RobustScaler usa estadísticas robustas:
Fórmula: X_scaled = (X - median) / IQR
- Resultado: No garantiza rango específico
- Uso: Datos con muchos outliers
- Ventaja: Resistente a outliers
Ejemplo Comparativo de Escaladores
# Ejemplo 5: Comparación de escaladores StandardScaler vs MinMaxScaler
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
# Crear datos con diferentes escalas y outliers
np.random.seed(42)
n_samples = 100
# Datos con diferentes escalas y algunos outliers
feature1 = np.random.normal(50, 15, n_samples)
feature1 = 150 # outlier
feature2 = np.random.normal(5000, 1000, n_samples)
feature2 = 15000 # outlier
feature3 = np.random.normal(0.5, 0.1, n_samples)
X = np.column_stack([feature1, feature2, feature3])
feature_names = ['Edad', 'Salario', 'Score']
# Aplicar diferentes escaladores
scalers = {
'StandardScaler': StandardScaler(),
'MinMaxScaler': MinMaxScaler(),
'RobustScaler': RobustScaler()
}
for name, scaler in scalers.items():
X_scaled = scaler.fit_transform(X)
df_scaled = pd.DataFrame(X_scaled, columns=feature_names)
print(f"{name} - Estadísticas:")
print(df_scaled.describe().round(3))
Pipeline Completo: Integrando Todos los Conceptos
Ejemplo Avanzado con LDA
# Ejemplo 6: Pipeline completo con LDA, ColumnTransformer y escaladores
import pandas as pd
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
# Cargar dataset
cancer = load_breast_cancer()
X = cancer.data
y = cancer.target
# Pipeline completo con múltiples pasos
full_pipeline = Pipeline([
# Paso 1: Preprocesamiento diferenciado por tipo de variable
('preprocessor', ColumnTransformer(
transformers=[
('num', StandardScaler(), numerical_features),
('cat', OneHotEncoder(drop='first', sparse_output=False), categorical_features)
]
)),
# Paso 2: Reducción dimensional con LDA
('lda', LinearDiscriminantAnalysis(n_components=1)),
# Paso 3: Clasificador final
('classifier', RandomForestClassifier(n_estimators=100, random_state=42))
])
# Entrenar y evaluar
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
full_pipeline.fit(X_train, y_train)
test_score = full_pipeline.score(X_test, y_test)
Cuándo Usar Cada Técnica
Selección de Técnica de Reducción Dimensional
| Criterio | PCA | LDA |
|---|---|---|
| Supervisión | No supervisado | Supervisado |
| Objetivo | Maximizar varianza | Maximizar separación de clases |
| Uso principal | Exploración, visualización | Clasificación |
| Limitaciones | Ignora clases | Máximo c-1 componentes |
| Mejor cuando | Datos sin etiquetas | Clasificación supervisada |
Selección de Escalador
| Escalador | Mejor uso | Evitar cuando |
|---|---|---|
| StandardScaler | Distribución normal | Muchos outliers |
| MinMaxScaler | Rango específico necesario | Outliers presentes |
| RobustScaler | Datos con outliers | Se necesita rango específico |
Mejores Prácticas
Para LDA
- Preprocesamiento: Siempre escalar las características antes de aplicar LDA
- Validación: Usar validación cruzada para evaluar el rendimiento
- Componentes: Recordar la limitación de c-1 componentes máximo
Para Pipelines
- Orden: Mantener el orden lógico: preprocesamiento → reducción dimensional → modelado
- Naming: Usar nombres descriptivos para cada paso
- Validación: Aplicar validación cruzada al pipeline completo
Para ColumnTransformers
- Identificación: Identificar correctamente tipos de variables
- Transformaciones: Elegir transformaciones apropiadas para cada tipo
- Remainder: Decidir qué hacer con columnas no especificadas
La combinación de LDA, pipelines, ColumnTransformers y escaladores apropiados proporciona una base sólida para desarrollar flujos de trabajo de machine learning robustos y eficientes, especialmente en tareas de clasificación supervisada donde la reducción dimensional es beneficiosa.
Hi :)
Matemáticas
Vectores
Álgebra Lineal
Geometría Analítica
Producto Punto
Espacios Vectoriales
Ortogonalidad
Normalización
Funciones
Álgebra
Composición de Funciones
Función Inversa
Combinación de Funciones
Transformaciones Gráficas
Aplicaciones Económicas
Interés Compuesto
Proporcionalidad
R
Data
Machine Learning
Aprendizaje Supervisado
Inteligencia Artificial
Clasificación
Regresión
Deep Learning