SVD y PCA: cómo el álgebra lineal comprime miles de dimensiones A developer explains how SVD and PCA, fundamental linear algebra techniques, can compress a matrix with a million columns down to ten while preserving most information. The techniques underpin recommendation systems, image compression, and machine learning, with mathematical guarantees from the Eckart-Young theorem. Reducí una matriz de un millón de columnas a diez y perdés menos información de la que imaginás: así funcionan SVD y PCA , el dúo de álgebra lineal que sostiene los sistemas de recomendación, la compresión de imágenes y buena parte del machine learning moderno. Un libro publicado el 11 de julio de 2026 en arXiv https://arxiv.org/abs/2607.11938 por Afonso Bandeira, Amit Singer y Thomas Strohmer, titulado "Mathematics of Data Science", dedica un capítulo entero a esta técnica junto a otros 15 sobre los fundamentos matemáticos de la ciencia de datos: desde geometría en dimensiones altas hasta redes neuronales profundas. Este artículo toma ese capítulo como punto de partida y lo explica desde cero, con código en Python. La descomposición en valores singulares SVD, por sus siglas en inglés es una factorización que existe para cualquier matriz, sin importar su forma ni si es cuadrada. Toma una matriz A de tamaño m x n y la reescribe como el producto de tres matrices: U, Sigma y V transpuesta. No hace falta que A represente nada especial: puede ser una tabla de clientes, los píxeles de una imagen o las calificaciones de usuarios sobre películas. El Análisis de Componentes Principales PCA es, en el fondo, una aplicación directa de SVD: se usa para encontrar las direcciones donde los datos varían más y proyectar la información sobre esas direcciones, descartando el ruido. Cuando un dataset tiene 500 columnas pero solo 8 explican el 95% del comportamiento, PCA es la herramienta que lo revela. Por qué importa: casi todo el machine learning trabaja con matrices de datos de alta dimensión imágenes, texto vectorizado, señales de sensores, genes . SVD y PCA son la base matemática que permite comprimir esa información sin destruirla, acelerar entrenamientos, eliminar ruido y visualizar datos que de otra forma serían imposibles de graficar en dos o tres ejes. El teorema de Eckart-Young, uno de los resultados que el libro de Bandeira, Singer y Strohmer desarrolla en detalle, demuestra algo muy fuerte: quedarte con los primeros k valores singulares te da la mejor aproximación posible de rango k a tu matriz original, medida en norma de Frobenius. No es una heurística, es un óptimo matemático. Toda matriz A de m filas y n columnas se puede escribir como A = U · Sigma · VT, donde U es una matriz ortogonal de m x m sus columnas son los vectores singulares izquierdos , V es otra matriz ortogonal de n x n columnas: vectores singulares derechos y Sigma es una matriz diagonal con los valores singulares , siempre positivos y ordenados de mayor a menor. Los valores singulares miden cuánta "energía" o varianza aporta cada dirección. El primer valor singular corresponde a la dirección donde los datos varían más; el segundo, a la siguiente dirección ortogonal con más varianza, y así sucesivamente. Si los primeros k valores singulares concentran casi toda la energía, podés descartar el resto sin perder información relevante: esa es la base matemática de la compresión con SVD. Cada valor singular pesa cuanta varianza aporta su direccion. php flowchart TD A "Matriz A m filas x n columnas " -- U "U: vectores singulares izquierdos" A -- S "Sigma: valores singulares ordenados" A -- V "V transpuesta: vectores singulares derechos" U -- R "A = U x Sigma x V-transpuesta" S -- R V -- R PCA reutiliza exactamente esta descomposición, pero antes centra los datos resta la media de cada columna para que el primer valor singular capture varianza real y no el desplazamiento del origen. Los vectores singulares derechos filas de V transpuesta se convierten en las componentes principales : nuevas direcciones, combinaciones lineales de las columnas originales, ordenadas por cuánta varianza explican. Una propiedad clave: U y V son ortogonales, lo que significa que rotan los datos sin deformarlos, solo cambian el sistema de referencia para alinear los ejes con la varianza real. Empecemos por el caso más simple posible: una matriz de 2x2, para ver qué devuelve numpy.linalg.svd sin ningún ruido de por medio. python import numpy as np A = np.array 4, 0 , 3, -5 U, S, Vt = np.linalg.svd A print "U:\n", U print "Valores singulares:", S print "Vt:\n", Vt Este bloque descompone una matriz de 2x2 en sus tres piezas. S devuelve un vector con los valores singulares no la matriz diagonal completa, NumPy la simplifica , ordenados de mayor a menor. Podés reconstruir la matriz original con U @ np.diag S @ Vt y confirmar que da el mismo resultado que A : esa reconstrucción exacta es la que después se trunca a los primeros k términos para comprimir. Ahora un caso más realista: un dataset simulado de 200 clientes con 50 variables numéricas comportamiento de compra, por ejemplo , reducido a 2 dimensiones con PCA implementado manualmente sobre SVD: python import numpy as np def pca manual X, k : X centrado = X - X.mean axis=0 U, S, Vt = np.linalg.svd X centrado, full matrices=False componentes = Vt :k X reducido = X centrado @ componentes.T varianza explicada = S :k 2 / np.sum S 2 return X reducido, varianza explicada rng = np.random.default rng 42 datos clientes = rng.normal size= 200, 50 datos reducidos, varianza = pca manual datos clientes, k=2 print "Forma original:", datos clientes.shape print "Forma reducida:", datos reducidos.shape print "Varianza explicada por componente:", varianza La función centra los datos, calcula la SVD y proyecta sobre las k primeras componentes. El resultado, datos reducidos , pasa de 50 columnas a 2, algo que ya podés graficar en un scatter plot. varianza explicada te dice qué porcentaje de la información original conservó cada componente: si sumás los dos valores y da algo bajo, como suele pasar con datos puramente aleatorios como en este ejemplo simulado, es esperable, porque no metimos estructura real en los datos, cualquier dirección explica más o menos lo mismo. 💡 Tip:si tu matriz es dispersa sparse , como una matriz término-documento de texto, usá scipy.sparse.linalg.svds o sklearn.decomposition.TruncatedSVD en vez de numpy.linalg.svd : esta última densifica la matriz en memoria y puede tirarte el proceso por RAM. Un tercer ejemplo, más cercano a un caso de uso real: comprimir una matriz por ejemplo, los píxeles de una imagen en escala de grises quedándote solo con los primeros k valores singulares y midiendo el error de esa aproximación. python import numpy as np def comprimir svd matriz, k : U, S, Vt = np.linalg.svd matriz, full matrices=False aproximacion = U :, :k @ np.diag S :k @ Vt :k, : error relativo = np.linalg.norm matriz - aproximacion / np.linalg.norm matriz return aproximacion, error relativo imagen = np.random.default rng 0 .normal size= 300, 200 , error k10 = comprimir svd imagen, k=10 , error k50 = comprimir svd imagen, k=50 print f"Error relativo con k=10: {error k10:.4f}" print f"Error relativo con k=50: {error k50:.4f}" Con imágenes reales fotos, escaneos, radiografías la mayoría de la energía suele concentrarse en los primeros valores singulares, porque hay estructura repetida bordes, texturas, zonas planas . En una matriz de ruido puro como la de este ejemplo, en cambio, el error baja de forma mucho más lenta a medida que aumentás k, precisamente porque no hay estructura que comprimir. Para reproducir estos ejemplos necesitás Python 3.10 o superior y dos paquetes: NumPy y, para la versión productiva, scikit-learn. Windows PowerShell : python -m pip install numpy scikit-learn macOS: python3 -m pip install numpy scikit-learn Linux: pip install numpy scikit-learn Con scikit-learn, el mismo PCA manual de arriba se reduce a estas líneas, con la ventaja de que la clase ya trae validaciones, distintos solvers y opciones adicionales: python from sklearn.decomposition import PCA import numpy as np rng = np.random.default rng 42 datos clientes = rng.normal size= 200, 50 pca = PCA n components=2 datos reducidos = pca.fit transform datos clientes print "Varianza explicada:", pca.explained variance ratio print "Suma acumulada:", pca.explained variance ratio .sum La clave exacta a configurar es n components : podés pasarle un entero cantidad fija de componentes o un float entre 0 y 1, por ejemplo n components=0.95 , en cuyo caso scikit-learn elige automáticamente cuántas componentes necesita para retener ese porcentaje de varianza. Para verificar que el PCA está bien calculado, revisá que pca.explained variance ratio .sum se acerque a 1.0 cuando usás todas las componentes posibles, y compará contra np.linalg.matrix rank datos clientes para confirmar cuántas dimensiones tienen información real rango de la matriz antes de reducir. Con 2 de 50 columnas ya se puede graficar el dataset completo. Los sistemas de recomendación tipo filtrado colaborativo , el que popularizó el Netflix Prize, factorizan la matriz usuario-item con una variante de SVD para predecir qué calificación le daría un usuario a una película que nunca vio. La matriz original tiene millones de celdas vacías usuarios que no vieron casi nada , y SVD encuentra factores latentes que generalizan esos huecos. La compresión de imágenes usa SVD de forma directa: cada imagen en escala de grises es una matriz de píxeles, y quedarte con los primeros k valores singulares reconstruye una versión de menor tamaño con pérdida controlada. Cuanto más estructura tenga la imagen original bordes definidos, zonas repetidas , menos k necesitás para una reconstrucción visualmente aceptable. En bioinformática, PCA reduce datasets de expresión génica con decenas de miles de columnas una por gen a un puñado de componentes que agrupan pacientes por similitud biológica. En finanzas, PCA sobre series de tasas de interés identifica los "factores" nivel, pendiente, curvatura que explican casi todo el movimiento de la curva completa, algo que los modelos de riesgo usan para simular escenarios con muchas menos variables que las tasas individuales. El error más frecuente es aplicar PCA sin estandarizar variables que están en escalas distintas: si una columna mide años 0-100 y otra mide ingresos 0-1.000.000 , la varianza del ingreso domina la primera componente sin que eso refleje ninguna relación real. La solución es escalar cada columna a media 0 y desviación estándar 1 antes de correr PCA, con sklearn.preprocessing.StandardScaler . Otro error es interpretar las componentes principales como si tuvieran significado de negocio directo: una componente es una combinación lineal de las variables originales, no una variable nueva con nombre propio. Hay que inspeccionar los pesos pca.components para entender qué variables originales pesan más en cada componente antes de bautizarla con un nombre. También es común elegir k de forma arbitraria. La práctica recomendada es graficar la varianza explicada acumulada contra el número de componentes el "scree plot" y cortar donde la curva se aplana, o fijar un umbral como 90-95% de varianza retenida en vez de adivinar un número redondo. ⚠️ Ojo:PCA solo captura relacioneslineales. Si tus datos tienen estructura curva o forman clusters no lineales, vas a necesitar t-SNE, UMAP o un autoencoder: PCA los va a aplastar en una proyección que pierde esa estructura. MétodoCuándo usarloVentajaLimitaciónSVD / PCADatos con relaciones lineales, necesitás velocidad e interpretabilidadDeterminista, rápido, matemáticamente óptimoNo captura estructura no linealt-SNEVisualización exploratoria en 2D/3D de clusters no linealesPreserva vecindarios localesLento en datasets grandes, no determinista, no proyecta datos nuevosUMAPVisualización y reducción a mayor escala que t-SNEMás rápido que t-SNE, preserva mejor la estructura globalHiperparámetros sensibles, resultados varían entre corridasAutoencoderDatos con relaciones muy no lineales y volumen suficiente para entrenar una redAprende cualquier función de reducciónNecesita más datos, GPU y ajuste fino que PCA Cuando el número de columnas crece mucho más rápido que el número de filas, por ejemplo 20.000 genes medidos en apenas 200 pacientes, aparece lo que el libro de Bandeira, Singer y Strohmer discute bajo el título "Curses, Blessings, and Surprises in High Dimensions": la intuición geométrica que tenemos en 2 o 3 dimensiones deja de funcionar. Un ejemplo concreto: en dimensiones altas, casi todo el volumen de una esfera se concentra cerca de su superficie, no en el centro. Y las distancias euclidianas entre puntos aleatorios tienden a volverse casi idénticas entre sí, lo que rompe algoritmos que dependen de "vecino más cercano", como k-NN, si no se reduce la dimensionalidad antes de aplicarlos. php flowchart LR D1 "Pocas dimensiones: distancias variadas" -- D2 "Mas dimensiones: distancias se parecen entre si" D2 -- D3 "Muchas dimensiones: casi todo el volumen esta en la superficie" D3 -- D4 "Sin reducir: k-NN y clustering pierden sentido" SVD y PCA no resuelven la maldición de la dimensionalidad por arte de magia: lo que hacen es identificar cuántas dimensiones realmente aportan información, el rango efectivo de la matriz, y descartar el resto, que suele ser ruido. Esto es distinto de reducir dimensiones a ciegas, y es la razón por la que un preprocesamiento con PCA suele mejorar el desempeño de algoritmos basados en distancias. El flujo completo de un pipeline de PCA en producción, desde datos crudos hasta componentes listas para un modelo posterior, se ve así: php flowchart TD A "Datos crudos" -- B "Estandarizar columnas" B -- C "Centrar datos restar la media " C -- D "Calcular SVD de la matriz centrada" D -- E "Elegir k componentes por varianza acumulada" E -- F "Proyectar datos sobre k componentes" F -- G "Datos reducidos listos para el modelo" Otro detalle avanzado: cuando la matriz es demasiado grande para calcular la SVD completa, por ejemplo con millones de filas, se usa randomized SVD , un algoritmo que aproxima los primeros k valores singulares proyectando la matriz sobre un subespacio aleatorio de dimensión reducida antes de descomponerla. sklearn.decomposition.PCA lo activa automáticamente con svd solver="randomized" cuando detecta datasets grandes, y el costo computacional pasa de depender de las dimensiones completas de la matriz a depender solo de k, el número de componentes que pediste. 💭 Clave:el rango efectivo de una matriz real casi nunca es su rango matemático completo. La mayoría de los datasets del mundo real tienen mucha menos "información" de la que sugiere su número de columnas, y por eso SVD funciona tan bien como técnica de compresión. 📖 Resumen en Telegram: Ver resumen Tu próximo paso: tomá un dataset propio en un DataFrame de pandas, corré PCA n components=0.95 .fit df estandarizado y graficá la varianza explicada acumulada para ver cuántas dimensiones reales tiene tu problema. SVD es la operación matemática general que factoriza cualquier matriz en tres piezas. PCA es una aplicación específica de SVD sobre datos centrados, pensada para encontrar direcciones de máxima varianza. En la práctica, PCA se calcula usando SVD por dentro. No hay un número universal. La práctica estándar es fijar un umbral de varianza explicada acumulada, 90-95% es común, y usar pca.explained variance ratio .cumsum para encontrar cuántas componentes lo alcanzan. No directamente. PCA asume variables numéricas continuas. Para datos categóricos existen variantes como MCA Análisis de Correspondencias Múltiples , o hay que codificar las categorías numéricamente antes, con cuidado de no introducir relaciones de orden falsas. Están relacionadas pero no son idénticas. La eigendecomposition solo existe para matrices cuadradas y diagonalizables. SVD existe para cualquier matriz, y los valores singulares de A son la raíz cuadrada de los autovalores de A transpuesta por A. Para datasets que caben en memoria, numpy.linalg.svd o sklearn.decomposition.PCA alcanzan. Para matrices dispersas de texto o grafos, scipy.sparse.linalg.svds o sklearn.decomposition.TruncatedSVD evitan densificar la matriz. Sí, con la variante truncada. Calcular la SVD completa de una matriz dispersa gigante la convierte en densa en memoria, lo que puede consumir gigabytes innecesarios: por eso TruncatedSVD calcula solo los primeros k valores singulares sin densificar la matriz completa. 📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion https://t.me/programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.