В этой статье я сделаю проект на Python о темах обучения без присмотра, которыми я ранее делился теоретической информацией и приложениями с R в сериях. Это будет подробный проект неконтролируемого обучения с набором данных Рак груди, Висконсин из репозитория машинного обучения UCI, который я использовал в статьях, в которых делился теоретической информацией.
Для блокнота Jupyter вы можете увидеть этот репозиторий Github.
Начнем с импорта данных:
import pandas as pd url = "https://raw.githubusercontent.com/ozturkfemre/unsupervisedlearning/main/dataset/wdbc.data" colnames = ["ID", "Diagnosis", "radius", "texture", "perimeter", "area", "smoothness", "compactness", "concavity", "concave.points", "symmetry", "fractal.dimension"] df = pd.read_csv(url, header=None, usecols=[0,1,2,3,4,5,6,7,8,9,10,11]) df.columns = colnames
Поскольку я не использую первые два столбца, а именно ID и Diagnonsis, в кластерном анализе давайте их удалим.
df1 = df.copy() # it is always useful to create a copy of the data. df = df.drop(["ID", "Diagnosis"], axis = 1)
Описательная статистика
df.describe().T
Когда я проверяю описательную статистику набора данных:
- Среднее значение переменной радиуса выше, чем медиана. Это означает, что переменная радиуса наклонена вправо. Когда мы сравниваем значения третьего квантиля и максимального значения, можно сказать, что в переменной могут быть некоторые выбросы. Тем не менее, лучше всего сделать окончательное суждение с помощью анализа блочной диаграммы.
- Среднее значение переменной текстуры выше, чем медиана. Это означает, что переменная текстуры наклонена вправо. Когда мы сравниваем значения третьего квантиля и максимального значения, можно сказать, что в переменной могут быть некоторые выбросы. Тем не менее, лучше всего сделать окончательное суждение с помощью анализа блочной диаграммы.
- Среднее значение переменной периметра выше, чем медиана. Это означает, что переменная периметра наклонена вправо. Когда мы сравниваем как значения третьего квантиля и максимальное значение, так и минимальное и значение первого квантиля, можно сказать, что в переменной могут быть некоторые выбросы. Тем не менее, лучше всего сделать окончательное суждение с помощью анализа блочной диаграммы.
Из описательной статистики легко можно сказать, что значения сильно различаются. Набор данных должен быть масштабирован. Однако, если существует высокая корреляция между парами переменных, к набору данных необходимо применить PCA. Таким образом, мне нужно проверить матрицу корреляции набора данных.
Корреляционный анализ
cm = df.corr() cm
import seaborn as sns import matplotlib.pyplot as plt fig = plt.figure(figsize=(15,10)) fig.patch.set_facecolor("#fbf9f4") fig.set_facecolor("#fbf9f4") sns.color_palette("mako", as_cmap=True) sns.heatmap(cm, annot=True, annot_kws={"size": 12}, cmap="Blues_r", linewidths=2, linecolor='yellow')
Матрица корреляции набора данных показывает, что существует высокая корреляция между парами переменных. Корреляция между радиусом, периметром и площадью больше 0,98. Это слишком много. Корреляция между компактностью, вогнутостью и вогнутостью точек больше 0,83. Фрактальная размерность — единственная переменная с отрицательной корреляцией. Таким образом, PCA необходимо применять к набору данных.
Визуализация данных
fig = plt.figure(figsize = (25,15)) fig.add_subplot(2, 5, 1) sns.boxplot(y=df['radius'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 2) sns.boxplot(y=df['texture'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 3) sns.boxplot(y=df['perimeter'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 4) sns.boxplot(y=df['area'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 5) sns.boxplot(y=df['smoothness'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 6) sns.boxplot(y=df['compactness'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 7) sns.boxplot(y=df['concavity'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 8) sns.boxplot(y=df['concave.points'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 9) sns.boxplot(y=df['symmetry'], color="#feb24c", linewidth=3, notch = True) fig.add_subplot(2, 5, 10) sns.boxplot(y=df['fractal.dimension'], color="#feb24c", linewidth=3, notch = True)
Из графиков видно, что в каждой переменной есть много выбросов. Дисперсия некоторых переменных (радиус, текстура, периметр, вогнутость, точки вогнутости) очень велика. На этом этапе может быть полезно отметить флажки переменных в соответствии с меткой столбца «Диагностика». Это помогло бы мне понять влияние переменных на классы.
fig = plt.figure(figsize = (25,15)) fig.add_subplot(2, 5, 1) sns.boxplot(y=df1['radius'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 2) sns.boxplot(y=df1['texture'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 3) sns.boxplot(y=df1['perimeter'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 4) sns.boxplot(y=df1['area'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 5) sns.boxplot(y=df1['smoothness'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 6) sns.boxplot(y=df1['compactness'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 7) sns.boxplot(y=df1['concavity'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 8) sns.boxplot(y=df1['concave.points'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 9) sns.boxplot(y=df1['symmetry'], x=df1['Diagnosis'], linewidth=3, notch = True) fig.add_subplot(2, 5, 10) sns.boxplot(y=df1['fractal.dimension'], x=df1['Diagnosis'], linewidth=3, notch = True)
При рассмотрении диаграмм переменных в соответствии с уровнями диагностики можно заметить, что уровень M принимает более высокие значения почти для каждой переменной. Это справедливо не только для переменной fractal.dimension. Опять же, было замечено, что дисперсия уровня М была выше для всех переменных, кроме переменной fractal.dimension. Я считаю, что это делает набор данных кластеризуемым.
Однако также может быть полезно проверить статистику Хопкинса, которая сообщает нам, является ли набор данных кластеризуемым или нет.
from pyclustertend import hopkins hopkins(pcadf, len(pcadf) - 1) 0.21065060484726908
Согласно статистике Хопкинса, набор данных можно кластеризовать. Это потому, что статистика Хопкинса близка к нулю.
Анализ основных компонентов
Так как я знаю из одного из предыдущих постов, что количество основных компонентов для этого набора данных равно 2, я продолжу с двумя основными компонентами.
from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA sdf = StandardScaler().fit_transform(df) pca = PCA(n_components=2) principalComponents = pca.fit_transform(sdf) pcadf = pd.DataFrame(data = principalComponents, columns = ['PC1', 'PC2'])
k-значит
Для теоретического объяснения k-средних вы можете прочитать этот пост.
Начнем с определения оптимального количества кластеров:
Метод локтя
from kneed import KneeLocator from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score Sum_of_squared_distances = [] K = range(1,10) for num_clusters in K : kmeans = KMeans(n_clusters=num_clusters, n_init=25) kmeans.fit(pcadf) Sum_of_squared_distances.append(kmeans.inertia_) plt.figure(figsize=(10,7)) plt.plot(K,Sum_of_squared_distances, 'x-') 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: # initialise kmeans km = KMeans(n_clusters=num_clusters, n_init=25) km.fit(pcadf) cluster_labels = km.labels_ # silhouette score silhouette_avg.append(silhouette_score(pcadf, 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()
При анализе графика силуэта можно заметить, что наибольшее значение силуэта приходится на два кластера. Однако можно попробовать и 3 кластера, так как особой разницы между ними нет.
Дэвис — метод Булдина
from sklearn.metrics import davies_bouldin_score K = [2, 3, 4, 5, 6, 7, 8, 9, 10] db = [] for num_clusters in K: # initialise kmeans kmeans = KMeans(n_clusters=num_clusters, n_init=25) kmeans.fit(pcadf) cluster_labels = kmeans.fit_predict(pcadf) # silhouette score db.append(davies_bouldin_score(pcadf, 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()
При анализе графика Дэвиса-Булдина можно заметить, что самое низкое значение Дэвиса-Булдина приходится на 6 кластеров. Это совсем другое, чем другие. Я сгруппирую набор данных для 2 и 3 кластеров, как предлагает силуэт.
k-средних для k = 2
# clustering kmeans2 = KMeans(n_clusters=2, random_state=0, n_init=25, algorithm='lloyd') kmeans2.fit(pcadf) # output zero = [] one = [] for i in kmeans2.labels_: if i == 0: zero.append(i) else: one.append(i) print('\n', "Cluster centers:", '\n', "Cluster 0 :", kmeans2.cluster_centers_[0],'\n', "Cluster 1 :", kmeans2.cluster_centers_[1], '\n','\n', "Clustering vector:" ,'\n', kmeans2.labels_, '\n','\n', "Total Within Cluster Sum of Squares : ", '\n', kmeans2.inertia_ , '\n', "Observation numbers :", '\n', "Cluster 0 :", len(zero), '\n', "Cluster 1 :", len(one)) Cluster centers: Cluster 0 : [ 3.00438761 -0.07488982] Cluster 1 : [-1.29082985 0.03217628] Clustering vector: [0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 0 1 1 0 0 1 1 1 1 0 1 1 0 1 1 1 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 1 0 1 0 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 0 1 1 1 1 0 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 1 0 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 1 1 1 0 0 1 0 1 1 1 0 1 1 1 0 1 1 1 1 0 1 1 0 0 1 1 1 1 1 1 1 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 1 1 1 1 1 0 1 0 0 0 1 1 1 0 0 1 1 1 0 1 1 1 1 1 0 0 1 1 0 1 1 0 0 1 0 1 1 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 1 0 0 0 1 1 1 1 0 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 1 0 0 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 0 1 1 0 1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 0 0 1 0 1 0 0 1 1 1 1 0 1 1 0 1 1 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 0 1] Total Within Cluster Sum of Squares : 2342.4243127486625 Observation numbers : Cluster 0 : 171 Cluster 1 : 398
Когда исследуется результат кластеризации k-средних с 2 кластерами, обнаруживается следующее:
- В кластере 1 398 наблюдений, в кластере 0 171 наблюдение.
- Сумма квадратов внутри кластера равна 2342,4243127486657.
Давайте проверим, как выглядят кластеры:
from scipy.spatial import ConvexHull from matplotlib.colors import to_rgba sns.set_style("whitegrid") data = pcadf xcol = "PC1" ycol = "PC2" hues = [0,1] 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=pcadf, x=xcol, y=ycol, hue=kmeans2.labels_, style=kmeans2.labels_, col=kmeans2.labels_, palette=palette, kind="scatter") def overlay_cv_hull_dataframe(x, y, color, data, hue): for hue_val, group in pcadf.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=kmeans2.labels_) g.set_axis_labels(xcol, ycol) plt.show()
Разделение можно наблюдать только в измерении PC1. В сумме квадратов кластера 1 больше, чем кластера 0. Причиной этого должна быть разница между числами наблюдений кластеров. Между кластерами нет видимого перекрытия.
Проверка кластера
Это будет единственный алгоритм кластеризации, для которого я разделяю коды проверки кластера. Я не хочу, чтобы это было настолько повторяющимся. Я покажу все метрики проверки во фрейме данных и позже сравню алгоритмы.
Силуэт
from yellowbrick.cluster import silhouette_visualizer plt.figure(figsize=(10,7)) silhouette_visualizer(kmeans2, pcadf, colors='yellowbrick')
При рассмотрении графика, содержащего значения силуэта каждого наблюдения, можно увидеть, что некоторые наблюдения в первом кластере, показанном синим цветом, имеют отрицательные значения. Это указывает на то, что эти наблюдения могли быть отнесены к неправильному кластеру.
silhouette_score(pcadf, kmeans2.labels_) 0.49228663332300016
Средняя оценка силуэта составляет 0,49. Это значение будет сравниваться с другими алгоритмами кластеризации.
Калински-Харабаш
from sklearn.metrics import calinski_harabasz_score calinski_harabasz_score(pcadf, kmeans2.labels_) 534.4714168884335
Это значение будет сравниваться с другими алгоритмами кластеризации.
Скорректированный индекс Rand
from sklearn.metrics.cluster import adjusted_rand_score adjusted_rand_score(df1['Diagnosis'],kmeans2.labels_) 0.6465880638205838
Это значение будет сравниваться с другими алгоритмами кластеризации.
Показатель точности
df1 = df1.assign( Diagnosis = lambda dataframe: dataframe["Diagnosis"].map(lambda Diagnosis: 0 if Diagnosis == "M" else 1) ) from sklearn.metrics import accuracy_score accuracy_score(df1['Diagnosis'],kmeans2.labels_) 0.9033391915641477
k-средних для k = 3
# clustering kmeans3 = KMeans(n_clusters=3, random_state=0, n_init=25, algorithm='lloyd') kmeans3.fit(pcadf) # output zero = [] one = [] two = [] for i in kmeans3.labels_: if i == 0: zero.append(i) elif i == 1: one.append(i) else: two.append(i) print('\n', "Cluster centers:", '\n', "Cluster 0 :", kmeans3.cluster_centers_[0],'\n', "Cluster 1 :", kmeans3.cluster_centers_[1],'\n', "Cluster 2 :", kmeans3.cluster_centers_[2], '\n', "Clustering vector:" ,'\n', kmeans3.labels_, '\n', "Total Within Cluster Sum of Squares : ", '\n', kmeans3.inertia_ , '\n', "Observation numbers :", '\n', "Cluster 0 :", len(zero), '\n', "Cluster 1 :", len(one), '\n', "Cluster 2 :", len(two)) Cluster centers: Cluster 0 : [1.04646079 1.89652539] Cluster 1 : [-1.53868518 -0.26518367] Cluster 2 : [ 3.35917626 -1.13723881] Clustering vector: [0 2 2 0 2 0 2 0 0 0 1 0 2 1 0 0 1 0 2 1 0 1 0 2 2 0 0 2 0 2 2 0 2 2 0 2 0 1 1 0 1 0 2 0 1 2 1 0 1 1 1 1 1 2 1 1 2 0 1 1 0 1 0 1 0 0 1 1 0 1 2 0 2 1 1 1 0 2 2 1 1 0 2 2 1 2 1 2 1 0 1 1 1 1 0 2 1 1 1 0 1 1 1 1 1 0 1 1 2 1 1 0 0 0 1 1 1 0 0 2 1 2 2 0 1 1 1 2 0 2 1 0 0 1 2 1 1 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 0 1 1 1 2 1 1 1 0 2 2 1 2 1 1 1 2 1 1 1 0 1 1 1 0 2 1 1 2 2 1 1 1 1 2 1 1 1 0 1 1 0 0 1 0 2 2 0 1 2 2 0 1 1 1 1 0 1 2 1 2 2 0 0 1 1 2 2 1 0 1 0 1 1 1 1 1 0 2 1 1 2 1 1 2 2 1 2 1 1 0 1 2 1 1 1 1 1 2 1 2 2 2 0 2 0 0 2 2 1 2 1 2 2 1 1 1 1 1 1 2 1 1 0 1 2 1 1 2 1 2 0 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 0 1 0 2 1 2 1 1 1 1 0 0 0 1 1 1 1 2 1 2 1 2 1 1 1 2 1 1 1 1 1 0 1 0 2 0 1 1 0 1 1 1 1 1 1 1 1 2 2 1 2 2 2 1 2 2 1 0 0 1 1 0 0 1 1 1 1 1 1 1 1 2 1 1 0 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 0 1 1 1 1 1 0 0 2 2 1 0 1 1 1 1 1 2 1 1 2 1 2 1 1 2 1 2 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 2 0 1 1 1 1 1 1 1 1 1 0 1 1 0 1 0 0 1 2 1 1 1 1 2 1 1 1 0 1 2 2 0 0 0 2 0 0 0 0 1 0 1 1 0 1 1 1 2 2 0 0 0 2 1 1 1 1 1 1 0 1 1 1 1 2 1 2 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 2 2 2 1 2 1] Total Within Cluster Sum of Squares : 1713.2725354315216 Observation numbers : Cluster 0 : 117 Cluster 1 : 335 Cluster 2 : 117
Когда исследуется результат кластеризации k-средних с 3 кластерами, обнаруживается следующее:
- В кластере 0 117 наблюдений, в кластере 2 117 наблюдений и в кластере 1 335 наблюдений.
- Всего в пределах кластера сумма квадратов для кластеров составляет 1713,272535431522.
sns.set_style("whitegrid") data = pcadf xcol = "PC1" ycol = "PC2" 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=pcadf, 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 pcadf.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, так и в размерах ПК2.
В сумме квадратов кластера 2 больше, чем других кластеров.
к-медоиды
Теоретическое объяснение k-medoids см. в этом посте.
Начнем с определения оптимального количества кластеров:
Метод локтя
Sum_of_squared_distances = [] K = range(1,10) for num_clusters in K : kmedoid = KMedoids(n_clusters=num_clusters) kmedoid.fit(pcadf) Sum_of_squared_distances.append(kmedoid.inertia_) plt.figure(figsize=(10,7)) plt.plot(K,Sum_of_squared_distances, 'x-') 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: # initialise kmeans kmedoids = KMedoids(n_clusters=num_clusters) kmedoids.fit(pcadf) cluster_labels = kmedoids.labels_ # silhouette score silhouette_avg.append(silhouette_score(pcadf, 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(pcadf) cluster_labels = kmedoids.fit_predict(pcadf) # silhouette score db.append(davies_bouldin_score(pcadf, 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 = 2
# clustering kmedoids2 = KMedoids(n_clusters=2) kmedoids2.fit(pcadf) # output zero = [] one = [] for i in kmedoids2.labels_: if i == 0: zero.append(i) else: one.append(i) print('\n', "Cluster medoids:", '\n', "Cluster 0 :", kmedoids2.cluster_centers_[0],'\n', "Cluster 1 :", kmedoids2.cluster_centers_[1], '\n','\n', "Clustering vector:" ,'\n', kmedoids2.labels_, '\n','\n', "Total Within Cluster Sum of Squares : ", '\n', kmedoids2.inertia_ , '\n', "Observation numbers :", '\n', "Cluster 0 :", len(zero), '\n', "Cluster 1 :", len(one)) Cluster medoids: Cluster 0 : [ 2.35928485 -0.30157828] Cluster 1 : [-1.35986794 -0.03765549] Clustering vector: [0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 1 1 0 1 1 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 1 0 1 1 0 1 1 1 1 0 1 0 1 1 0 1 0 0 1 1 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 0 1 1 1 1 0 0 0 1 0 0 1 1 1 1 0 0 0 1 0 0 1 0 1 1 1 0 1 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 1 1 1 0 0 1 0 1 1 0 0 1 1 1 0 1 1 1 1 0 1 1 0 0 1 1 1 1 0 1 1 1 0 1 1 1 0 1 0 0 0 0 1 0 0 0 1 1 1 0 1 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 1 1 1 0 0 1 1 0 1 1 0 0 1 0 1 1 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 1 1 1 1 1 1 0 1 0 1 1 0 1 1 0 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 1 0 0 0 1 1 1 1 0 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 1 0 0 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 0 1 1 0 1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 1 0 1 1 1 1 0 1 1 1 1 1 0 0 1 0 1 0 0 1 1 1 1 0 1 1 0 1 1 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1] Total Within Cluster Sum of Squares : 968.3783653743097 Observation numbers : Cluster 0 : 190 Cluster 1 : 379
Когда результаты алгоритма кластеризации анализируются, можно констатировать следующее:
- В кластере 0 190 наблюдений, в кластере 1 379 наблюдений, которые можно назвать несбалансированными.
- Медоид кластера для кластера 0 равен [2,35928485 -0,30157828], [-1,35986794 -0,03765549] — для кластера 1.
- Сумма квадратов внутри кластера равна 968,3783653743101.
sns.set_style("whitegrid") data = pcadf xcol = "PC1" ycol = "PC2" hues = [0,1] 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=pcadf, x=xcol, y=ycol, hue=kmedoids2.labels_, style=kmedoids2.labels_, col=kmedoids2.labels_, palette=palette, kind="scatter") def overlay_cv_hull_dataframe(x, y, color, data, hue): for hue_val, group in pcadf.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=kmedoids2.labels_) g.set_axis_labels(xcol, ycol) plt.show()
При анализе двумерных и трехмерных графов перекрытия не наблюдается. Как и в k-средних, наблюдается, что разделение происходит только в измерении PC1. Дисперсия в кластере, показанном светло-синим цветом, выше.
Иерархическая кластеризация
Теоретическое объяснение иерархической кластеризации см. в этом посте.
В этом разделе я сгруппирую набор данных с помощью методов минимальной дисперсии Уорда и средней связи алгоритма иерархической кластеризации.
Метод минимальной дисперсии Уорда
import scipy.cluster.hierarchy as sch plt.figure(figsize = (16 ,8)) dendrogram = sch.dendrogram(sch.linkage(pcadf, method = "ward")) plt.title("Dendrogram") plt.show()
По дендограмме лучше всего резать расстояние на 35–40. Таким образом, 2 кластера подходят для этого метода.
# clustering from sklearn.cluster import AgglomerativeClustering hc = AgglomerativeClustering(n_clusters = 2, metric = "euclidean", linkage = "ward") ward2 = hc.fit_predict(pcadf) # output zero = [] one = [] for i in ward2: if i == 0: zero.append(i) else: one.append(i) print("Observation Numbers :", '\n', "Cluster 0: ", len(zero),'\n', "Cluster 1: ", len(one)) Observation Numbers : Cluster 0: 180 Cluster 1: 389 sns.set_style("whitegrid") data = pcadf xcol = "PC1" ycol = "PC2" hues = [0,1] 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=pcadf, x=xcol, y=ycol, hue=ward2, style=ward2, col=ward2, palette=palette, kind="scatter") def overlay_cv_hull_dataframe(x, y, color, data, hue): for hue_val, group in pcadf.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=ward2) g.set_axis_labels(xcol, ycol) plt.show()
Перекрытие наблюдается при анализе двумерного графика. Замечено, что разделение происходит только в измерении PC1. Дисперсия в кластере, показанном светло-синим цветом, выше.
Метод средней связи
plt.figure(figsize = (16 ,8)) dendrogram = sch.dendrogram(sch.linkage(pcadf, method = "average")) plt.title("Dendrogram") plt.show()
Согласно дендограмме, в каждом случае будет дисбаланс между номерами наблюдений в кластерах. Тем не менее, похоже, что 3 кластера подходят для этого метода.
# clustering hc = AgglomerativeClustering(n_clusters = 32, metric = "euclidean", linkage = "average") average3 = hc.fit_predict(pcadf) # output zero = [] one = [] two = [] for i in average3: if i == 0: zero.append(i) elif i == 1: one.append(i) else: two.append(i) print("Observation Numbers :", '\n', "Cluster 0: ", len(zero),'\n', "Cluster 1: ", len(one), '\n', "Cluster 2: ", len(two)) Observation Numbers : Cluster 0: 30 Cluster 1: 24 Cluster 2: 515
Как видно из дендограммы, число наблюдений несбалансировано. Это не то, чего я хочу. Тем не менее, я проверю метрики проверки.
На этом пост закончился. Анализ будет продолжен с другими алгоритмами кластеризации.
Как всегда:
«На случай, если я тебя не увижу, добрый день, добрый вечер и спокойной ночи!»