can you implement in a didactical similar way the DBSCAN algorithm? follow the structure of the kmedoids.py and add the plot in the clustering.py file

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()
"""

FILE: kmedoids.py

"""
#
# from sklearn.metrics.pairwise import euclidean_distances
# import numpy as np
#
#
# class KMedoids(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_ = []
#         self.indices = []
#
#     def fit(self, X):
#         rint = self.rstate.randint
#         self.indices = [rint(X.shape[0])]
#         for _ in range(self.n_clusters - 1):
#             i = rint(X.shape[0])
#             while i in self.indices:
#                 i = rint(X.shape[0])
#             self.indices.append(i)
#         self.cluster_centers_ = X[self.indices, :]
#
#         self.y_pred = np.argmin(self.dist(X, self.cluster_centers_), axis=1)
#         cost, _ = self.compute_cost(X, self.indices)
#         new_cost = cost
#         new_y_pred = self.y_pred.copy()
#         new_indices = self.indices[:]
#         initial = True
#         while (new_cost < cost) | initial:
#             initial = False
#             cost = new_cost
#             self.y_pred = new_y_pred
#             self.indices = new_indices
#             for k in range(self.n_clusters):
#                 for r in [i for i, x in enumerate(new_y_pred == k) if x]:
#                     if r not in self.indices:
#                         indices_temp = self.indices[:]
#                         indices_temp[k] = r
#                         new_cost_temp, y_pred_temp = self.compute_cost(X, indices_temp)
#                         if new_cost_temp < new_cost:
#                             new_cost = new_cost_temp
#                             new_y_pred = y_pred_temp
#                             new_indices = indices_temp
#
#         self.cluster_centers_ = X[self.indices, :]
#
#     def compute_cost(self, X, indices):
#         y_pred = np.argmin(self.dist(X, X[indices,:]), axis=1)
#         return np.sum([np.sum(self.dist(X[y_pred == i], X[[indices[i]], :])) for i in set(y_pred)]), y_pred
#
#     def predict(self, X):
#         return np.argmin(self.dist(X, self.cluster_centers_), axis=1)
#
#



from sklearn.metrics.pairwise import euclidean_distances
import numpy as np


class KMedoids(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_ = []
        self.indices = []

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

        self.y_pred = np.argmin(self.dist(X, self.cluster_centers_), axis=1)
        cost, _ = self.compute_cost(X, self.indices)
        new_cost = cost
        new_y_pred = self.y_pred.copy()
        new_indices = self.indices[:]
        initial = True
        while (new_cost < cost) | initial:
            initial = False
            cost = new_cost
            self.y_pred = new_y_pred
            self.indices = new_indices
            for k in range(self.n_clusters):
                for r in [i for i, x in enumerate(new_y_pred == k) if x]:
                    if r not in self.indices:
                        indices_temp = self.indices[:]
                        indices_temp[k] = r
                        new_cost_temp, y_pred_temp = self.compute_cost(X, indices_temp)
                        if new_cost_temp < new_cost:
                            new_cost = new_cost_temp
                            new_y_pred = y_pred_temp
                            new_indices = indices_temp

        self.cluster_centers_ = X[self.indices, :]

    def compute_cost(self, X, indices):
        y_pred = np.argmin(self.dist(X, X[indices,:]), axis=1)
        return np.sum([np.sum(self.dist(X[y_pred == i], X[[indices[i]], :])) for i in set(y_pred)]), y_pred

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


"""