K-means
Aprende K-Means en Python con scikit-learn. Cubre el algoritmo, escalado de características, elección de K, puntuaciones de silueta y errores comunes.
K-Means es un algoritmo de aprendizaje automático no supervisado que divide un conjunto de datos en K clústeres no superpuestos minimizando la distancia cuadrada total entre cada punto de datos y el centroide del clúster asignado. Esta página explica cómo funciona el algoritmo, cómo implementarlo en Python con scikit-learn, cómo elegir el número correcto de clústeres y los errores clave que se deben evitar.
¿Qué es el clustering K-Means?
K-Means agrupa los puntos de datos de modo que los puntos dentro del mismo clúster sean lo más similares posible, mientras que los puntos en clústeres diferentes sean lo más distintos posible. La "similitud" se mide mediante la distancia euclidiana — la distancia en línea recta entre dos puntos en el espacio de características.
Dado que K-Means trabaja con valores de distancia sin procesar, es un algoritmo no supervisado: descubre estructura en datos sin etiquetas sin necesitar una variable objetivo.
Las aplicaciones comunes en el mundo real incluyen:
- Segmentación de clientes — agrupar clientes según hábitos de gasto y datos demográficos.
- Compresión de imágenes — reducir colores reemplazando cada píxel con el color de su centroide más cercano.
- Agrupación de documentos — agrupar artículos de noticias o tickets de soporte por tema.
- Detección de anomalías — los puntos alejados de todos los centroides pueden ser valores atípicos.
Cómo funciona el algoritmo
K-Means alterna entre dos pasos hasta que las asignaciones de clústeres dejan de cambiar (convergencia):
- Inicializar — elegir K centroides iniciales (puntos de partida). La estrategia predeterminada
k-means++los distribuye para reducir malas inicializaciones. - Asignar — asignar cada punto de datos al centroide más cercano, formando K clústeres.
- Actualizar — recalcular cada centroide como la media de todos los puntos asignados a él.
- Repetir los pasos 2–3 hasta que ningún punto cambie de clúster, o se alcance el número máximo de iteraciones.
La cantidad que se minimiza se denomina inercia (también escrita WCSS — suma de cuadrados dentro del clúster):
inertia = sum over all points of (distance from point to its centroid)²Una inercia menor significa clústeres más compactos y cohesionados.
Inicialización k-means++
El valor predeterminado init='k-means++' (predeterminado en scikit-learn) selecciona el primer centroide aleatoriamente, luego elige cada centroide siguiente con probabilidad proporcional a su distancia cuadrada desde el centroide ya elegido más cercano. Esto distribuye los centroides iniciales y generalmente encuentra mejores clústeres más rápido que la inicialización puramente aleatoria.
Escalado de características antes del clustering
K-Means depende totalmente de la distancia euclidiana. Si una característica se mide en miles (por ejemplo, ingresos anuales) y otra en dígitos simples (por ejemplo, una calificación del 1 al 5), la característica de mayor valor domina el cálculo de distancias y la característica de menor rango queda efectivamente ignorada. Siempre escala tus características primero.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)StandardScaler centra cada característica a media 0 y desviación estándar 1. Consulta el capítulo de Escalado para conocer alternativas como MinMaxScaler.
Implementación de K-Means con scikit-learn
El siguiente ejemplo genera un conjunto de datos sintético, lo escala, ajusta K-Means e inspecciona los resultados.
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
import numpy as np
# Generate 300 points in 3 natural clusters
X, y_true = make_blobs(n_samples=300, centers=3, random_state=42)
# Scale the features — always required before K-Means
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Fit K-Means with 3 clusters
kmeans = KMeans(n_clusters=3, init='k-means++', n_init=10, random_state=42)
kmeans.fit(X_scaled)
# Cluster assignment for every training point (0-indexed)
labels = kmeans.labels_
print('Cluster labels (first 10):', labels[:10])
# e.g. [2 2 0 1 0 1 2 2 0 1]
print('Cluster sizes:', np.bincount(labels))
# e.g. [100 100 100]
print('Inertia (WCSS):', round(kmeans.inertia_, 2))
# e.g. 18.26
print('Iterations to converge:', kmeans.n_iter_)
# e.g. 2Atributos clave después del ajuste:
| Atributo | Descripción |
|---|---|
labels_ | ID de clúster (0 a K-1) para cada punto de entrenamiento |
cluster_centers_ | Coordenadas de los K centroides (forma: K × n_features) |
inertia_ | WCSS total — cuanto menor, mejor |
n_iter_ | Número de iteraciones EM hasta la convergencia |
Predicción de pertenencia a clúster para datos nuevos
Después de ajustar el escalador y el modelo sobre los datos de entrenamiento, usa scaler.transform() + kmeans.predict() para asignar nuevos puntos a los clústeres existentes. Nunca vuelvas a ajustar el escalador sobre datos nuevos.
import numpy as np
# Two new, unseen points (original feature scale)
new_points = np.array([[0.5, -0.5],
[-1.0, 2.0]])
# Transform with the SAME scaler used during training
new_scaled = scaler.transform(new_points)
# Predict cluster membership
predictions = kmeans.predict(new_scaled)
print('Predicted clusters:', predictions)
# e.g. [2 0]Cómo elegir el número correcto de clústeres (K)
K-Means requiere que especifiques K de antemano, lo cual suele ser la parte más difícil. Dos técnicas complementarias ayudan: el método del codo y la puntuación de silueta.
El método del codo
Representa la inercia (WCSS) frente a K. A medida que K crece, la inercia siempre disminuye — pero la tasa de mejora se ralentiza. El "codo" es el punto donde agregar otro clúster produce rendimientos decrecientes.
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
X, _ = make_blobs(n_samples=300, centers=3, random_state=42)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
wcss = []
for k in range(1, 11):
km = KMeans(n_clusters=k, n_init=10, random_state=42)
km.fit(X_scaled)
wcss.append(km.inertia_)
plt.plot(range(1, 11), wcss, marker='o')
plt.xlabel('Number of clusters (K)')
plt.ylabel('Inertia (WCSS)')
plt.title('Elbow method')
plt.tight_layout()
plt.show()En este conjunto de datos (3 clústeres naturales), la inercia cae bruscamente de K=1 a K=3, luego se estabiliza. El codo en K=3 confirma el número real de clústeres.
La puntuación de silueta
La puntuación de silueta mide qué tan bien encaja cada punto en su propio clúster en comparación con el clúster vecino más cercano. Varía de -1 (clúster equivocado) a +1 (clúster perfectamente separado). Una puntuación superior a 0,5 es generalmente buena; por encima de 0,7 es excelente.
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
X, _ = make_blobs(n_samples=300, centers=3, random_state=42)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
for k in range(2, 8):
km = KMeans(n_clusters=k, n_init=10, random_state=42)
labels = km.fit_predict(X_scaled)
score = silhouette_score(X_scaled, labels)
print(f'k={k} silhouette={score:.3f}')Salida:
k=2 silhouette=0.688
k=3 silhouette=0.848
k=4 silhouette=0.679
k=5 silhouette=0.522
k=6 silhouette=0.357
k=7 silhouette=0.371K=3 produce la puntuación de silueta más alta (0.848), confirmando que tres clústeres describen mejor estos datos. Usa ambos métodos juntos — el gráfico del codo indica dónde se estanca la mejora; la puntuación de silueta proporciona un único número para comparar entre valores de K.
Visualización de clústeres K-Means
Los diagramas de dispersión revelan si los clústeres están bien separados. Para conjuntos de datos con más de dos características, primero aplicarías reducción de dimensionalidad (como PCA) antes de graficar.
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
X, _ = make_blobs(n_samples=300, centers=3, random_state=42)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
kmeans = KMeans(n_clusters=3, n_init=10, random_state=42)
labels = kmeans.fit_predict(X_scaled)
centers = kmeans.cluster_centers_
plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=labels, cmap='viridis', s=40, alpha=0.7)
plt.scatter(centers[:, 0], centers[:, 1],
c='red', marker='X', s=200, zorder=5, label='Centroids')
plt.legend()
plt.title('K-Means clustering (k=3)')
plt.xlabel('Feature 1 (scaled)')
plt.ylabel('Feature 2 (scaled)')
plt.tight_layout()
plt.show()Cada color representa un clúster. Las cruces rojas marcan los centroides — la posición media de todos los puntos de ese clúster.
Ventajas y limitaciones
Ventajas
- Simple y rápido. K-Means es O(n · k · i) donde n es el número de puntos, k es el número de clústeres e i es el número de iteraciones. Escala a millones de puntos de datos con
MiniBatchKMeans. - Fácil de interpretar. Los centroides proporcionan un resumen concreto de los valores típicos de cada clúster.
- Funciona bien cuando los clústeres son aproximadamente esféricos y de tamaño similar.
- No requiere datos etiquetados. Totalmente no supervisado — no se necesita variable objetivo.
Limitaciones
- K debe especificarse de antemano. Usa el método del codo y la puntuación de silueta para orientar la elección, pero ninguno ofrece una respuesta definitiva para datos ambiguos.
- Sensible a la inicialización. Una mala configuración inicial puede converger a un óptimo local.
k-means++y múltiples reinicios (n_init=10) reducen este riesgo. - Asume clústeres esféricos de tamaño similar. K-Means tiene dificultades con clústeres alargados, en forma de media luna o muy desiguales. Usa Clustering Jerárquico o DBSCAN para formas no globulares.
- Sensible a valores atípicos. Un único punto extremo puede desplazar un centroide lejos del centro real del clúster. Elimina o limita los valores atípicos antes del ajuste.
- Sensible a la escala. Las características en escalas diferentes deben estandarizarse — consulta el capítulo de Escalado.
Errores comunes
Omitir el escalado de características. Este es el error más común. Sin escalado, las características con rangos numéricos grandes dominan el cálculo de distancias y las características de menor rango se ignoran.
Establecer n_init=1. El valor predeterminado en versiones más antiguas de scikit-learn era n_init='warn' (que advertía si no lo establecías). Siempre establece n_init=10 (o más) para ejecutar K-Means con 10 inicializaciones aleatorias diferentes y conservar el mejor resultado.
Reajustar el escalador en datos nuevos. Ajusta el StandardScaler una vez sobre tus datos de entrenamiento, luego llama a transform() en cualquier dato nuevo. Llamar a fit_transform() nuevamente sobre datos nuevos cambia la escala y hace que el modelo sea inconsistente.
Tratar los IDs de clúster como un orden significativo. Los IDs de clúster (0, 1, 2, …) son etiquetas arbitrarias asignadas por scikit-learn. No son ordinales — el clúster 2 no es "más grande" ni "más importante" que el clúster 0. Compara los clústeres por sus valores de centroide y conteos de miembros.
Usar K-Means con datos no numéricos. K-Means requiere características numéricas para calcular distancias. Para datos categóricos, codifícalos primero (por ejemplo, con codificación one-hot) y considera si la distancia euclidiana sigue teniendo sentido para tu caso de uso. Consulta el capítulo de Datos Categóricos.
K-Means vs Clustering Jerárquico
| Característica | K-Means | Clustering Jerárquico |
|---|---|---|
| Número de clústeres | Debe especificarse K antes de ejecutar | Puede elegirse después de ejecutar (inspeccionar dendrograma) |
| Escalabilidad | Escala a millones de filas | O(n²) de memoria — impracticable por encima de ~10 000 filas |
| Determinismo | Aleatorio (usa random_state para reproducibilidad) | Totalmente determinista |
| Formas de clústeres | Mejor para clústeres esféricos | Flexible con diferentes métodos de enlace |
| Salida | Asignaciones planas de clústeres | Árbol (dendrograma) que muestra el historial de fusiones |
Usa K-Means cuando tu conjunto de datos sea grande y ya tengas una buena estimación de K. Usa Clustering Jerárquico cuando quieras explorar varios valores de K sin reajustar, o cuando los clústeres puedan no ser esféricos.
Lista de verificación práctica
Sigue esta lista de verificación al aplicar K-Means a un nuevo conjunto de datos:
- Elimina o limita los valores atípicos — los valores extremos distorsionan los centroides.
- Codifica las variables categóricas — K-Means requiere entrada numérica.
- Escala las características con
StandardScaler(oMinMaxScalersi la distribución de la característica está acotada). - Usa el gráfico del codo para reducir los valores candidatos de K.
- Usa la puntuación de silueta para comparar cuantitativamente valores específicos de K.
- Establece
n_init=10einit='k-means++'para una inicialización robusta. - Inspecciona los tamaños de los clústeres con
np.bincount(labels)— tamaños muy desiguales pueden indicar una K deficiente o contaminación por valores atípicos. - Visualiza con un diagrama de dispersión (o proyección PCA para datos de alta dimensionalidad).
Capítulos relacionados
- Escalado — estandarización de características antes del clustering
- Clustering Jerárquico — una alternativa que no requiere K de antemano
- K-Vecinos más Cercanos — un método supervisado que también usa distancias
- Árbol de Decisión — clasificación supervisada sin suposiciones de distancia
- División Entrenamiento / Prueba — evaluación de modelos de aprendizaje automático
- Diagrama de Dispersión — visualización de clústeres con Matplotlib