K-Medoids Clustering
Theory, Python Implementation and Comparison with K-Means
1. What is K-Medoids?
K-Medoids is an unsupervised machine learning clustering algorithm. It divides a dataset into a predefined number of groups called clusters.
The main idea of K-Medoids is to select an actual observation from the dataset as the representative point of each cluster. This representative point is called a medoid.
How K-Medoids Works
- Choose the number of clusters, K.
- Select K initial medoids.
- Calculate the distance between every data point and every medoid.
- Assign each data point to its nearest medoid.
- Calculate the total clustering cost.
- Try replacing a medoid with another data point.
- If the replacement reduces the cost, keep the replacement.
- Repeat until no useful replacement improves the clustering.
Manhattan Distance
In the example below, Manhattan distance is used.
Example
Suppose the medoid is (9,3) and the point is (8,4).
Therefore, the distance between the two points is 2.
2. Dataset Used in the Example
The following 10-point dataset is used for both K-Means and K-Medoids comparison.
| Point | X | Y |
|---|---|---|
| P1 | 9 | 3 |
| P2 | 8 | 4 |
| P3 | 4 | 6 |
| P4 | 8 | 5 |
| P5 | 2 | 5 |
| P6 | 3 | 8 |
| P7 | 5 | 8 |
| P8 | 4 | 4 |
| P9 | 10 | 4 |
| P10 | 9 | 6 |
Initial Medoid 1 = (4,6)
Initial Medoid 2 = (9,3)
3. Python Implementation of K-Medoids
The following code implements K-Medoids using the same dataset. It uses Manhattan distance and creates two clusters.
# ==========================================
# K-MEDOIDS CLUSTERING
# ==========================================
# Install library in Google Colab
!pip install scikit-learn-extra -q
# Import libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn_extra.cluster import KMedoids
# Dataset
data = np.array([
[9, 3],
[8, 4],
[4, 6],
[8, 5],
[2, 5],
[3, 8],
[5, 8],
[4, 4],
[10, 4],
[9, 6]
])
# Create DataFrame
df = pd.DataFrame(data, columns=["X", "Y"])
print("Dataset:")
print(df)
# Number of clusters
k = 2
# Create K-Medoids model
model = KMedoids(
n_clusters=k,
metric="manhattan",
random_state=42
)
# Train model
model.fit(data)
# Cluster labels
labels = model.labels_
print("\nCluster Labels:")
print(labels)
# Get medoids
medoid_indices = model.medoid_indices_
medoids = data[medoid_indices]
print("\nMedoids:")
for i, medoid in enumerate(medoids):
print(
"Medoid", i + 1,
"=", tuple(medoid)
)
# Display clusters
for cluster in range(k):
print("\nCluster", cluster + 1)
cluster_points = data[labels == cluster]
print(cluster_points)
# Total clustering cost
print("\nTotal Cost:")
print(model.inertia_)
# Plot clusters
plt.figure(figsize=(9, 7))
for cluster in range(k):
points = data[labels == cluster]
plt.scatter(
points[:, 0],
points[:, 1],
s=100,
label=f"Cluster {cluster + 1}"
)
# Plot medoids
plt.scatter(
medoids[:, 0],
medoids[:, 1],
s=250,
marker="X",
label="Medoids"
)
# Point labels
for i, point in enumerate(data):
plt.annotate(
f"P{i + 1}",
(point[0], point[1]),
xytext=(5, 5),
textcoords="offset points"
)
plt.xlabel("X")
plt.ylabel("Y")
plt.title("K-Medoids Clustering")
plt.legend()
plt.grid(True)
plt.show()
4. K-Means vs K-Medoids
Both algorithms are clustering techniques, but they calculate their cluster representatives differently.
| Feature | K-Means | K-Medoids |
|---|---|---|
| Type | Unsupervised | Unsupervised |
| Representative | Centroid | Medoid |
| Representative is actual data point? | No | Yes |
| Center calculation | Mean | Actual observation |
| Distance in this example | Euclidean | Manhattan |
| Effect of outliers | More sensitive | Generally more robust |
| Speed | Usually faster | Usually more computationally expensive |
| Center can be a new point? | Yes | No |
Calculates the average of points to create a centroid.
K-Medoids:
Selects an existing observation as the medoid.
5. Result Comparison for This Dataset
| Cluster | K-Means | K-Medoids |
|---|---|---|
| Cluster 1 | P3, P5, P6, P7, P8 | P3, P5, P6, P7, P8 |
| Cluster 2 | P1, P2, P4, P9, P10 | P1, P2, P4, P9, P10 |
| Cluster 1 Representative | (3.6, 6.2) | (4, 6) |
| Cluster 2 Representative | (8.8, 4.4) | (9, 3) |
K-Means produces the new centroids:
K-Medoids uses actual observations:
6. Single Python Program: K-Means + K-Medoids
The following program performs both algorithms on the same dataset and displays their results side-by-side.
# ==========================================================
# K-MEANS AND K-MEDOIDS ON THE SAME DATASET
# ==========================================================
# Install K-Medoids library in Google Colab
!pip install scikit-learn-extra -q
# ----------------------------------------------------------
# IMPORT LIBRARIES
# ----------------------------------------------------------
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn_extra.cluster import KMedoids
# ----------------------------------------------------------
# DATASET
# ----------------------------------------------------------
data = np.array([
[9, 3],
[8, 4],
[4, 6],
[8, 5],
[2, 5],
[3, 8],
[5, 8],
[4, 4],
[10, 4],
[9, 6]
])
# ----------------------------------------------------------
# DISPLAY DATASET
# ----------------------------------------------------------
df = pd.DataFrame(
data,
columns=["X", "Y"]
)
print("ORIGINAL DATASET")
print(df)
# ----------------------------------------------------------
# NUMBER OF CLUSTERS
# ----------------------------------------------------------
K = 2
# ==========================================================
# K-MEANS
# ==========================================================
kmeans = KMeans(
n_clusters=K,
init=np.array([
[4, 6],
[9, 3]
]),
n_init=1,
random_state=42
)
kmeans.fit(data)
# K-Means labels
kmeans_labels = kmeans.labels_
# K-Means centroids
kmeans_centroids = kmeans.cluster_centers_
# K-Means cost
kmeans_cost = kmeans.inertia_
print("\n")
print("=" * 50)
print("K-MEANS RESULTS")
print("=" * 50)
print("\nCluster Labels:")
print(kmeans_labels)
print("\nCentroids:")
for i, centroid in enumerate(kmeans_centroids):
print(
"Centroid", i + 1,
"=", tuple(np.round(centroid, 2))
)
print("\nK-Means SSE:")
print(round(kmeans_cost, 2))
# Display K-Means clusters
for cluster in range(K):
print("\nK-Means Cluster", cluster + 1)
points = data[kmeans_labels == cluster]
print(points)
# ==========================================================
# K-MEDOIDS
# ==========================================================
kmedoids = KMedoids(
n_clusters=K,
metric="manhattan",
init=np.array([
[4, 6],
[9, 3]
]),
random_state=42
)
kmedoids.fit(data)
# K-Medoids labels
kmedoids_labels = kmedoids.labels_
# K-Medoids medoid indices
medoid_indices = kmedoids.medoid_indices_
# K-Medoids medoids
kmedoids_medoids = data[medoid_indices]
# K-Medoids cost
kmedoids_cost = kmedoids.inertia_
print("\n")
print("=" * 50)
print("K-MEDOIDS RESULTS")
print("=" * 50)
print("\nCluster Labels:")
print(kmedoids_labels)
print("\nMedoids:")
for i, medoid in enumerate(kmedoids_medoids):
print(
"Medoid", i + 1,
"=", tuple(medoid)
)
print("\nK-Medoids Cost:")
print(kmedoids_cost)
# Display K-Medoids clusters
for cluster in range(K):
print("\nK-Medoids Cluster", cluster + 1)
points = data[kmedoids_labels == cluster]
print(points)
# ==========================================================
# SIDE-BY-SIDE COMPARISON
# ==========================================================
print("\n")
print("=" * 70)
print("K-MEANS vs K-MEDOIDS")
print("=" * 70)
print("\nK-MEANS")
for cluster in range(K):
points = data[kmeans_labels == cluster]
print(
"Cluster",
cluster + 1,
":",
[tuple(p) for p in points]
)
print("\nK-MEDOIDS")
for cluster in range(K):
points = data[kmedoids_labels == cluster]
print(
"Cluster",
cluster + 1,
":",
[tuple(p) for p in points]
)
print("\nK-Means Centroids:")
for centroid in kmeans_centroids:
print(tuple(np.round(centroid, 2)))
print("\nK-Medoids Medoids:")
for medoid in kmedoids_medoids:
print(tuple(medoid))
# ==========================================================
# PLOT K-MEANS
# ==========================================================
plt.figure(figsize=(8, 6))
for cluster in range(K):
points = data[kmeans_labels == cluster]
plt.scatter(
points[:, 0],
points[:, 1],
s=100,
label=f"Cluster {cluster + 1}"
)
plt.scatter(
kmeans_centroids[:, 0],
kmeans_centroids[:, 1],
s=250,
marker="X",
label="Centroids"
)
for i, point in enumerate(data):
plt.annotate(
f"P{i + 1}",
(point[0], point[1]),
xytext=(5, 5),
textcoords="offset points"
)
plt.xlabel("X")
plt.ylabel("Y")
plt.title("K-Means Clustering")
plt.legend()
plt.grid(True)
plt.show()
# ==========================================================
# PLOT K-MEDOIDS
# ==========================================================
plt.figure(figsize=(8, 6))
for cluster in range(K):
points = data[kmedoids_labels == cluster]
plt.scatter(
points[:, 0],
points[:, 1],
s=100,
label=f"Cluster {cluster + 1}"
)
plt.scatter(
kmedoids_medoids[:, 0],
kmedoids_medoids[:, 1],
s=250,
marker="X",
label="Medoids"
)
for i, point in enumerate(data):
plt.annotate(
f"P{i + 1}",
(point[0], point[1]),
xytext=(5, 5),
textcoords="offset points"
)
plt.xlabel("X")
plt.ylabel("Y")
plt.title("K-Medoids Clustering")
plt.legend()
plt.grid(True)
plt.show()
7. Conclusion
K-Means and K-Medoids are both unsupervised clustering algorithms used to divide observations into groups.
- K-Means calculates a centroid using the mean of the observations in a cluster.
- K-Medoids selects an actual observation as the representative of a cluster.
- In this example, both methods produce the same two groups.
- However, K-Means produces new center points, whereas K-Medoids uses existing observations.
- K-Medoids can be useful when the use of an actual observation as the cluster representative is important.
K-Means → Mean = Centroid
K-Medoids → Existing Data Point = Medoid
No comments:
Post a Comment