K-means
Aprenda K-Means em Python com scikit-learn. Aborda o algoritmo, escalonamento de features, escolha de K, silhouette score e erros comuns.
K-Means é um algoritmo de aprendizado de máquina não supervisionado que particiona um conjunto de dados em K clusters não sobrepostos, minimizando a distância quadrada total entre cada ponto de dados e o centroide do cluster ao qual foi atribuído. Esta página explica como o algoritmo funciona, como implementá-lo em Python com scikit-learn, como escolher o número correto de clusters e os principais erros a evitar.
O que é Agrupamento K-Means?
K-Means agrupa pontos de dados de forma que pontos no mesmo cluster sejam o mais similares possível entre si, enquanto pontos em clusters diferentes sejam o mais distintos possível. "Similar" é medido pela distância Euclidiana — a distância em linha reta entre dois pontos no espaço de features.
Como K-Means trabalha com valores brutos de distância, é um algoritmo não supervisionado: ele descobre estrutura em dados sem rótulo, sem precisar de uma variável alvo.
Aplicações comuns no mundo real incluem:
- Segmentação de clientes — agrupa clientes por hábitos de consumo e dados demográficos.
- Compressão de imagens — reduz as cores substituindo cada pixel pela cor do centroide mais próximo.
- Agrupamento de documentos — agrupa artigos de notícias ou tickets de suporte por tema.
- Detecção de anomalias — pontos distantes de todos os centroides podem ser outliers.
Como o Algoritmo Funciona
K-Means alterna entre duas etapas até que as atribuições de cluster parem de mudar (convergência):
- Inicializar — escolhe K centroides iniciais (pontos de partida). A estratégia padrão
k-means++os espaça para reduzir inicializações ruins. - Atribuir — atribui cada ponto de dados ao centroide mais próximo, formando K clusters.
- Atualizar — recalcula cada centroide como a média de todos os pontos atribuídos a ele.
- Repetir as etapas 2–3 até que nenhum ponto mude de cluster, ou até atingir o número máximo de iterações.
A quantidade sendo minimizada é chamada de inércia (também escrita WCSS — soma dos quadrados dentro do cluster):
inertia = sum over all points of (distance from point to its centroid)²Menor inércia significa clusters mais compactos e coesos.
Inicialização k-means++
O padrão init='k-means++' (padrão do scikit-learn) seleciona o primeiro centroide aleatoriamente e, em seguida, escolhe cada centroide subsequente com probabilidade proporcional ao seu quadrado da distância até o centroide já escolhido mais próximo. Isso espalha os centroides iniciais e geralmente encontra clusters melhores mais rapidamente do que a inicialização puramente aleatória.
Escalonamento de Features Antes do Agrupamento
K-Means depende inteiramente da distância Euclidiana. Se uma feature é medida em milhares (por exemplo, renda anual) e outra em dígitos simples (por exemplo, uma avaliação de 1 a 5), a feature de grande valor domina o cálculo de distância e a feature de menor alcance é efetivamente ignorada. Sempre escalone suas features primeiro.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)StandardScaler centraliza cada feature na média 0 e desvio padrão 1. Veja o capítulo Scale para alternativas como MinMaxScaler.
Implementando K-Means com scikit-learn
O exemplo a seguir gera um conjunto de dados sintético, escala-o, ajusta o K-Means e inspeciona os 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 principais após o ajuste:
| Atributo | Descrição |
|---|---|
labels_ | ID do cluster (0 a K-1) para cada ponto de treinamento |
cluster_centers_ | Coordenadas dos K centroides (formato: K × n_features) |
inertia_ | WCSS total — menor é melhor |
n_iter_ | Número de iterações EM até a convergência |
Prevendo a associação de cluster para novos dados
Após ajustar o scaler e o modelo nos dados de treinamento, use scaler.transform() + kmeans.predict() para atribuir novos pontos aos clusters existentes. Nunca reajuste o scaler em novos dados.
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]Escolhendo o Número Correto de Clusters (K)
K-Means exige que você especifique K antecipadamente, o que muitas vezes é a parte mais difícil. Duas técnicas complementares ajudam: o método Elbow e o silhouette score.
O método Elbow
Trace a inércia (WCSS) em relação a K. À medida que K cresce, a inércia sempre diminui — mas a taxa de melhoria desacelera. O "cotovelo" é o ponto onde adicionar outro cluster gera retornos decrescentes.
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()Neste conjunto de dados (3 clusters naturais), a inércia cai drasticamente de K=1 a K=3 e depois se estabiliza. O cotovelo em K=3 confirma o número verdadeiro de clusters.
O silhouette score
O silhouette score mede o quão bem cada ponto se encaixa no seu próprio cluster em comparação com o cluster vizinho mais próximo. Varia de -1 (cluster errado) a +1 (cluster perfeitamente separado). Uma pontuação acima de 0,5 é geralmente boa; acima de 0,7 é forte.
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}')Saída:
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 produz o maior silhouette score (0,848), confirmando que três clusters descrevem melhor estes dados. Use ambos os métodos juntos — o gráfico Elbow indica onde a melhoria estagna; o silhouette score fornece um único número para comparar entre os valores de K.
Visualizando Clusters K-Means
Gráficos de dispersão revelam se os clusters estão bem separados. Para conjuntos de dados com mais de duas features, você primeiro aplicaria redução de dimensionalidade (como PCA) antes de plotar.
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 cor representa um cluster. Cruzes vermelhas marcam os centroides — a posição média de todos os pontos naquele cluster.
Vantagens e Limitações
Vantagens
- Simples e rápido. K-Means é O(n · k · i) onde n é o número de pontos, k é o número de clusters e i é o número de iterações. Escala para milhões de pontos de dados com
MiniBatchKMeans. - Fácil de interpretar. Os centroides fornecem um resumo concreto dos valores típicos de cada cluster.
- Funciona bem quando os clusters são aproximadamente esféricos e de tamanhos iguais.
- Não requer dados rotulados. Totalmente não supervisionado — nenhuma variável alvo necessária.
Limitações
- K deve ser especificado antecipadamente. Use o método Elbow e o silhouette score para orientar a escolha, mas nenhum dos dois fornece uma resposta definitiva para dados ambíguos.
- Sensível à inicialização. Uma configuração inicial ruim pode convergir para um ótimo local.
k-means++e múltiplas reinicializações (n_init=10) reduzem esse risco. - Assume clusters esféricos e de tamanhos iguais. K-Means tem dificuldades com clusters alongados, em forma de crescente ou muito desiguais. Use Agrupamento Hierárquico ou DBSCAN para formas não globulares.
- Sensível a outliers. Um único ponto extremo pode puxar um centroide para longe do verdadeiro centro do cluster. Remova ou limite outliers antes do ajuste.
- Sensível à escala. Features em escalas diferentes devem ser padronizadas — veja o capítulo Scale.
Erros Comuns
Ignorar o escalonamento de features. Este é o erro mais comum de todos. Sem escalonamento, features com grandes intervalos numéricos dominam o cálculo de distância e features de menor intervalo são ignoradas.
Definir n_init=1. O padrão em versões mais antigas do scikit-learn era n_init='warn' (que alertava se você não o definisse). Sempre defina n_init=10 (ou mais) para executar K-Means com 10 inicializações aleatórias diferentes e manter o melhor resultado.
Reajustar o scaler em novos dados. Ajuste o StandardScaler uma vez nos seus dados de treinamento e, em seguida, chame transform() em quaisquer novos dados. Chamar fit_transform() novamente em novos dados altera a escala e torna o modelo inconsistente.
Tratar IDs de cluster como ordem significativa. Os IDs de cluster (0, 1, 2, …) são rótulos arbitrários atribuídos pelo scikit-learn. Eles não são ordinais — o cluster 2 não é "maior" ou "mais importante" do que o cluster 0. Compare clusters pelos valores dos centroides e contagens de membros.
Usar K-Means em dados não numéricos. K-Means requer features numéricas para calcular distâncias. Para dados categóricos, codifique-os primeiro (por exemplo, com one-hot encoding) e considere se a distância Euclidiana ainda faz sentido para o seu caso de uso. Veja o capítulo de Dados Categóricos.
K-Means vs Agrupamento Hierárquico
| Feature | K-Means | Agrupamento Hierárquico |
|---|---|---|
| Número de clusters | Deve especificar K antes de executar | Pode escolher após a execução (inspecionar dendrograma) |
| Escalabilidade | Escala para milhões de linhas | O(n²) memória — impraticável acima de ~10 000 linhas |
| Determinismo | Aleatório (use random_state para reprodutibilidade) | Totalmente determinístico |
| Formas de clusters | Melhor para clusters esféricos | Flexível com diferentes métodos de ligação |
| Saída | Atribuições de cluster planas | Árvore (dendrograma) mostrando histórico de fusões |
Use K-Means quando seu conjunto de dados for grande e você já tiver uma boa estimativa de K. Use Agrupamento Hierárquico quando quiser explorar vários valores de K sem reajustar, ou quando os clusters não forem esféricos.
Lista de Verificação Prática
Siga esta lista de verificação ao aplicar K-Means a um novo conjunto de dados:
- Remova ou limite outliers — valores extremos distorcem os centroides.
- Codifique variáveis categóricas — K-Means requer entrada numérica.
- Escale as features com
StandardScaler(ouMinMaxScalerse a distribuição da feature for limitada). - Use o gráfico Elbow para estreitar os valores candidatos de K.
- Use o silhouette score para comparar valores específicos de K quantitativamente.
- Defina
n_init=10einit='k-means++'para uma inicialização robusta. - Inspecione os tamanhos dos clusters com
np.bincount(labels)— tamanhos muito desiguais podem indicar um K inadequado ou contaminação por outliers. - Visualize com um gráfico de dispersão (ou projeção PCA para dados de alta dimensionalidade).
Capítulos Relacionados
- Scale — padronizando features antes do agrupamento
- Agrupamento Hierárquico — uma alternativa que não requer K antecipadamente
- K-Nearest Neighbors — um método supervisionado que também usa distância
- Árvore de Decisão — classificação supervisionada sem suposições de distância
- Divisão Treino / Teste — avaliando modelos de aprendizado de máquina
- Gráfico de Dispersão — visualizando clusters com Matplotlib