Group Part I: Machine Learning

Q4: Clustering Analysis (Any one) A. Implement K-Means clustering on the Iris CSV dataset. Determine the number of clusters using the elbow method. Dataset link: https://www.kaggle.com/datasets/uciml/iris B. Implement the K-Medoid algorithm on a credit card dataset. Determine the number of clusters using the silhouette method. Dataset link: https://www.kaggle.com/datasets/arjunbhasin2013/ccdata

Clustering Analysis (Any one)

Solution and implementation for Q4 from Machine Learning (ml).

4A_kmeans_iris_elbow.ipynb Download
Code Cell [In]
# Q4A: K-Means clustering on Iris.csv; choose k with the elbow method.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
Code Cell [In]
# Load the locally saved Iris dataset. Species is kept only for interpretation.
iris = pd.read_csv("Iris.csv")
X = iris.drop(columns=["Id", "Species"])
X_scaled = StandardScaler().fit_transform(X)
print("Dataset shape:", iris.shape)
print("Features:", X.columns.tolist())
iris.head()
Code Cell [In]
# Fit K-Means for several k values and record within-cluster inertia.
k_values = np.arange(1, 11)
inertias = []
for k in k_values:
    model = KMeans(n_clusters=int(k), n_init=10, random_state=42)
    model.fit(X_scaled)
    inertias.append(model.inertia_)
print(pd.DataFrame({"k": k_values, "inertia": inertias}).to_string(index=False))
Code Cell [In]
# The elbow is the point farthest from the line joining the endpoints.
# Inspect the plot too: this geometric rule is a guide, not a guarantee.
scaled_inertia = (np.array(inertias) - min(inertias)) / (max(inertias) - min(inertias))
scaled_k = (k_values - k_values[0]) / (k_values[-1] - k_values[0])
distance_to_line = np.abs(scaled_inertia + scaled_k - 1) / np.sqrt(2)
best_k = int(k_values[np.argmax(distance_to_line)])
plt.figure(figsize=(7, 4))
plt.plot(k_values, inertias, marker="o")
plt.axvline(best_k, color="crimson", linestyle="--", label=f"Elbow: k={best_k}")
plt.xlabel("Number of clusters (k)")
plt.ylabel("Inertia")
plt.title("K-Means elbow method on Iris")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
print("Selected number of clusters:", best_k)
Code Cell [In]
# Cluster using the selected k and inspect the result.
final_model = KMeans(n_clusters=best_k, n_init=10, random_state=42)
cluster_labels = final_model.fit_predict(X_scaled)
print("Cluster sizes:", pd.Series(cluster_labels).value_counts().sort_index().to_dict())
print(pd.crosstab(iris["Species"], cluster_labels, rownames=["Species"], colnames=["Cluster"]))
Code Cell [In]
# PCA is used only for a two-dimensional visualization of the clusters.
points_2d = PCA(n_components=2).fit_transform(X_scaled)
plt.figure(figsize=(7, 5))
plt.scatter(points_2d[:, 0], points_2d[:, 1], c=cluster_labels, cmap="viridis", s=35)
plt.xlabel("Principal component 1")
plt.ylabel("Principal component 2")
plt.title(f"Iris K-Means clusters (k={best_k})")
plt.show()
4B_kmedoids_credit_card_silhouette.ipynb Download
Code Cell [In]
# Q4B: K-Medoids on the credit card dataset; choose k by silhouette score.
# Source: https://www.kaggle.com/datasets/arjunbhasin2013/ccdata
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.impute import SimpleImputer
from sklearn.metrics import pairwise_distances, silhouette_score
from sklearn.preprocessing import StandardScaler
Code Cell [In]
# Read the downloaded CSV. Sample reproducibly because exact pairwise
# distances over all 8,950 customers would use substantial memory.
credit = pd.read_csv("CC GENERAL.csv")
sample = credit.sample(n=800, random_state=42).reset_index(drop=True)
numeric = sample.drop(columns=["CUST_ID"])
X = SimpleImputer(strategy="median").fit_transform(numeric)
X_scaled = StandardScaler().fit_transform(X)
distances = pairwise_distances(X_scaled, metric="euclidean")
print("Full dataset:", credit.shape, "Clustering sample:", sample.shape)
print("Missing values in full dataset:", credit.isna().sum().sum())
Code Cell [In]
# Alternating K-Medoids: each medoid is an actual customer record.
# Start with far-apart records, assign to nearest medoid, then minimize
# the sum of within-cluster distances. This uses no extra package.
def fit_kmedoids(distance_matrix, n_clusters, max_iter=30):
    n = len(distance_matrix)
    medoids = [int(np.argmin(distance_matrix.sum(axis=1)))]
    while len(medoids) < n_clusters:
        nearest = distance_matrix[:, medoids].min(axis=1)
        nearest[medoids] = -1
        medoids.append(int(np.argmax(nearest)))
    for _ in range(max_iter):
        labels = np.argmin(distance_matrix[:, medoids], axis=1)
        new_medoids = []
        for cluster in range(n_clusters):
            members = np.flatnonzero(labels == cluster)
            if len(members) == 0:
                raise ValueError("Empty cluster; try a smaller k")
            totals = distance_matrix[np.ix_(members, members)].sum(axis=1)
            new_medoids.append(int(members[np.argmin(totals)]))
        if new_medoids == medoids:
            break
        medoids = new_medoids
    labels = np.argmin(distance_matrix[:, medoids], axis=1)
    return labels, medoids
Code Cell [In]
# The highest average silhouette score determines k for this sample.
k_values = range(2, 7)
scores = []
for k in k_values:
    labels, _ = fit_kmedoids(distances, k)
    score = silhouette_score(distances, labels, metric="precomputed")
    scores.append(score)
    print(f"k={k}: silhouette={score:.3f}")
best_k = list(k_values)[int(np.argmax(scores))]
plt.figure(figsize=(7, 4))
plt.plot(list(k_values), scores, marker="o")
plt.axvline(best_k, color="crimson", linestyle="--", label=f"Best k={best_k}")
plt.xlabel("Number of clusters (k)")
plt.ylabel("Average silhouette score")
plt.title("Credit card K-Medoids silhouette selection")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
Code Cell [In]
# Show the selected medoids and a two-dimensional view of the clusters.
labels, medoids = fit_kmedoids(distances, best_k)
print("Cluster sizes:", pd.Series(labels).value_counts().sort_index().to_dict())
print("Medoid customer IDs:", sample.loc[medoids, "CUST_ID"].tolist())
points_2d = PCA(n_components=2).fit_transform(X_scaled)
plt.figure(figsize=(7, 5))
plt.scatter(points_2d[:, 0], points_2d[:, 1], c=labels, cmap="tab10", s=15, alpha=0.7)
plt.scatter(points_2d[medoids, 0], points_2d[medoids, 1],
            c="black", marker="X", s=140, label="Medoids")
plt.xlabel("Principal component 1")
plt.ylabel("Principal component 2")
plt.title(f"Credit card customer clusters (k={best_k})")
plt.legend()
plt.show()

Other Questions in Machine Learning

See All Available Questions
Download