Segmentation, deduplication and exploratory structure-finding. The main decision is whether you know how many groups you want — KMeans requires you to say, density methods do not.
Contents
KMeanscommon
Default. You must specify n_clusters
MiniBatchKMeans
Same, for large data
DBSCAN
Density-based. Arbitrary shapes, marks outliers, no cluster count needed
HDBSCAN
DBSCAN without the hard epsilon parameter (1.3+). Usually preferable
AgglomerativeClustering
Hierarchical, gives a dendrogram
SpectralClustering, Birch, OPTICS, MeanShift
Situational
Worked examples 2
Runnable, with the arguments that change the outcome
called out. Output shapes are shown as comments where they help.
Choosing the number of clusters honestly
KMeans requires you to specify k. Silhouette score is a better guide than the elbow plot, which is usually ambiguous.
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
for k in range(2, 9):
km = KMeans(n_clusters=k, n_init=10, random_state=42).fit(X_scaled)
print(f"k={k} inertia={km.inertia_:12.0f} "
f"silhouette={silhouette_score(X_scaled, km.labels_):.3f}")
# k=2 inertia= 418203 silhouette=0.412
# k=3 inertia= 312887 silhouette=0.389
# k=4 inertia= 266401 silhouette=0.301 <- quality falling
#
# Inertia always decreases with k, so it can never tell you when to
# stop. Silhouette peaks, which is a usable signal.
Density clustering when you do not know k
HDBSCAN finds clusters of varying density, marks genuine outliers as noise, and needs no cluster count.
from sklearn.cluster import HDBSCAN # 1.3+
import numpy as np
hdb = HDBSCAN(
min_cluster_size=50, # smallest group you would call a cluster
min_samples=10, # higher = more points labelled noise
metric="euclidean",
)
labels = hdb.fit_predict(X_scaled)
print("clusters:", len(set(labels)) - (1 if -1 in labels else 0))
print("noise points:", (labels == -1).sum())
# Label -1 means noise, not a cluster. That is a feature: KMeans is
# forced to assign every point somewhere, so genuine outliers get
# absorbed into whichever cluster is least wrong.