Кластеризация — популярный метод анализа данных путем группировки похожих точек данных в кластеры. Однако определение оптимального количества кластеров является важным шагом в кластерном анализе, который может сильно повлиять на результаты. В этом исследовании мы смоделировали пять различных наборов данных и оценили производительность трех алгоритмов кластеризации, включая k-means, k-medoids и иерархическую кластеризацию, используя три различных метода определения оптимального количества кластеров, включая метод локтя, средний силуэт метод и индекс Дэвиса-Булдина. Результаты этого исследования могут дать ценную информацию о производительности алгоритмов кластеризации и методов определения оптимального количества кластеров.

Наборы данных были сгенерированы с помощью функции rmvnorm из пакета mvtnorm на языке программирования R через пользовательскую функцию.

Мы создали 5 наборов данных по разным сценариям на языке программирования R и проверили, как алгоритмы кластеризации и методы определения оптимального количества кластеров работают в каждом сценарии.

Мы создали базу данных с помощью MongoDB и извлекли наборы данных из базы данных.

Сценарий 1 — количество элементов кластера (250) равно, расстояние между центрами кластеров далеко

Сценарий 2 — Количество элементов кластера (250) равно, расстояние между центрами кластеров близко

Сценарий 3 — Количество элементов кластера (50, 350, 350) разное, расстояние между центрами кластеров большое

Сценарий 4 — Количество элементов кластера (50, 350, 350) разное, расстояние между центрами кластеров близкое

Сценарий 5 — Количество элементов кластера (25, 375, 350) разное, расстояние между центрами кластеров близкое

Сценарий 1

import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(14,10)) 
sns.scatterplot(data=df, x="X1", y="X2", hue="class")

K-средних для сценария 1

Первый сценарий, состоящий из 3 кластеров, многомерный нормальный набор данных с смоделированным распределением.
Каждый кластер имеет равное количество (250) кластеров с элементами.
Разница между центрами кластеров чрезмерна.
Дисперсия кластеров слишком.

Когда мы изучаем точечную диаграмму нашего набора данных, мы видим, что кластеры сильно отличаются друг от друга. Хотя разница между граничными элементами невелика, мы ожидаем, что алгоритмы кластеризации успешно справятся с этим различием.

Поскольку разница между масштабами нашего набора данных велика, необходима стандартизация, чтобы алгоритмы кластеризации, основанные на расчете расстояния, работали более успешно. По этой причине мы стандартизируем наш набор данных.

from sklearn.preprocessing import StandardScaler

