2.9 MiB
Загрузка данных¶
from sklearn import datasets
import pandas as pd
import numpy as np
X, y = datasets.make_classification(
n_samples=144,
n_features=5,
n_informative=3,
n_classes=4,
)
print(X)
df = pd.DataFrame(X) # type: ignore
df.columns = ["X1", "X2", "X3", "X4", "X5"]
df['y'] = y
y_names = ['class' + str(i) for i in range(5)]
display(df.head())
display(df.tail())
display(y)
Визуализация данных с учетом понимания из особенностей¶
from visual import draw_data_2d
import matplotlib.pyplot as plt
plt.figure(figsize=(16, 12))
draw_data_2d(df, 0, 1, y.tolist(), y_names, plt.subplot(3, 2, 1))
draw_data_2d(df, 2, 3, y.tolist(), y_names, plt.subplot(3, 2, 2))
draw_data_2d(df, 0, 2, y.tolist(), y_names, plt.subplot(3, 2, 3))
draw_data_2d(df, 1, 3, y.tolist(), y_names, plt.subplot(3, 2, 4))
draw_data_2d(df, 1, 2, y.tolist(), y_names, plt.subplot(3, 2, 5))
draw_data_2d(df, 0, 3, y.tolist(), y_names, plt.subplot(3, 2, 6))
Визуализация данных без понимания их особенностей¶
plt.figure(figsize=(16, 12))
draw_data_2d(df, 0, 1, subplot=plt.subplot(3, 2, 1))
draw_data_2d(df, 2, 3, subplot=plt.subplot(3, 2, 2))
draw_data_2d(df, 0, 2, subplot=plt.subplot(3, 2, 3))
draw_data_2d(df, 1, 3, subplot=plt.subplot(3, 2, 4))
draw_data_2d(df, 1, 2, subplot=plt.subplot(3, 2, 5))
draw_data_2d(df, 0, 3, subplot=plt.subplot(3, 2, 6))
Иерархическая агломеративная кластеризация¶
Также формируется дендрограмма
from utils_clusters import get_linkage_matrix, run_agglomerative
from visual import draw_dendrogram
from scipy.cluster import hierarchy
tree = run_agglomerative(df)
linkage_matrix = get_linkage_matrix(tree)
draw_dendrogram(linkage_matrix)
Получение результатов иерархической кластеризации¶
Также производится сравнение с реальным разбиением
https://joernhees.de/blog/2015/08/26/scipy-hierarchical-clustering-and-dendrogram-tutorial/
result = hierarchy.fcluster(linkage_matrix, 10, criterion="distance")
display(result)
display(y)
# result = [0 if val == 1 else 1 if val == 3 else 2 for val in result]
plt.figure(figsize=(16, 24))
draw_data_2d(df, 0, 1, result, y_names, plt.subplot(4, 2, 1))
draw_data_2d(df, 0, 1, y.tolist(), y_names, plt.subplot(4, 2, 2))
draw_data_2d(df, 2, 3, result, y_names, plt.subplot(4, 2, 3))
draw_data_2d(df, 2, 3, y.tolist(), y_names, plt.subplot(4, 2, 4))
draw_data_2d(df, 0, 2, result, y_names, plt.subplot(4, 2, 5))
draw_data_2d(df, 0, 2, y.tolist(), y_names, plt.subplot(4, 2, 6))
draw_data_2d(df, 1, 3, result, y_names, plt.subplot(4, 2, 7))
draw_data_2d(df, 1, 3, y.tolist(), y_names, plt.subplot(4, 2, 8))
Неиерархическая четка кластеризация (k-means)¶
from utils_clusters import print_cluster_result, run_kmeans
random_state = 9
labels, centers = run_kmeans(df, 2, random_state)
print_cluster_result(df, 2, labels)
display(centers)
display(y)
Визуализация результатов кластеризации¶
from visual import draw_cluster_results
plt.figure(figsize=(16, 12))
draw_cluster_results(df, 0, 1, labels, centers, plt.subplot(2, 2, 1))
draw_cluster_results(df, 2, 3, labels, centers, plt.subplot(2, 2, 2))
draw_cluster_results(df, 0, 2, labels, centers, plt.subplot(2, 2, 3))
draw_cluster_results(df, 1, 3, labels, centers, plt.subplot(2, 2, 4))
Разбиение на 4 кластера и сравнение с реальным разбиением¶
labels, centers = run_kmeans(df, 4, random_state)
plt.figure(figsize=(16, 24))
draw_data_2d(df, 0, 1, labels.tolist(), y_names, plt.subplot(4, 2, 1))
draw_data_2d(df, 0, 1, y.tolist(), y_names, plt.subplot(4, 2, 2))
draw_data_2d(df, 2, 3, labels.tolist(), y_names, plt.subplot(4, 2, 3))
draw_data_2d(df, 2, 3, y.tolist(), y_names, plt.subplot(4, 2, 4))
draw_data_2d(df, 0, 2, labels.tolist(), y_names, plt.subplot(4, 2, 5))
draw_data_2d(df, 0, 2, y.tolist(), y_names, plt.subplot(4, 2, 6))
draw_data_2d(df, 1, 3, labels.tolist(), y_names, plt.subplot(4, 2, 7))
draw_data_2d(df, 1, 3, y.tolist(), y_names, plt.subplot(4, 2, 8))
Понижение размерности до n=2¶
from sklearn.decomposition import PCA
reduced_data = PCA(n_components=2).fit_transform(df)
reduced_data
Визуализация данных после понижения размерности¶
plt.figure(figsize=(16, 6))
draw_data_2d(
pd.DataFrame({"Column1": reduced_data[:, 0], "Column2": reduced_data[:, 1]}),
0,
1,
subplot=plt.subplot(1, 2, 1),
)
draw_data_2d(
pd.DataFrame({"Column1": reduced_data[:, 0], "Column2": reduced_data[:, 1]}),
0,
1,
y.tolist(),
y_names,
plt.subplot(1, 2, 2),
)
Визуализация результатов неиерархической кластеризации для двух кластеров с учетом понижения размерности¶
from utils_clusters import fit_kmeans
from visual import draw_clusters
kmeans = fit_kmeans(reduced_data, 2, random_state)
draw_clusters(reduced_data, kmeans)
Визуализация результатов неиерархической кластеризации для четырех кластеров с учетом понижения размерности¶
kmeans = fit_kmeans(reduced_data, 4, random_state)
draw_clusters(reduced_data, kmeans)
Сравнение результатов кластеризации с реальным разбиением с учетом понижения размерности¶
labels = [2 if val == 1 else 1 if val == 2 else val for val in kmeans.labels_]
plt.figure(figsize=(16, 12))
draw_data_2d(
pd.DataFrame({"Column1": reduced_data[:, 0], "Column2": reduced_data[:, 1]}),
0,
1,
labels,
y_names,
plt.subplot(2, 2, 1),
)
draw_data_2d(
pd.DataFrame({"Column1": reduced_data[:, 0], "Column2": reduced_data[:, 1]}),
0,
1,
result,
y_names,
plt.subplot(2, 2, 2),
)
draw_data_2d(
pd.DataFrame({"Column1": reduced_data[:, 0], "Column2": reduced_data[:, 1]}),
0,
1,
y.tolist(),
y_names,
plt.subplot(2, 2, 3),
)
Выбор количества кластеров на основе инерции¶
Инерция -- сумма квадратов расстояний выборок до ближайшего центра кластера, взвешенная по весам выборок, если таковые имеются.
from utils_clusters import get_clusters_inertia
from visual import draw_elbow_diagram
inertias, clusters_range = get_clusters_inertia(df, random_state)
display(clusters_range)
display(inertias)
draw_elbow_diagram(inertias, clusters_range)
Выбор количества кластеров на основе коэффициента силуэта¶
Коэффициент силуэта рассчитывается с использованием среднего расстояния внутри кластера (а) и среднего расстояния до ближайшего кластера (b) для каждого образца. Коэффициент силуэта для образца равен (b - a) / max(a, b). Для пояснения: b — это расстояние между образцом и ближайшим кластером, частью которого образец не является. Обратите внимание, что коэффициент силуэта определяется только в том случае, если количество меток равно 2 <= n_labels <= n_samples - 1.
Эта функция возвращает средний коэффициент силуэта по всем образцам.
Лучшее значение — 1, худшее — -1. Значения около 0 указывают на перекрывающиеся кластеры. Отрицательные значения обычно указывают на то, что образец был отнесен к неправильному кластеру.
from utils_clusters import get_clusters_silhouette_scores
from visual import draw_silhouettes_diagram
silhouette_scores, clusters_range = get_clusters_silhouette_scores(df, random_state)
display(clusters_range)
display(silhouette_scores)
draw_silhouettes_diagram(silhouette_scores, clusters_range)
Пример анализа силуэтов для разбиения от 2 до 12 кластеров¶
max_clusters = int(math.sqrt(len(df)))
https://scikit-learn.org/1.5/auto_examples/cluster/plot_kmeans_silhouette_analysis.html
from utils_clusters import get_clusters_silhouettes
from visual import draw_silhouettes
silhouettes = get_clusters_silhouettes(reduced_data, random_state)
draw_silhouettes(reduced_data, silhouettes)