K-Means y aprendizaje no supervisado
Agrupar datos sin etiquetas: centroides, distancia y el método del codo
Sin etiquetas no hay respuestas que copiar, pero sí estructura que descubrir. Aquí ves cómo K-Means alterna asignaciones y centroides, cómo elegir el número de grupos con la inercia y por qué escalar las variables cambia por completo el resultado.
- Explicar el ciclo asignar-recalcular de K-Means y cuándo termina
- Calcular distancias y asignar puntos a centroides a mano
- Elegir `k` con el método del codo y comprobarlo con la silueta
- Escalar variables antes de agrupar y justificar por qué
- Reconocer las limitaciones de K-Means y cuándo probar otro método
Antes necesitas: Fundamentos de Machine Learning · NumPy: vectores, matrices y vectorización · Estadística y análisis exploratorio
En esta lección
- ¿Qué es el clustering?
- ¿Para qué sirve?
- ¿Cómo funciona por dentro?
- Sintaxis: KMeans en scikit-learn
- Ejemplo básico: la distancia decide
- Visualización: puntos y centroides
- Traza: cómo se asigna un punto
- Ejemplo intermedio: una iteración completa
- Cómo elegir k: el codo
- Escalado: el paso que cambia el resultado
- Limitaciones y otros métodos
- Caso práctico: segmentar clientes
- Errores comunes
- Buenas prácticas
- Ejercicios
- Soluciones explicadas
- Resumen
- Evaluación
Tienes las compras de cientos de clientes y ninguna etiqueta que diga «este es del segmento A». No hay respuestas que copiar, pero sí una pregunta razonable: ¿qué grupos existen por sí solos? Eso es el aprendizaje no supervisado.
K-Means es el método más usado para responderla, y también el más malinterpretado. Al terminar esta lección sabrás qué garantiza —y qué no— y podrás mirar un gráfico de grupos sin creer que los colores son la verdad.
¿Qué es el clustering?
El clustering o agrupamiento consiste en repartir ejemplos en grupos de modo que los de un mismo grupo se parezcan entre sí más que a los de otros. No hay etiqueta correcta: el resultado es una hipótesis de estructura, no un hecho. Dos ejecuciones con distinto k o distinta escala producen agrupaciones distintas, y ninguna es «la verdadera».
K-Means es un algoritmo de clustering particional: eliges de antemano cuántos grupos quieres (k) y él reparte todos los puntos entre esos grupos, sin dejar ninguno fuera. Cada grupo queda representado por un centroide, el punto promedio de sus miembros.
¿Para qué sirve?
- Segmentar clientes: agrupar por frecuencia de compra y gasto para diseñar campañas distintas.
- Compresión de color: reducir una imagen a 16 colores agrupando los píxeles parecidos.
- Detección de patrones: encontrar zonas de una ciudad con comportamiento similar de consumo.
- Preparar datos: crear una columna de segmento que después alimente un modelo supervisado.
¿Cómo funciona por dentro?
El algoritmo alterna dos pasos hasta que nada cambia. Primero asigna cada punto al centroide más cercano, normalmente con distancia euclidiana. Después recalcula cada centroide como la media de los puntos asignados. Si los centroides se mueven, vuelve a asignar; si no, termina.
La inicialización importa: dos puntos de partida distintos pueden terminar en agrupaciones distintas. Por eso se usa k-means++, que elige centroides iniciales separados entre sí, y se repite el proceso varias veces (n_init) quedándose con la solución de menor inercia. La inercia es la suma de distancias al cuadrado de cada punto a su centroide: mide qué tan compactos son los grupos.
Sintaxis: KMeans en scikit-learn
python -m pip install scikit-learnfrom sklearn.cluster import KMeans
puntos = [[1, 1], [2, 1], [1, 2], [6, 5], [7, 6], [6, 6]]
modelo = KMeans(n_clusters=2, n_init=10, random_state=0)
etiquetas = modelo.fit_predict(puntos)
print(etiquetas)
print(modelo.cluster_centers_.round(2))
print(round(modelo.inertia_, 2))fit_predict entrena y devuelve la etiqueta de cada punto en una sola llamada. cluster_centers_ guarda las coordenadas de los centroides y inertia_ la suma de errores al cuadrado. Las etiquetas son números arbitrarios: el grupo 0 no es «mejor» que el 1, y su numeración puede cambiar entre ejecuciones.
Ejemplo básico: la distancia decide
Con dos centroides en (1, 1) y (6, 5), un punto en (2, 1) pertenece al que esté más cerca. La distancia euclidiana se calcula con el teorema de Pitágoras:
from math import dist
centroides = [(1, 1), (6, 5)]
punto = (2, 1)
for i, c in enumerate(centroides):
print(f"centroide {i}: {dist(punto, c):.2f}")La salida es centroide 0: 1.00 y centroide 1: 5.66. La diferencia es enorme, así que la asignación no admite dudas. Cuando dos distancias están muy cerca, el punto cae en una frontera y puede cambiar de grupo con un pequeño movimiento de los centroides.
Visualización: puntos y centroides
Seis puntos y dos centroides iniciales. Recorre los pasos para ver cómo los centroides se desplazan hacia el promedio de su grupo:
Recorre los pasos para ver cómo cambian las celdas.
Los dos grupos están claramente separados, así que cualquier inicialización razonable llega al mismo resultado. En datos reales los grupos se tocan, y ahí la inicialización y la escala sí cambian la respuesta.
Traza: cómo se asigna un punto
La asignación es una comparación de distancias. Observa el cálculo interno para el mismo punto (2, 1):
from math import distpunto = (2, 1)centroides = [(1, 1), (6, 5)]distancias = [dist(punto, c) for c in centroides]elegido = distancias.index(min(distancias))print("distancias:", [round(d, 2) for d in distancias])print("cluster:", elegido)(2, 1).—
Avanza paso a paso: observa la línea resaltada, las variables y la salida.
Ejemplo intermedio: una iteración completa
Este código hace exactamente lo que dibuja la matriz, sin scikit-learn: asigna los seis puntos y recalcula los dos centroides.
from math import dist
puntos = [(1, 1), (2, 1), (1, 2), (6, 5), (7, 6), (6, 6)]
centroides = [(1, 1), (6, 5)]
grupos = [[], []]
for p in puntos:
i = 0 if dist(p, centroides[0]) <= dist(p, centroides[1]) else 1
grupos[i].append(p)
nuevos = []
for g in grupos:
x = sum(p[0] for p in g) / len(g)
y = sum(p[1] for p in g) / len(g)
nuevos.append((round(x, 2), round(y, 2)))
print("tamaños:", [len(g) for g in grupos])
print("centroides:", nuevos)La salida es tamaños: [3, 3] y centroides: [(1.33, 1.33), (6.33, 5.67)]. Con esos centroides nuevos ningún punto cambia de grupo, así que el algoritmo se detiene en la iteración siguiente. n_init controla cuántas veces se repite todo el proceso con inicializaciones distintas.
Cómo elegir k: el codo
K-Means necesita que le digas k. Una forma de buscarlo es entrenar con varios valores y mirar la inercia: siempre baja al aumentar k, porque más centroides siempre ajustan mejor. Lo útil es encontrar el punto donde la caída deja de ser grande. Para estos seis puntos, la mejor inercia de cada k es:
| k | Inercia | Lectura |
|---|---|---|
| 1 | 68.33 | todos los puntos en un solo grupo |
| 2 | 2.67 | caída enorme: aparecen los dos grupos reales |
| 3 | 1.83 | mejora pequeña, un grupo se parte en dos |
| 4 | 1.00 | cada subgrupo se divide otra vez |
El codo está en k = 2: pasar de 68.33 a 2.67 es una mejora de otra magnitud, mientras que las siguientes apenas restan una unidad. La silueta confirma la lectura con otra pregunta: compara la distancia media dentro del grupo con la del grupo vecino. Va de −1 a 1, y con estos puntos da 0.83, un valor alto.
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
puntos = [[1, 1], [2, 1], [1, 2], [6, 5], [7, 6], [6, 6]]
modelo = KMeans(n_clusters=2, init=[[1.0, 1.0], [6.0, 5.0]], n_init=1, random_state=0)
modelo.fit(puntos)
print("etiquetas:", modelo.labels_)
print("centroides:", modelo.cluster_centers_.round(2))
print("inercia:", round(modelo.inertia_, 2))
print("silueta:", round(silhouette_score(puntos, modelo.labels_), 2))La salida es etiquetas: [0 0 0 1 1 1], los centroides [[1.33 1.33] [6.33 5.67]], inercia: 2.67 y silueta: 0.83. Pasar init como lista fija los centroides de partida y hace el experimento reproducible; en un proyecto real deja init="k-means++" y usa n_init=10.
Escalado: el paso que cambia el resultado
La distancia euclidiana suma diferencias al cuadrado de todas las columnas, así que una variable con números grandes domina. Si comparas ingresos (0 a 100 000) con edad (18 a 80), la distancia la decide el ingreso: la edad es invisible. Escalar con StandardScaler pone cada columna en unidades de desviación típica y devuelve el voto a ambas.
Encadena el escalado y el modelo en un Pipeline para que el tratamiento sea idéntico en entrenamiento y en producción. Y hazlo antes de comparar inercias: los valores de la tabla solo son comparables entre sí si se calcularon con la misma escala.
Limitaciones y otros métodos
K-Means supone grupos aproximadamente esféricos, de tamaño parecido y separables por distancia al centro. Falla con formas alargadas, con densidades muy distintas y con ruido: un valor atípico arrastra su centroide. Además, siempre asigna todos los puntos, aunque algunos no pertenezcan a ningún grupo.
| Método | Qué aporta | Cuándo probarlo |
|---|---|---|
KMeans | grupos compactos y rápidos | datos numéricos con grupos redondos |
DBSCAN | grupos por densidad y puntos de ruido | formas irregulares y outliers |
AgglomerativeClustering | jerarquía y dendrograma | no sabes k y quieres explorar |
GaussianMixture | pertenencia probabilística | grupos que se solapan |
PCA | reduce dimensiones antes de agrupar | muchas columnas correlacionadas |
Caso práctico: segmentar clientes
Imagina dos variables por cliente: visitas al mes y gasto mensual en cientos. Con los seis puntos de la lección, los grupos son «compra ocasional» y «compra frecuente». Un cliente nuevo con 5 visitas y 3 de gasto cae del lado del segundo grupo:
from math import dist
centroides = [(1.33, 1.33), (6.33, 5.67)]
nuevo = (5, 3)
for i, c in enumerate(centroides):
print(i, round(dist(nuevo, c), 2))La salida es 0 4.03 y 1 2.98: el cliente se asigna al grupo 1. La decisión de negocio empieza justo aquí: ¿qué campaña recibe cada grupo, y qué pasa con quien queda cerca de la frontera? El algoritmo agrupa; la acción la defines tú.
Errores comunes
- No escalar: una columna con números grandes decide todos los grupos.
- Elegir
kmirando solo la inercia: siempre baja al aumentark. - Interpretar las etiquetas como categorías reales: el grupo 0 no existe fuera de tu modelo.
- Ignorar la inicialización: con
n_init=1puedes quedarte en un mínimo local malo. - Agrupar variables correlacionadas: dos columnas que miden lo mismo cuentan doble en la distancia.
Buenas prácticas
- Escala o normaliza antes de calcular distancias, y documenta qué transformación usaste.
- Prueba varios
ky guarda la curva de inercia y la silueta de cada uno. - Repite con varias semillas para comprobar que la solución es estable.
- Describe cada grupo con la media de sus variables originales, no con los centroides escalados.
- Valida el resultado con alguien del negocio: un grupo sin interpretación no se puede usar.
Ejercicios
Asigna cuatro puntos
(1.5, 1.5) y (8.5, 8.5), asigna los puntos (1, 1), (2, 2), (8, 8) y (9, 9) calculando distancias. Imprime la etiqueta y la distancia al centroide elegido con dos decimales.Pista
Reutiliza dist de math y guarda en una lista la distancia menor. El índice de esa distancia es la etiqueta.
Calcula la inercia
(4/3, 4/3) y (19/3, 17/3), calcula la inercia sin usar scikit-learn: suma la distancia al cuadrado de cada punto a su centroide. Imprime el resultado redondeado a dos decimales.Pista
Asigna cada punto según su coordenada: los tres primeros van al centroide 0. Recuerda que la inercia no lleva raíz.
from sklearn.cluster import KMeans modelo = KMeans(n_clusters=, n_init=10, random_state=0) modelo.fit(puntos) print("etiquetas:", ) print("inercia:", round(modelo.inertia_, 2))
El número de grupos se pasa en `n_clusters`; las etiquetas quedan guardadas en un atributo con guion bajo final.
Soluciones explicadas
En el primer ejercicio, la simetría hace que todas las distancias mínimas coincidan en 0.71 (√0.5). Eso no significa que los grupos sean iguales: significa que los puntos están a la misma distancia de su centro. Si añadieras un punto en (5, 5), quedaría exactamente en la frontera y su asignación dependería del desempate.
En el segundo, la inercia es la suma de cuadrados, no la raíz. Por eso el valor es pequeño: cada punto está a poco más de medio unitario de su centroide. Con datos reales, la inercia carece de unidades interpretables; sirve para comparar soluciones sobre el mismo conjunto y la misma escala.
Resumen
- El clustering busca estructura sin etiquetas; el resultado es una hipótesis, no una verdad.
- K-Means alterna asignar puntos al centroide más cercano y recalcular la media del grupo.
- La inercia mide compacidad y siempre baja al aumentar
k. - El codo señala el
kdonde la mejora deja de compensar; la silueta lo confirma. - Sin escalar, la variable con números más grandes domina la distancia.
- K-Means supone grupos esféricos y sensibles a los outliers; DBSCAN y los métodos jerárquicos cubren otros casos.
Evaluación
¿Qué hace K-Means en el paso de actualización?
La inercia baja de 68.33 a 2.67 al pasar de k = 1 a k = 2, y a 1.83 con k = 3. ¿Qué sugiere el codo?
Agrupas ingresos (0 a 100 000) y edad (18 a 80) sin escalar. ¿Qué ocurre?
¿Ya lo entendiste?
Márcala como completada para llevar tu progreso y desbloquear el siguiente paso de la ruta.