El módulo anterior terminó con una observación incómoda: los siete algoritmos supervisados exigían una columna y, y MercaFresh no tiene ninguna columna que diga a qué segmento pertenece cada cliente — los segmentos son precisamente lo que quiere descubrir. Esta lección abre el aprendizaje no supervisado con su algoritmo emblema: K-means. Aprenderás qué es el clustering y en qué se diferencia radicalmente de clasificar, cómo funciona K-means paso a paso (hasta poder ejecutarlo a mano), cómo decidir cuántos grupos buscar con la inercia y el método del codo, cómo medir la calidad con el silhouette score, y cómo aplicarlo con scikit-learn a las features RFM de MercaFresh para obtener segmentos de clientes con lectura de negocio. Es la lección que convierte "queremos segmentar a nuestros clientes" en un procedimiento concreto.
Contenido
- Clustering: agrupar sin etiquetas
- El algoritmo K-means paso a paso
- Ejemplo numérico a mano
- ¿Cuántos clusters? Inercia y método del codo
- Silhouette score: medir la calidad de la partición
- k-means++ y
n_init: el problema de la inicialización - Segmentando clientes MercaFresh con scikit-learn
- Interpretación: perfiles por centroide
- Limitaciones de K-means
Clustering: agrupar sin etiquetas
En 01-03 distinguimos supervisado de no supervisado; ahora la diferencia se vuelve operativa. Compara los dos problemas de MercaFresh:
| Clasificación (04-02) | Clustering (esta lección) | |
|---|---|---|
| Pregunta | ¿Este cliente abandonará? | ¿Qué grupos de clientes existen? |
Etiqueta y |
Sí: churn (0/1), observada en el histórico |
No existe: nadie sabe los grupos de antemano |
| "Respuesta correcta" | Sí: podemos comparar predicción con realidad | No: no hay verdad contra la que corregir |
| El modelo aprende | Una frontera features → etiqueta | Estructura interna de las features |
| Evaluación | Aciertos sobre etiquetas conocidas | Medidas internas de cohesión/separación |
Dos consecuencias prácticas de que no exista "respuesta correcta":
- No hay error que minimizar contra etiquetas. El algoritmo optimiza un criterio interno: que los puntos de un mismo grupo estén cerca entre sí (cohesión) y lejos de los otros grupos (separación).
- La validación final es humana. Un clustering es útil si los grupos que descubre significan algo para el negocio ("clientes VIP", "dormidos", "nuevos"). Dos particiones distintas pueden ser ambas defendibles; en clasificación eso no ocurre.
El clustering responde a preguntas de descubrimiento: segmentos de clientes, familias de productos que se compran juntos, patrones de comportamiento que nadie había nombrado aún.
El algoritmo K-means paso a paso
K-means busca dividir $n$ puntos en $K$ grupos, cada uno representado por su centroide (el punto medio del grupo). La idea completa cabe en un ciclo de dos pasos que se repite hasta que nada cambia:
- Inicialización: elegir $K$ (¡lo fija el analista!) y colocar $K$ centroides iniciales.
- Asignación: cada punto se asigna al centroide más cercano (distancia euclídea, la misma de 04-05).
- Actualización: cada centroide se recalcula como la media de los puntos que tiene asignados (de ahí means).
- Convergencia: si ninguna asignación ha cambiado (o los centroides apenas se mueven), parar; si no, volver al paso 2.
flowchart TD
A["Elegir K y colocar<br/>K centroides iniciales"] --> B["ASIGNACIÓN:<br/>cada punto va al<br/>centroide más cercano"]
B --> C["ACTUALIZACIÓN:<br/>cada centroide = media<br/>de sus puntos asignados"]
C --> D{"¿Alguna asignación<br/>ha cambiado?"}
D -- "Sí" --> B
D -- "No" --> E["Convergencia:<br/>clusters finales"]
Fíjate en el paralelismo con el descenso de gradiente de 04-01: también aquí hay un bucle que mejora una función objetivo en cada vuelta. La función que K-means reduce en cada iteración es la inercia:
$$\text{Inercia} = \sum_{i=1}^{n} \lVert x_i - c_{k(i)} \rVert^2$$
es decir, la suma de distancias al cuadrado de cada punto a su centroide. Cada asignación y cada actualización la reducen o la dejan igual, por lo que el algoritmo siempre converge — aunque, como veremos, puede converger a un óptimo local.
Y la aclaración prometida en 04-05: K-NN y K-means no comparten nada salvo la letra K. K-NN es supervisado (vota entre vecinos etiquetados) y su K son vecinos consultados; K-means es no supervisado y su K son grupos buscados. Solo comparten el uso de la distancia euclídea — y por eso ambos exigen escalar (03-05).
Ejemplo numérico a mano
Ejecutemos K-means sobre 6 clientes de MercaFresh usando una sola feature, recencia_dias, con $K=2$:
Inicialización (deliberadamente mala, para ver que el algoritmo se recupera): centroides $c_1 = 3$ y $c_2 = 8$.
Iteración 1 — asignación: cada punto va al centroide más cercano.
| Punto | Dist. a $c_1=3$ | Dist. a $c_2=8$ | Cluster |
|---|---|---|---|
| 3 | 0 | 5 | 1 |
| 5 | 2 | 3 | 1 |
| 8 | 5 | 0 | 2 |
| 40 | 37 | 32 | 2 |
| 45 | 42 | 37 | 2 |
| 52 | 49 | 44 | 2 |
Iteración 1 — actualización: $c_1 = \text{media}(3,5) = 4$; $c_2 = \text{media}(8,40,45,52) = 36{,}25$.
Iteración 2 — asignación: el punto 8 ahora dista 4 de $c_1$ y 28,25 de $c_2$ → cambia al cluster 1. Los demás no cambian.
Iteración 2 — actualización: $c_1 = \text{media}(3,5,8) = 5{,}33$; $c_2 = \text{media}(40,45,52) = 45{,}67$.
Iteración 3 — asignación: nadie cambia. Convergencia.
Resultado: {3, 5, 8} (clientes recientes) y {40, 45, 52} (clientes fríos). Aunque la inicialización puso ambos centroides dentro del mismo grupo natural, el ciclo asignar-actualizar los separó en dos iteraciones. Con dos features el procedimiento es idéntico, solo que las distancias son euclídeas en 2D — buen momento para repasar el cálculo de 04-05.
¿Cuántos clusters? Inercia y método del codo
K-means no descubre K: lo exige como entrada. ¿Cómo elegirlo? La inercia por sí sola no sirve de árbitro, porque siempre baja al aumentar K (con $K=n$ cada punto es su propio centroide e inercia = 0). Lo que sí es informativo es cómo baja:
- Mientras K sea menor que el número de grupos reales, añadir un cluster parte un grupo genuino y la inercia cae mucho.
- Cuando K supera el número de grupos reales, los nuevos clusters solo trocean grupos ya compactos y la ganancia es marginal.
El método del codo consiste en dibujar la inercia frente a K y buscar el punto donde la curva pasa de caer en picado a caer suavemente — el "codo".
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
inercias = []
rango_k = range(1, 11)
for k in rango_k:
km = KMeans(n_clusters=k, random_state=42)
km.fit(X_esc) # X_esc: features RFM ya escaladas
inercias.append(km.inertia_) # atributo con la inercia final
plt.plot(rango_k, inercias, "o-")
plt.xlabel("Número de clusters (K)")
plt.ylabel("Inercia")
plt.title("Método del codo — clientes MercaFresh")
plt.show()Qué hace el código: entrena un K-means por cada K de 1 a 10 y guarda inertia_, que scikit-learn calcula tras converger. En los datos de clientes de MercaFresh la curva suele doblarse en torno a K=4: de 1 a 4 cada cluster nuevo reduce mucho la inercia; a partir de 4, casi nada. El codo no siempre es nítido — es una guía, no un oráculo; la decisión final combina el codo, el silhouette (siguiente apartado) y la interpretabilidad de negocio.
Silhouette score: medir la calidad de la partición
Sin etiquetas no hay accuracy posible, pero sí podemos medir si la partición es geométricamente buena. El silhouette score evalúa, para cada punto $i$:
- $a(i)$: distancia media de $i$ a los puntos de su propio cluster (cohesión: cuanto menor, mejor).
- $b(i)$: distancia media de $i$ a los puntos del cluster ajeno más cercano (separación: cuanto mayor, mejor).
$$s(i) = \frac{b(i) - a(i)}{\max(a(i), b(i))}$$
La interpretación es directa:
| $s(i)$ | Lectura |
|---|---|
| Cerca de +1 | El punto está mucho más cerca de los suyos que del cluster vecino: bien asignado |
| Cerca de 0 | Está en la frontera entre dos clusters |
| Negativo | Está más cerca de otro cluster que del suyo: probablemente mal asignado |
El score global es la media de todos los $s(i)$. Como regla orientativa: por encima de 0,5 la estructura es clara; entre 0,25 y 0,5, razonable; por debajo, débil o inexistente. En datos reales de clientes (que no forman islas perfectas) valores de 0,3-0,5 son habituales y perfectamente aprovechables.
from sklearn.metrics import silhouette_score
for k in range(2, 8): # silhouette exige K >= 2
etiquetas = KMeans(n_clusters=k, random_state=42).fit_predict(X_esc)
print(f"K={k} | silhouette = {silhouette_score(X_esc, etiquetas):.3f}")Usado junto al codo, el silhouette suele desempatar: si el codo duda entre 3 y 4, gana el K con mejor silhouette (o el que produzca segmentos más accionables para negocio).
k-means++ y n_init: el problema de la inicialización
K-means converge siempre, pero no necesariamente al mejor resultado: centroides iniciales desafortunados (por ejemplo, dos en el mismo grupo natural y ninguno en otro) pueden dejar el algoritmo atrapado en un óptimo local con inercia peor. Dos defensas estándar, ambas activadas por defecto en scikit-learn:
- k-means++ (
init="k-means++"): en lugar de centroides al azar puro, elige el primero al azar y cada siguiente con probabilidad proporcional al cuadrado de la distancia a los ya elegidos — es decir, tiende a repartirlos lejos unos de otros. Inicios repartidos, convergencia más rápida y mejor. n_init: ejecuta el algoritmo completo varias veces con inicializaciones distintas y se queda con la ejecución de menor inercia. En versiones modernas de scikit-learn el valor por defecto esn_init="auto".
Con ambas activadas, K-means es muy estable en la práctica; aun así, fija random_state para que tus resultados sean reproducibles.
Segmentando clientes MercaFresh con scikit-learn
Toca el ejemplo protagonista. Partimos de la tabla rfm construida en 03-06 — recencia_dias, pedidos_por_mes, gasto_medio_pedido, más el ratio_inactividad — y de la lección 03-05 sabemos que un algoritmo de distancias exige StandardScaler: sin él, gasto_medio_pedido (decenas de euros) aplastaría a pedidos_por_mes (unidades) por puro accidente de unidades.
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
features = ["recencia_dias", "pedidos_por_mes",
"gasto_medio_pedido", "ratio_inactividad"]
X = rfm[features]
# 1. Escalar: imprescindible para un algoritmo de distancias (03-05)
scaler = StandardScaler()
X_esc = scaler.fit_transform(X)
# 2. Entrenar con el K elegido por codo + silhouette
km = KMeans(n_clusters=4, init="k-means++", n_init="auto", random_state=42)
rfm["segmento"] = km.fit_predict(X_esc)
print(silhouette_score(X_esc, rfm["segmento"]).round(3)) # p. ej. 0.41
print(rfm["segmento"].value_counts())Detalles del código, línea a línea:
fit_predictentrena y devuelve directamente la etiqueta de cluster (0 a 3) de cada cliente. Ojo: son etiquetas inventadas por el algoritmo, sin significado propio ni orden — el "segmento 2" de hoy puede llamarse "0" si reentrenas con otra semilla. El significado se lo pondremos nosotros al interpretar.- Nota una diferencia clave con el módulo 4: aquí no hay
train_test_split, porque no hay etiquetas que predecir sobre datos nuevos; usamos todos los clientes para descubrir su estructura. (La evaluación de modelos supervisados, con sus particiones, llega en el módulo 6.) - Escalamos con
fit_transformsobre todo el dataset por la misma razón: sin conjunto de test, no hay fuga que evitar.
Interpretación: perfiles por centroide
Un clustering sin interpretación es un número del 0 al 3 pegado a cada cliente: inútil. La herramienta clave son los centroides desescalados: como cada centroide es la media de su grupo, describirlo en unidades originales retrata al cliente típico del segmento.
# Centroides en unidades originales (deshacemos el escalado)
centroides = pd.DataFrame(scaler.inverse_transform(km.cluster_centers_),
columns=features).round(2)
centroides["n_clientes"] = rfm["segmento"].value_counts().sort_index().values
print(centroides)km.cluster_centers_ está en el espacio escalado (medias 0, desviaciones 1); inverse_transform lo devuelve a días, pedidos y euros. Un resultado típico en MercaFresh:
| Segmento | recencia_dias | pedidos_por_mes | gasto_medio_pedido | ratio_inactividad | n_clientes | Lectura de negocio |
|---|---|---|---|---|---|---|
| 0 | 9 | 4,1 | 78 € | 0,05 | 180 | VIP: compran mucho, a menudo y hace nada |
| 1 | 15 | 2,2 | 34 € | 0,10 | 410 | Habituales: la base sana del negocio |
| 2 | 95 | 0,6 | 41 € | 0,62 | 150 | Dormidos: valiosos pero enfriándose — riesgo de churn |
| 3 | 21 | 1,1 | 22 € | 0,28 | 260 | Ocasionales: compra esporádica y ticket bajo |
Esta tabla es el entregable real de la segmentación: el equipo de marketing puede diseñar una acción por fila (programa de fidelidad para VIP, campaña de reactivación para dormidos...). Observa el círculo que se cierra: el segmento 2 coincide con el perfil que la regresión logística de 04-02 señalaba como churn probable — el no supervisado descubre el grupo que el supervisado predecía, sin haber visto una sola etiqueta.
Limitaciones de K-means
K-means es rápido, simple y escalable, pero paga esas virtudes con supuestos fuertes:
- Clusters esféricos y de tamaño similar. Al asignar por distancia al centroide, K-means dibuja fronteras que reparten el espacio en regiones convexas: dos grupos alargados, anidados o con forma de luna los partirá mal por muy evidentes que sean a la vista (lo veremos gráficamente en 05-04, donde DBSCAN los resuelve).
- K fijado a priori. El codo y el silhouette ayudan, pero la decisión es del analista; con estructura ambigua, dos K distintos pueden ser defendibles. El clustering jerárquico (05-02) esquiva parcialmente el problema mostrando todas las escalas de agrupamiento a la vez.
- Sensibilidad a outliers. El centroide es una media, y ya vimos en 02-01 lo que un valor extremo hace con una media: un cliente con gasto altísimo arrastra su centroide o incluso secuestra un cluster para él solo. Mitigaciones: tratar outliers antes (03-01) o usar transformación log (03-03).
- Sensibilidad a la escala. Sin
StandardScaler, la feature de mayor magnitud domina la distancia y el clustering resultante es un artefacto de las unidades (03-05). No es opcional: es requisito. - Todo punto acaba en un cluster. K-means no tiene concepto de "ruido": el pedido más anómalo del mundo será asignado a algún centroide. Para detectar anomalías necesitaremos DBSCAN (05-04).
Errores Comunes y Consejos
- Olvidar escalar. El error número uno. Si tus segmentos parecen ordenados únicamente por gasto, casi seguro que agrupaste euros contra unidades sin escalar. Revisa siempre que aplicaste
StandardScalerantes defit. - Tomar el número de cluster como categoría con significado. La etiqueta 0/1/2/3 es arbitraria y puede cambiar entre ejecuciones. Nombra los segmentos por su perfil (tabla de centroides), nunca por su número.
- Buscar el codo en datos sin estructura. Si los clientes forman una nube continua sin grupos, el codo será una curva suave sin doblez y el silhouette rondará 0. Eso también es un resultado: significa que no hay segmentos naturales con esas features — prueba otras features antes que forzar un K.
- Interpretar centroides en el espacio escalado. Un centroide con
gasto = 1.8no significa 1,80 €: son desviaciones estándar. Usainverse_transformantes de presentar perfiles a negocio. - Consejo: ejecuta el clustering dos o tres veces con distintos
random_state(manteniendon_init). Si los segmentos cambian mucho, la estructura es débil y conviene desconfiar de cualquier interpretación fina.
Ejercicios
Ejercicio 1. Continúa a mano el ejemplo de la lección con los mismos 6 puntos (3, 5, 8, 40, 45, 52) pero con la inicialización $c_1 = 40$, $c_2 = 45$ y $K=2$. Itera hasta converger. ¿Llegas a la misma partición final que en la lección? ¿Qué te dice esto sobre los óptimos locales?
Ejercicio 2. Con la tabla rfm de MercaFresh (o un DataFrame sintético equivalente), calcula el codo y el silhouette para K de 2 a 8 usando solo dos features: recencia_dias y pedidos_por_mes. Elige un K justificándolo con ambos criterios y dibuja los clusters en un scatter con plt.scatter(..., c=etiquetas).
Ejercicio 3. Añade a la tabla rfm un cliente outlier con gasto_medio_pedido = 5000 (el resto de features en valores medios) y reejecuta el K-means de la lección con K=4, con y sin StandardScaler. Compara las tablas de centroides. ¿Qué le ocurre al outlier en cada caso y por qué?
Soluciones
Ejercicio 1
- Iteración 1 — asignación: 3, 5, 8 y 40 van con $c_1=40$ (están más cerca de 40 que de 45); 45 y 52 con $c_2=45$. Actualización: $c_1 = \text{media}(3,5,8,40) = 14$; $c_2 = \text{media}(45,52) = 48{,}5$.
- Iteración 2 — asignación: 40 dista 26 de $c_1$ y 8,5 de $c_2$ → cambia al cluster 2. Actualización: $c_1 = 5{,}33$; $c_2 = 45{,}67$.
- Iteración 3: nadie cambia. Convergencia en {3,5,8} y {40,45,52} — la misma partición que en la lección pese a una inicialización opuesta. En este dataset la estructura es tan clara que ambos inicios llegan al mismo óptimo; con datos ambiguos no está garantizado, y por eso existen k-means++ y
n_init.
Ejercicio 2
import numpy as np
from sklearn.metrics import silhouette_score
X2 = StandardScaler().fit_transform(rfm[["recencia_dias", "pedidos_por_mes"]])
for k in range(2, 9):
km = KMeans(n_clusters=k, random_state=42)
lab = km.fit_predict(X2)
print(f"K={k} | inercia={km.inertia_:8.1f} | sil={silhouette_score(X2, lab):.3f}")
km = KMeans(n_clusters=3, random_state=42) # ajusta al K elegido
lab = km.fit_predict(X2)
plt.scatter(X2[:, 0], X2[:, 1], c=lab, s=15)
plt.scatter(*km.cluster_centers_.T, marker="X", s=200, c="red")
plt.xlabel("recencia (esc.)"); plt.ylabel("pedidos/mes (esc.)")
plt.show()Con solo estas dos features lo habitual es que codo y silhouette apunten a K=3 (activos frecuentes, activos esporádicos, dormidos): al quitar el gasto desaparece la distinción VIP/habitual. Lección implícita: los segmentos dependen de las features elegidas, no solo del algoritmo.
Ejercicio 3
Sin escalar, la columna de gasto domina la distancia por completo: el outlier queda solo en un cluster propio (o arrastra brutalmente un centroide) y los otros tres clusters se reparten por gasto, ignorando recencia y frecuencia. Con StandardScaler el daño se reduce (el outlier queda a unas cuantas desviaciones, no a tres órdenes de magnitud), pero sigue siendo el punto más influyente de su cluster y sesga su centroide hacia arriba — la media no perdona (02-01). Tratamiento correcto: detectar y tratar el outlier en limpieza (03-01) o amortiguarlo con log1p (03-03) antes de segmentar.
Conclusión
Has cruzado la frontera del no supervisado: sin etiquetas ni "respuesta correcta", K-means descubre grupos alternando dos pasos —asignar cada punto a su centroide más cercano y recalcular centroides como medias— hasta converger. Sabes elegir K combinando el codo (inercia) con el silhouette, sabes que k-means++ y n_init te protegen de malas inicializaciones, y has segmentado los clientes de MercaFresh con sus features RFM escaladas, convirtiendo números de cluster en perfiles con nombre y acción de negocio. También conoces la letra pequeña: clusters esféricos, K a priori, alergia a outliers y a escalas dispares.
Entre esas limitaciones hay una especialmente elegante de resolver: ¿y si no tuviéramos que fijar K de antemano, sino que pudiéramos ver todas las formas de agrupar a los clientes —de 2 grupos a 20— en un solo dibujo y cortar donde más convenga? Ese dibujo existe, se llama dendrograma, y es el corazón de la siguiente lección: el clustering jerárquico.
Curso de Machine Learning
Módulo 1: Introducción al Machine Learning
- ¿Qué es el Machine Learning?
- Historia y evolución del Machine Learning
- Tipos de Machine Learning
- Aplicaciones del Machine Learning
- El flujo de trabajo de un proyecto de Machine Learning
Módulo 2: Fundamentos de Estadística y Probabilidad
- Conceptos básicos de estadística
- Distribuciones de probabilidad
- Correlación y covarianza
- Inferencia estadística
- Teorema de Bayes
Módulo 3: Preprocesamiento de Datos
- Limpieza de datos
- Manejo de datos faltantes
- Transformación de datos
- Codificación de variables categóricas
- Normalización y estandarización
- Ingeniería de características
Módulo 4: Algoritmos de Machine Learning Supervisado
- Regresión lineal
- Regresión logística
- Árboles de decisión
- Máquinas de soporte vectorial (SVM)
- K-Vecinos más cercanos (K-NN)
- Naive Bayes
- Redes neuronales
Módulo 5: Algoritmos de Machine Learning No Supervisado
- Clustering: K-means
- Clustering jerárquico
- Análisis de componentes principales (PCA)
- Análisis de agrupamiento DBSCAN
- Visualización de datos con t-SNE y UMAP
Módulo 6: Evaluación y Validación de Modelos
- División de datos: entrenamiento, validación y prueba
- Métricas de evaluación
- Validación cruzada
- Curva ROC y AUC
- Overfitting y underfitting
Módulo 7: Técnicas Avanzadas y Optimización
- Regularización: Ridge, Lasso y Elastic Net
- Ensemble Learning
- Gradient Boosting
- Redes neuronales profundas (Deep Learning)
- Optimización de hiperparámetros
Módulo 8: Implementación y Despliegue de Modelos
- Frameworks y bibliotecas populares
- Implementación de modelos en producción
- Mantenimiento y monitoreo de modelos
- Consideraciones éticas y de privacidad
Módulo 9: Proyectos Prácticos
- Proyecto 1: Predicción de precios de viviendas
- Proyecto 2: Clasificación de imágenes
- Proyecto 3: Análisis de sentimientos en redes sociales
- Proyecto 4: Detección de fraudes
- Proyecto 5: Segmentación de clientes
