A complete K-Means workflow in Python — scaling, choosing k via the elbow method and silhouette score together, fitting the final model, and visualizing the resulting clusters.
The Full Workflow
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as plt
# 1. Dataset -- customer annual spend and visit frequency
df = pd.DataFrame({
"annual_spend": [1200, 1500, 1300, 40000, 42000, 39000, 800, 1100, 41000],
"visits_per_month": [1, 2, 1, 8, 9, 7, 1, 2, 8],
})
# 2. Scale -- essential, since spend and visits are on very different ranges
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df)
# 3. Choose k using elbow + silhouette together
wcss, silhouettes = [], []
k_range = range(2, 6)
for k in k_range:
model = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = model.fit_predict(X_scaled)
wcss.append(model.inertia_)
silhouettes.append(silhouette_score(X_scaled, labels))
for k, w, s in zip(k_range, wcss, silhouettes):
print(f"k={k}: WCSS={w:.2f}, silhouette={s:.3f}")
# 4. Fit the final model with the chosen k
final_model = KMeans(n_clusters=2, random_state=42, n_init=10)
df["cluster"] = final_model.fit_predict(X_scaled)
print(df)
Visualizing the Clusters
plt.scatter(df["annual_spend"], df["visits_per_month"], c=df["cluster"], cmap="viridis")
centers_original_scale = scaler.inverse_transform(final_model.cluster_centers_)
plt.scatter(centers_original_scale[:, 0], centers_original_scale[:, 1],
c="red", marker="X", s=200, label="Centroids")
plt.xlabel("Annual Spend"); plt.ylabel("Visits per Month"); plt.legend()
plt.show()
Notice scaler.inverse_transform() is used to plot centroids back in the original, human-readable units — the model works in scaled space, but the visualization should communicate in units a stakeholder actually understands.
Predicting a Cluster for a New Point
new_customer = [[2000, 3]]
new_customer_scaled = scaler.transform(new_customer)
predicted_cluster = final_model.predict(new_customer_scaled)
print(predicted_cluster)
Common Mistakes
- Forgetting to scale features before clustering — the single most common K-Means mistake, same root cause as unscaled KNN.
- Fitting the scaler after clustering, or on the full dataset when a train/production split matters — fit scalers only on the data you're actually clustering from, then apply consistently to new points.
- Interpreting cluster labels (0, 1, 2, ...) as having any inherent order or meaning — they're arbitrary identifiers, not ranks.
Interview Relevance
Q: "Why do you scale features before running K-Means, specifically?" K-Means assigns points based on Euclidean distance to centroids — an unscaled feature with a much larger numeric range (like raw annual spend in the thousands vs visit counts in single digits) would dominate every distance calculation, effectively making the clustering ignore the smaller-range feature almost entirely.
Practice Question
Modify the workflow above to also print each cluster's mean annual_spend and visits_per_month in the original (unscaled) units, to help interpret what each cluster represents.