dfclust = StandardScaler().fit_transform(dfclust)

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
 kmeans = KMeans(n_clusters=num_clusters, n_init=25)
 kmeans.fit(dfclust)
 Sum_of_squared_distances.append(kmeans.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

silhouette_avg = []
K = range(2,10)
for num_clusters in K:
 
 # initialise kmeans
 km = KMeans(n_clusters=num_clusters, n_init=25)
 km.fit(dfclust)
 cluster_labels = km.labels_
 
 # silhouette score
 silhouette_avg.append(silhouette_score(dfclust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:
 kmeans = KMeans(n_clusters=num_clusters, n_init=25)
 kmeans.fit(dfclust)
 cluster_labels = kmeans.fit_predict(dfclust)
 
 db.append(davies_bouldin_score(dfclust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты всех определений оптимальных номеров кластеров для алгоритма k-средних.

Все методы правильно определили количество кластеров.

Коды кластеризации K-средних и график для сценария-1

from scipy.spatial import ConvexHull
from matplotlib.colors import to_rgba
sns.set_style("whitegrid")
data = dfclust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=dfclust, x=xcol, y=ycol, hue=kmeans3.labels_, style=kmeans3.labels_, col=kmeans3.labels_, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in dfclust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=kmeans3.labels_)
g.set_axis_labels(xcol, ycol)

plt.show()

Мы также видим предсказанное нами разложение на графике кластеризации. Кажется, это очень удачная декомпозиция

К-медоиды для сценария 1

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
 kmedoid = KMedoids(n_clusters=num_clusters)
 kmedoid.fit(dfclust)
 Sum_of_squared_distances.append(kmedoid.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
silhouette_avg = []
for num_clusters in K:
 
 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(dfclust)
 cluster_labels = kmedoids.labels_
 
 silhouette_avg.append(silhouette_score(dfclust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Дэвис Булдин

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:
 kmeans = KMeans(n_clusters=num_clusters, n_init=25)
 kmeans.fit(dfclust)
 cluster_labels = kmeans.fit_predict(dfclust)
 
 db.append(davies_bouldin_score(dfclust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты всех алгоритмов определения оптимальных номеров кластеров для k-medoids.

Все методы правильно определили количество кластеров

Коды кластеризации K-medoids и график для сценария-1

sns.set_style("whitegrid")
data = dfclust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=dfclust, x=xcol, y=ycol, hue=kmedoids3.labels_, style=kmedoids3.labels_, col=kmedoids3.labels_, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in dfclust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=kmedoids3.labels_)
g.set_axis_labels(xcol, ycol)

plt.show()

Мы также видим предсказанное нами разделение на графике кластеризации. Кажется, он также успешен, но не похож на k-средние, потому что кластеры 0 и 1 кажутся ближе, чем график k-средних.

Иерархическая кластеризация

В иерархическом алгоритме, в отличие от других, мы будем смотреть на собственную дендрограмму, где мы используем метод единого определения номера кластера.

Когда мы изучаем дендограмму, 3 кажется наиболее подходящим количеством кластеров. По этой причине мы продолжаем с 3 кластерами

График иерархической кластеризации

Когда мы изучаем график кластеризации, мы замечаем, что по сравнению с kmeans расстояние между кластером 1, показанным темно-синим, и кластером 0, показанным голубым, меньше. Он очень похож на k-medoids.

Сценарий 2

import csv

with open('results2.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(results2[0].keys()) 

with open('results2.csv', 'a', newline='') as file:
    writer = csv.writer(file)
    for item in results2:
        writer.writerow(item.values())


plt.figure(figsize=(14,10)) 
sns.scatterplot(data=df2, x="X1", y="X2", hue="class")

Когда мы сравним его с первым сценарием, мы сможем увидеть разницу более четко. В первом сценарии между кластерами не было перекрытия, тогда как в этом сценарии перекрытия больше. Алгоритмы кластеризации будут иметь больше трудностей в этом сценарии, и их успех уменьшится больше.

Поскольку разница между масштабами нашего набора данных велика, необходима стандартизация, чтобы алгоритмы кластеризации, основанные на расчете расстояния, работали более успешно. По этой причине мы стандартизируем наш набор данных.

K-средних для сценария 2

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
 kmeans = KMeans(n_clusters=num_clusters, n_init=25)
 kmeans.fit(df2clust)
 Sum_of_squared_distances.append(kmeans.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

silhouette_avg = []
K = range(2,10)
for num_clusters in K:

 km = KMeans(n_clusters=num_clusters, n_init=25)
 km.fit(df2clust)
 cluster_labels = km.labels_

 silhouette_avg.append(silhouette_score(df2clust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса-Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:
 kmeans = KMeans(n_clusters=num_clusters, n_init=25)
 kmeans.fit(df2clust)
 cluster_labels = kmeans.fit_predict(df2clust)
 
 db.append(davies_bouldin_score(df2clust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты определения оптимальных номеров кластеров для алгоритма k-средних. Сценарий 2.

ГРАФИК КЛАСТЕРИЗАЦИИ K-СРЕДНИХ ДЛЯ СЦЕНАРИЯ 2

Когда мы изучили точечную диаграмму набора данных, мы заметили, что кластеры перекрываются. Однако, когда мы рассматриваем график кластеров, образовавшихся в результате кластеризации, мы не видим такого совпадения.

К-медоиды для сценария 2

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
 kmedoid = KMedoids(n_clusters=num_clusters)
 kmedoid.fit(df2clust)
 Sum_of_squared_distances.append(kmedoid.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
silhouette_avg = []
for num_clusters in K:
 
 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(df2clust)
 cluster_labels = kmedoids.labels_
 
 silhouette_avg.append(silhouette_score(df2clust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:
 
 # initialise kmeans
 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(df2clust)
 cluster_labels = kmedoids.fit_predict(df2clust)
 
 # silhouette score
 db.append(davies_bouldin_score(df2clust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты всех определений оптимальных номеров кластеров для алгоритма k-medoids Сценарий 2

Только локтевой метод правильно определил количество кластеров.

Коды кластеризации K-medoids и график для Сценария-2

sns.set_style("whitegrid")
data = df2clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df2clust, x=xcol, y=ycol, hue=kmedoids3.labels_, style=kmedoids3.labels_, col=kmedoids3.labels_, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df2clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=kmedoids3.labels_)
g.set_axis_labels(xcol, ycol)

plt.show()

С K-средними создается очень похожий график.

Иерархическая кластеризация

import scipy.cluster.hierarchy as sch
plt.figure(figsize = (16 ,8))

dendrogram = sch.dendrogram(sch.linkage(df2clust, method  = "ward"))

plt.title("Dendrogram")
plt.show()

sns.set_style("whitegrid")
data = df2clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df2clust, x=xcol, y=ycol, hue=ward3, style=ward3, col=ward3, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df2clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=ward3)
g.set_axis_labels(xcol, ycol)

plt.show()

В отличие от kmeans и kmedoids, мы видим, что в графе кластеров есть перекрытия, возникающие в результате иерархической кластеризации. Учитывая, что в наборе данных есть перекрытие, можно считать, что он, возможно, выполнил более успешную кластеризацию, чем другие методы.

Сценарий 3

result3 = client['ist']['case3'].aggregate([])
results3 = list(result3)
import csv

with open('results3.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(results3[0].keys()) 

with open('results3.csv', 'a', newline='') as file:
    writer = csv.writer(file)
    for item in results3:
        writer.writerow(item.values())

df3 = pd.read_csv('results3.csv')

plt.figure(figsize=(14,10)) 
sns.scatterplot(data=df3, x="X1", y="X2", hue="class")

Третий сценарий.
Моделируется набор данных из 3 кластеров с многомерным нормальным распределением.
Каждый кластер имеет разное количество элементов кластера (50, 350, 350).
Разница между расстояниями центров кластеров велико.
Отличия кластеров высоки

Единственная разница в этом сценарии, очень похожем на первый сценарий, заключается в том, что количество элементов в кластере 1, обозначенном цветом внутри дыни, меньше, чем в двух других кластерах. В то время как во втором сценарии между кластерами есть перекрытие, в этом сценарии, как и в первом сценарии, перекрытия нет. Мы увидим, смогут ли алгоритмы кластеризации обнаружить кластер 1 в этом сценарии, в котором меньше элементов по сравнению с двумя другими кластерами.

K-средних для сценария 3

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
    kmeans = KMeans(n_clusters=num_clusters, n_init=25)
    kmeans.fit(df3clust)
    Sum_of_squared_distances.append(kmeans.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

silhouette_avg = []
K = range(2,10)
for num_clusters in K:

 km = KMeans(n_clusters=num_clusters, n_init=25)
 km.fit(df3clust)
 cluster_labels = km.labels_

 silhouette_avg.append(silhouette_score(df3clust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:
 kmeans = KMeans(n_clusters=num_clusters, n_init=25)
 kmeans.fit(df3clust)
 cluster_labels = kmeans.fit_predict(df3clust)
 
 db.append(davies_bouldin_score(df3clust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты определения оптимальных номеров кластеров для алгоритма k-средних. Сценарий 3.

Все методы правильно определили количество кластеров. Сценарий 3 показывает, что по мере удаления центров кластеров друг от друга методы с большей вероятностью правильно оценивают количество кластеров.

ГРАФИК КЛАСТЕРИЗАЦИИ K-СРЕДНИХ ДЛЯ СЦЕНАРИЯ 3

sns.set_style("whitegrid")
data = df3clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df3clust, x=xcol, y=ycol, hue=kmeans3.labels_, style=kmeans3.labels_, col=kmeans3.labels_, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df3clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=kmeans3.labels_)
g.set_axis_labels(xcol, ycol)

plt.show()

Кажется, это успешная кластеризация.

К-медоиды для сценария 3

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
 kmedoid = KMedoids(n_clusters=num_clusters)
 kmedoid.fit(df3clust)
 Sum_of_squared_distances.append(kmedoid.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
silhouette_avg = []
for num_clusters in K:
 
 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(df3clust)
 cluster_labels = kmedoids.labels_
 
 silhouette_avg.append(silhouette_score(df3clust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:

 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(df3clust)
 cluster_labels = kmedoids.fit_predict(df3clust)

 db.append(davies_bouldin_score(df3clust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты всех определений оптимальных номеров кластеров для алгоритма k-medoids Сценарий 3

Ни один из методов не мог точно определить количество кластеров

КЛАСТЕРНЫЙ УЧАСТОК K-MEDOIDS ДЛЯ СЦЕНАРИЯ 3

Мы видим очень странное скопление. Алгоритм выполнил кластеризацию путем слияния небольшого количества кластеров с большим количеством кластеров. Мы можем сравнить последствия этого странного результата с показателями кластерной валидности.

Иерархическая кластеризация

import scipy.cluster.hierarchy as sch
plt.figure(figsize = (16 ,8))

dendrogram = sch.dendrogram(sch.linkage(df3clust, method  = "ward"))

plt.title("Dendrogram")
plt.show()

sns.set_style("whitegrid")
data = df3clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df3clust, x=xcol, y=ycol, hue=ward3, style=ward3, col=ward3, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df3clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=ward3)
g.set_axis_labels(xcol, ycol)

plt.show()

Так же, как k-means, наблюдается успешная кластеризация

Сценарий 4

result4 = client['ist']['case4'].aggregate([])
results4 = list(result4)
import csv

with open('results4.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(results4[0].keys()) 

with open('results4.csv', 'a', newline='') as file:
    writer = csv.writer(file)
    for item in results4:
        writer.writerow(item.values())

df4 = pd.read_csv('results4.csv')

plt.figure(figsize=(14,10)) 
sns.scatterplot(data=df4, x="X1", y="X2", hue="class")

Четвертый сценарий;

Моделируется набор данных из 3 кластеров с многомерным нормальным распределением.
Каждый кластер имеет разное количество элементов кластера (50, 350, 350).
Разница между расстояниями между центрами кластеров невелика.
Дисперсия кластеров высока.

По сравнению с третьим сценарием заметно перекрытие между кластерами, так как центры кластеров расположены ближе друг к другу. Обнаружение кластера 1, который имеет небольшое количество кластеров и обозначается цветом внутренней части дыни, может быть сложным для алгоритмов кластеризации и методов подсчета кластеров.

K-средних для сценария 4

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
    kmeans = KMeans(n_clusters=num_clusters, n_init=25)
    kmeans.fit(df4clust)
    Sum_of_squared_distances.append(kmeans.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

silhouette_avg = []
K = range(2,10)
for num_clusters in K:

 km = KMeans(n_clusters=num_clusters, n_init=25)
 km.fit(df4clust)
 cluster_labels = km.labels_

 silhouette_avg.append(silhouette_score(df4clust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:
 kmeans = KMeans(n_clusters=num_clusters, n_init=25)
 kmeans.fit(df4clust)
 cluster_labels = kmeans.fit_predict(df4clust)
 
 db.append(davies_bouldin_score(df4clust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты определения оптимальных номеров кластеров для алгоритма k-средних. Сценарий 4.

Ни один из методов не мог точно определить количество кластеров

ГРАФИК КЛАСТЕРИЗАЦИИ K-СРЕДНИХ ДЛЯ СЦЕНАРИЯ 4

sns.set_style("whitegrid")
data = df4clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df4clust, x=xcol, y=ycol, hue=kmeans3.labels_, style=kmeans3.labels_, col=kmeans3.labels_, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df4clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=kmeans3.labels_)
g.set_axis_labels(xcol, ycol)

plt.show()

Мы видим, что кластер 0, показанный голубым цветом, в котором меньше кластеров, содержит больше элементов, чем должно.

В связи с этим можно предположить, что кластеризация не удалась.

К-медоиды для сценария 4

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
 kmedoid = KMedoids(n_clusters=num_clusters)
 kmedoid.fit(df4clust)
 Sum_of_squared_distances.append(kmedoid.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

silhouette_avg = []
for num_clusters in K:
 
 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(df4clust)
 cluster_labels = kmedoids.labels_
 
 silhouette_avg.append(silhouette_score(df4clust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:

 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(df4clust)
 cluster_labels = kmedoids.fit_predict(df4clust)

 db.append(davies_bouldin_score(df4clust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты всех определений оптимальных номеров кластеров для алгоритма k-medoids. Сценарий 4.

Ни один из методов не мог точно определить количество кластеров.

КЛАСТЕРНЫЙ УЧАСТОК K-MEDOIDS ДЛЯ СЦЕНАРИЯ 4

sns.set_style("whitegrid")
data = df4clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df4clust, x=xcol, y=ycol, hue=kmedoids3.labels_, style=kmedoids3.labels_, col=kmedoids3.labels_, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df4clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=kmedoids3.labels_)
g.set_axis_labels(xcol, ycol)

Когда мы смотрим на кластерный график, мы видим, что кластер 0, показанный голубым цветом, должен иметь меньше элементов, а кластер 2, показанный зеленым цветом, должен иметь меньше элементов. Опять же, можно сказать, что это неудачная кластеризация.

Иерархическая кластеризация

import scipy.cluster.hierarchy as sch
plt.figure(figsize = (16 ,8))

dendrogram = sch.dendrogram(sch.linkage(df4clust, method  = "ward"))

plt.title("Dendrogram")
plt.show()

sns.set_style("whitegrid")
data = df4clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df4clust, x=xcol, y=ycol, hue=ward3, style=ward3, col=ward3, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df4clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=ward3)
g.set_axis_labels(xcol, ycol)

plt.show()

По сравнению с графиками kmeans и kmedoids кажется, что алгоритм иерархической кластеризации может быть более успешным.

Сценарий 5

Пятый сценарий;

Моделируется набор данных из 3 кластеров с многомерным нормальным распределением.
Каждый кластер имеет разное количество элементов кластера (25, 375, 350).
Разница между расстояниями между центрами кластеров невелика.
Дисперсия кластеров высока.

result5 = client['ist']['case5'].aggregate([])
results5 = list(result5)
import csv

with open('results5.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(results5[0].keys()) 

with open('results5.csv', 'a', newline='') as file:
    writer = csv.writer(file)
    for item in results5:
        writer.writerow(item.values())

df5 = pd.read_csv('results5.csv')

plt.figure(figsize=(14,10)) 
sns.scatterplot(data=df5, x="X1", y="X2", hue="class")

По сравнению с четвертым сценарием центры кластеров расположены ближе друг к другу, что указывает на перекрытие между кластерами. Обнаружение кластера 1, который имеет еще меньше кластеров по сравнению с четвертым сценарием и обозначается цветом внутренней части дыни, может быть сложной задачей для алгоритмов кластеризации и методов подсчета кластеров.

Пятый сценарий;

Моделируется набор данных из 3 кластеров с многомерным нормальным распределением.
Каждый кластер имеет разное количество элементов кластера (25, 375, 350).
Разница между расстояниями между центрами кластеров невелика.
Дисперсия кластеров высока.

K-средних для сценария 5

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
    kmeans = KMeans(n_clusters=num_clusters, n_init=25)
    kmeans.fit(df5clust)
    Sum_of_squared_distances.append(kmeans.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

silhouette_avg = []
K = range(2,10)
for num_clusters in K:

 km = KMeans(n_clusters=num_clusters, n_init=25)
 km.fit(df5clust)
 cluster_labels = km.labels_

 silhouette_avg.append(silhouette_score(df5clust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:
 kmeans = KMeans(n_clusters=num_clusters, n_init=25)
 kmeans.fit(df5clust)
 cluster_labels = kmeans.fit_predict(df5clust)
 
 db.append(davies_bouldin_score(df5clust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты всех определений оптимальных номеров кластеров для алгоритма k-средних. Сценарий 5.

Ни один из методов не мог точно определить количество кластеров.

ГРАФИК КЛАСТЕРИЗАЦИИ K-СРЕДНИХ ДЛЯ СЦЕНАРИЯ 5

sns.set_style("whitegrid")
data = df5clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df5clust, x=xcol, y=ycol, hue=kmeans3.labels_, style=kmeans3.labels_, col=kmeans3.labels_, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df5clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=kmeans3.labels_)
g.set_axis_labels(xcol, ycol)

plt.show()

Как и в предыдущем результате, мы видим, что кластер 1, в котором должно быть меньше кластеров, содержит меньше элементов.

В связи с этим можно предположить, что кластеризация не удалась.

K-MEDOIDS для сценария 5

Метод локтя

Sum_of_squared_distances = []
K = range(1,10)
for num_clusters in K :
 kmedoid = KMedoids(n_clusters=num_clusters)
 kmedoid.fit(df5clust)
 Sum_of_squared_distances.append(kmedoid.inertia_)
plt.figure(figsize=(10,7))
plt.plot(K,Sum_of_squared_distances, 'bx-')
plt.xlabel('cluster number') 
plt.ylabel('Total Within Cluster Sum of Squares') 
plt.title('Elbow Plot')
plt.show()

Метод среднего силуэта

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
silhouette_avg = []
for num_clusters in K:
 
 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(df5clust)
 cluster_labels = kmedoids.labels_
 
 silhouette_avg.append(silhouette_score(df5clust, cluster_labels))

plt.figure(figsize=(10,7))
plt.plot(K,silhouette_avg,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Silhouette score') 
plt.title('Average Silhouette Plot')
plt.show()

Метод Дэвиса Булдина

K = [2, 3, 4, 5, 6, 7, 8, 9, 10]
db = []
for num_clusters in K:

 kmedoids = KMedoids(n_clusters=num_clusters)
 kmedoids.fit(df5clust)
 cluster_labels = kmedoids.fit_predict(df5clust)

 db.append(davies_bouldin_score(df5clust, cluster_labels))


plt.figure(figsize=(10,7))
plt.plot(K,db,'bx-')
plt.xlabel('cluster number k') 
plt.ylabel('Davies Bouldin score') 
plt.title('Davies Bouldin Plot')
plt.show()

Вот графические результаты всех определений оптимальных номеров кластеров для алгоритма k-medoids. Сценарий 5.

Как видите, ни один из методов не может предсказать реальный номер кластера в сценарии 5.

КЛАСТЕРНЫЙ УЧАСТОК K-MEDOIDS ДЛЯ СЦЕНАРИЯ 5

sns.set_style("whitegrid")
data = df5clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df5clust, x=xcol, y=ycol, hue=kmedoids3.labels_, style=kmedoids3.labels_, col=kmedoids3.labels_, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df5clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=kmedoids3.labels_)
g.set_axis_labels(xcol, ycol)

Когда мы смотрим на кластерный график, мы видим, что в то время как кластер 2, показанный зеленым цветом, должен иметь меньше элементов, напротив, кластер o, показанный голубым цветом, имеет меньше элементов.

Опять же, можно сказать, что это неудачная кластеризация.

Иерархическая кластеризация

import scipy.cluster.hierarchy as sch
plt.figure(figsize = (16 ,8))

dendrogram = sch.dendrogram(sch.linkage(df5clust, method  = "ward"))

plt.title("Dendrogram")
plt.show()

Первая неудача для иерархической дендрограммы - предполагаемый номер кластера 2 или 4.

sns.set_style("whitegrid")
data = df5clust
xcol = "X1"
ycol = "X2"
hues = [0,1,2]
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
plt.figure(figsize=(15,10))
g = sns.relplot(data=df5clust, x=xcol, y=ycol, hue=ward3, style=ward3, col=ward3, palette=palette, kind="scatter")
def overlay_cv_hull_dataframe(x, y, color, data, hue):
    for hue_val, group in df5clust.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue=ward3)
g.set_axis_labels(xcol, ycol)

Так же, как kmeans и kmedoids, можно утверждать, что этот алгоритм неправильно разделяет кластеры. Метрики достоверности кластера могут позволить нам сделать более надежную интерпретацию.

Выбор лучшего алгоритма

results = {"Senaryo": cases,
           "Algoritma": clusteringalgorithm, 
           "AverageSilhouette": siscores,
           "CalinskiHarabasz": chscores,
           "AccuracyRate": accuracyrates,
           } 
  
results = pd.DataFrame(results)
results

for algorithm in results['Algoritma'].unique():
    # Algoritmayı kullanan senaryoları filtrele
    algorithm_data = results[results['Algoritma'] == algorithm]
    # Senaryo adlarına göre sırala
    sorted_data = algorithm_data.sort_values('Senaryo')
    # Grafik oluştur
    plt.bar(sorted_data['Senaryo'], sorted_data['CalinskiHarabasz'], label=algorithm)
    plt.xlabel('Senaryo')
    plt.ylabel('Calinski Harabasz')
    plt.title(f'Algoritma: {algorithm}')
    plt.legend()
    plt.show()

Все методы были успешными в сценарии, когда количество элементов кластера было равно, а расстояние между центрами кластеров было большим.
В сценариях, где расстояние между центрами кластеров было больше, k-медоиды не работали.
Все методы не удалось, когда был дисбаланс между количеством элементов кластера.
Только в сценарии, где центры кластера были далеко друг от друга, метод k-средних и иерархический были успешными.

В сценариях, где разница между центрами кластеров была высокой, метод k-средних и иерархический были успешными.
В сценариях, где разница между центрами кластеров была небольшой, ни один из методов не был успешным.
В сценариях не удались все методы. где был дисбаланс между количеством элементов кластера.
Успех k-medoids уменьшался по мере усложнения сценария.

В сценарии, где разница между центрами кластеров была высокой, а элементы кластера были равными, все методы дали успешные результаты.
В сценариях, где разница между центрами кластеров была небольшой, ни один из методов не был успешным.
Все методы не сработали в сценариях, где был дисбаланс между количеством элементов кластера.
Успех k-medoids снижался по мере усложнения сценария.

Подводя итог в целом, можно сказать, что если все алгоритмы и методы работают при равенстве элементов кластера и удалении центров, то можно сказать, что алгоритмы кластеризации работают не совсем корректно в противоположных и разных ситуациях. Мы надеемся, что эти методы и алгоритмы, которые мы создали и протестировали в разных сценариях, мы надеемся, что кто-то, кому они пригодятся, увидит их заранее и предпочтет выполнять работу, согласно которой он работает на них. Я хотел бы выразить благодарность моим друзьям по группе Sencer ÇAKIR и Huseyin YILDIZ за их вклад.

И, конечно же, я хотел бы выразить глубокую благодарность Стамбульской академии наук о данных, частью которой мы являемся, за то, что они вдохновили нас на этот проект. Спасибо за прочтение