Given the following kmeans.py and clustering.py files. I want to show didactically the Elbow method in order to find the best K. can you provide in two steps: 1. all the skeleton and suggestions in order to implement the elbow method (the students will have to fill it. 2. a simple and intuitive implementation of it. can you also add plots?. itmust be very clear and easy to be implemented. strictly follow the structure of my files. use the total within-clusters sum of squares as metric

FILE kmeans.py
from sklearn.metrics.pairwise import euclidean_distances
import numpy as np


class KMeans(object):
    def __init__(self, n_clusters=2, dist=euclidean_distances, random_state=42):
        self.n_clusters = n_clusters
        self.dist = dist
        self.rstate = np.random.RandomState(random_state)
        self.cluster_centers_ = []

    def fit(self, X):
        rint = self.rstate.randint
        initial_indices = [rint(X.shape[0])]
        for _ in range(self.n_clusters - 1):
            i = rint(X.shape[0])
            while i in initial_indices:
                i = rint(X.shape[0])
            initial_indices.append(i)
        self.cluster_centers_ = X[initial_indices, :]

        continue_condition = True

        while continue_condition:
            old_centroids = self.cluster_centers_.copy()
            self.y_pred = np.argmin(self.dist(X, self.cluster_centers_), axis=1)
            for i in set(self.y_pred):
                self.cluster_centers_[i] = np.mean(X[self.y_pred == i], axis=0)

            if (old_centroids == self.cluster_centers_).all():
                continue_condition = False

    def predict(self, X):
        return np.argmin(self.dist(X, self.cluster_centers_), axis=1)

FILE: clustering.py


import numpy as np
# from sklearn.cluster import KMeans
# from sklearn_extra.cluster import KMedoids
from sklearn import datasets
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as plt
from kmeans import KMeans
from kmedoids import KMedoids


proc_data, y_true = datasets.make_blobs(n_samples=500,
                                        n_features=2,
                                        centers=4,
                                        cluster_std=1,
                                        center_box=(-10.0, 10.0),
                                        shuffle=True,
                                        random_state=88)  # For reproducibility


k_s = 4

mean = np.mean(proc_data,  axis=0)
std = np.std(proc_data,  axis=0)
proc_data = (proc_data - mean) / std

# K-Means from scratch
kmeans_obj_2 = KMeans(n_clusters=k_s, random_state=42)
kmeans_obj_2.fit(proc_data)
y_pred = kmeans_obj_2.predict(proc_data)
print(f"Silhouette Coefficient Home K-Means:\t{silhouette_score(proc_data,y_pred)}")

plt.scatter(proc_data[:, 0], proc_data[:, 1], c=y_pred, s=4)
plt.title("K-Means")
plt.show()
#################################################################################################
# K-Medoids from scratch
kmedoids_obj = KMedoids(n_clusters=k_s, random_state=46)
kmedoids_obj.fit(proc_data)
y_pred = kmedoids_obj.predict(proc_data)
print(f"Silhouette Coefficient Home K-Medoids:\t{silhouette_score(proc_data,y_pred)}")

plt.scatter(proc_data[:, 0], proc_data[:, 1], c=y_pred, s=4)
plt.title("K-Medoids")
plt.show()

plt.show()

##################

from sklearn.cluster import DBSCAN  # Import DBSCAN from scikit-learn

# DBSCAN with scikit-learn
# Note: Adjust eps and min_samples as necessary for the dataset
dbscan_obj = DBSCAN(eps=0.3, min_samples=5)
dbscan_obj.fit(proc_data)
y_pred = dbscan_obj.labels_

# Silhouette score requires at least 2 clusters
if len(set(y_pred)) > 1:
    print(f"Silhouette Coefficient scikit-learn DBSCAN:\t{silhouette_score(proc_data, y_pred)}")
else:
    print("Silhouette Coefficient scikit-learn DBSCAN:\tCannot compute silhouette score with only one cluster.")

plt.scatter(proc_data[:, 0], proc_data[:, 1], c=y_pred, s=4, cmap='viridis')
plt.title("DBSCAN (scikit-learn)")
plt.show()


############################################

# Experiment with different eps and min_samples values
eps_values = [0.2, 0.3, 0.5]
min_samples_values = [3, 5, 10]

for eps in eps_values:
    for min_samples in min_samples_values:
        dbscan_obj = DBSCAN(eps=eps, min_samples=min_samples)
        dbscan_obj.fit(proc_data)
        y_pred = dbscan_obj.labels_
        n_clusters = len(set(y_pred)) - (1 if -1 in y_pred else 0)
        print(f"DBSCAN with eps={eps}, min_samples={min_samples}, clusters={n_clusters}")
        if n_clusters > 1:
            score = silhouette_score(proc_data, y_pred)
            print(f"  Silhouette Score: {score}")
        else:
            print("  Silhouette Score: Cannot compute (only one cluster)")
        plt.scatter(proc_data[:, 0], proc_data[:, 1], c=y_pred, s=4, cmap='viridis')
        plt.title(f"DBSCAN (eps={eps}, min_samples={min_samples})")
        plt.show()