AiTechWorlds
AiTechWorlds
University learning companion
Machine Learning Fundamentals · K-Means Clustering
Use this course-level map to place “K-Means Clustering” in the larger system. It complements—not replaces—a topic-specific worked diagram.
Clustering is unsupervised learning you have data but no labels. K Means finds structure by grouping similar examples together. It's one of the most widely used algorithms in data science, powering customer segmentation, anomaly detection, image compression, and document organization.
K Means iterates between two steps until convergence:
Initialize: Place k centroids randomly in the feature space
Step 1 — Assignment:
For each point, find the nearest centroid
Assign that point to that centroid's cluster
Step 2 — Update:
Move each centroid to the mean of all points assigned to it
Repeat until centroids stop moving (convergence)Iteration 0 (random init):
Centroid 1: (1, 5)
Centroid 2: (8, 2)
Centroid 3: (5, 8)
Iteration 1 (after assignment + update):
Centroid 1: (2, 4) ← Moved to mean of its cluster
Centroid 2: (7, 3)
Centroid 3: (5, 7)
... continues until centroids stabilizefrom sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
import numpy as np
import matplotlib.pyplot as plt
# Generate sample data
X, y_true = make_blobs(n_samples=500, centers=4, cluster_std=0.8, random_state=42)
# Scale data — K-Means is distance-based
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Fit K-Means
kmeans = KMeans(
n_clusters=4,
init='k-means++', # Smart initialization (better than random)
n_init=10, # Run 10 times with different seeds, keep best
max_iter=300,
random_state=42
)
kmeans.fit(X_scaled)
# Results
labels = kmeans.labels_
centroids = kmeans.cluster_centers_
inertia = kmeans.inertia_ # Sum of squared distances to centroids
print(f"Inertia: {inertia:.2f}")
print(f"Cluster sizes: {np.bincount(labels)}")
# Visualize
plt.figure(figsize=(10, 6))
scatter = plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', alpha=0.6)
centroids_unscaled = scaler.inverse_transform(centroids)
plt.scatter(centroids_unscaled[:, 0], centroids_unscaled[:, 1],
marker='*', s=300, c='red', label='Centroids')
plt.colorbar(scatter)
plt.title('K-Means Clustering Result')
plt.legend()
plt.show()inertias = []
silhouette_scores = []
k_range = range(2, 12)
from sklearn.metrics import silhouette_score
for k in k_range:
kmeans = KMeans(n_clusters=k, init='k-means++', n_init=10, random_state=42)
kmeans.fit(X_scaled)
inertias.append(kmeans.inertia_)
silhouette_scores.append(silhouette_score(X_scaled, kmeans.labels_))
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Elbow curve
ax1.plot(k_range, inertias, 'bo-')
ax1.set_xlabel('Number of Clusters (k)')
ax1.set_ylabel('Inertia')
ax1.set_title('Elbow Method')
# Silhouette scores
ax2.plot(k_range, silhouette_scores, 'ro-')
ax2.set_xlabel('Number of Clusters (k)')
ax2.set_ylabel('Silhouette Score')
ax2.set_title('Silhouette Analysis')
ax2.axvline(x=k_range[np.argmax(silhouette_scores)], linestyle='--', color='gray')
plt.tight_layout()
plt.show()
best_k = k_range[np.argmax(silhouette_scores)]
print(f"Best k by silhouette: {best_k}")Elbow method: Look for the "elbow" where adding more clusters stops significantly reducing inertia.
Silhouette score (better): Measures how similar a point is to its own cluster compared to other clusters.
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
# Customer data
np.random.seed(42)
n_customers = 1000
customer_data = pd.DataFrame({
'age': np.random.normal(40, 12, n_customers).astype(int),
'annual_income': np.random.normal(50000, 20000, n_customers),
'spending_score': np.random.normal(50, 25, n_customers),
'visits_per_month': np.random.exponential(3, n_customers),
'avg_order_value': np.random.normal(80, 40, n_customers)
})
customer_data = customer_data.clip(lower=0)
# Scale features
scaler = StandardScaler()
X_scaled = scaler.fit_transform(customer_data)
# Find optimal k
silhouettes = []
for k in range(2, 10):
km = KMeans(n_clusters=k, n_init=10, random_state=42)
km.fit(X_scaled)
silhouettes.append(silhouette_score(X_scaled, km.labels_))
best_k = range(2, 10)[np.argmax(silhouettes)]
# Fit final model
kmeans = KMeans(n_clusters=best_k, n_init=10, random_state=42)
customer_data['segment'] = kmeans.fit_predict(X_scaled)
# Analyze segments
segment_profiles = customer_data.groupby('segment').mean().round(1)
print("Customer Segment Profiles:")
print(segment_profiles)
# Name segments based on characteristics
# High income, high spending → Premium customers
# Low income, low spending → Budget shoppers
# etc.from sklearn.datasets import make_moons, make_circles
# K-Means fails on non-spherical data
X_moons, _ = make_moons(n_samples=200, noise=0.05)
kmeans = KMeans(n_clusters=2)
labels = kmeans.fit_predict(X_moons)
# K-Means will give wrong clusters — it assumes circular/spherical shapes
# DBSCAN handles non-spherical clusters
from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps=0.15, min_samples=5)
labels_dbscan = dbscan.fit_predict(X_moons)
# DBSCAN correctly finds the moon shapesfrom sklearn.cluster import MiniBatchKMeans
# For large datasets: MiniBatchKMeans is much faster
mini_kmeans = MiniBatchKMeans(
n_clusters=4,
batch_size=1000,
n_init=3,
random_state=42
)
mini_kmeans.fit(X_scaled)from sklearn.cluster import DBSCAN
# eps: maximum distance between two points to be considered neighbors
# min_samples: minimum neighbors to form a core point
dbscan = DBSCAN(eps=0.5, min_samples=5)
labels = dbscan.fit_predict(X_scaled)
# DBSCAN assigns -1 to noise/outliers
n_clusters = len(set(labels)) - (1 if -1 in labels else 0)
n_noise = list(labels).count(-1)
print(f"Clusters found: {n_clusters}")
print(f"Noise points: {n_noise}")DBSCAN advantages: finds arbitrary shapes, detects outliers, doesn't need k specified.
from sklearn.metrics import silhouette_score, davies_bouldin_score, calinski_harabasz_score
labels = kmeans.labels_
# Silhouette score: higher is better (-1 to 1)
print(f"Silhouette: {silhouette_score(X_scaled, labels):.3f}")
# Davies-Bouldin: lower is better
print(f"Davies-Bouldin: {davies_bouldin_score(X_scaled, labels):.3f}")
# Calinski-Harabasz: higher is better
print(f"Calinski-Harabasz: {calinski_harabasz_score(X_scaled, labels):.1f}")None of these metrics tell you if your clusters are meaningful only domain knowledge can do that. A clustering is good if the resulting segments are actionable and make business sense.
from sklearn.decomposition import PCA
# Reduce to 2D for visualization
pca = PCA(n_components=2)
X_2d = pca.fit_transform(X_scaled)
plt.figure(figsize=(10, 6))
scatter = plt.scatter(X_2d[:, 0], X_2d[:, 1], c=labels, cmap='tab10', alpha=0.6)
plt.colorbar(scatter, label='Cluster')
plt.xlabel(f'PC1 ({pca.explained_variance_ratio_[0]:.1%} variance)')
plt.ylabel(f'PC2 ({pca.explained_variance_ratio_[1]:.1%} variance)')
plt.title('Clusters in PCA Space')
plt.show()| Algorithm | Use When |
|---|---|
| K Means | Clusters are spherical, roughly equal size, large datasets |
| DBSCAN | Arbitrary cluster shapes, many outliers, unknown k |
| Hierarchical | Need dendrogram, small dataset, unknown k |
| GMM | Soft cluster assignments, probabilistic framework |
| Spectral | Non convex clusters, graph based data |
K Means is a great default fast, scalable, and effective when clusters are reasonably well separated.
Next lesson: Principal Component Analysis reducing dimensions to find the essential structure in data.
University learning companion
Machine Learning Fundamentals · K-Means Clustering
Complete the steps without looking at the answer first; record evidence rather than only a final result.
These are primary or established learning references for this subject. Check the relevant chapter or specification for the exact topic and version.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises