1.7 MiB
Бизнес-цель: класстеризация цен на золото с целью выделения основных ценовых групп.
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
df = pd.read_csv("../static/csv/FINAL_USO.csv")
df=df.drop(columns=["Date"])
columns = df.columns
index = df.index
print(df.columns)
Визуализация по парам признаков
def draw_data_2d(data, x_index, y_index, subplot):
# Преобразуем numpy.ndarray в pandas.DataFrame
data = pd.DataFrame(data, columns=columns, index=index)
sns.scatterplot(x=data.columns[x_index], y=data.columns[y_index], data=data, ax=subplot)
subplot.set_title(f'{data.columns[x_index]} vs {data.columns[y_index]}')
subplot.set_xlabel(data.columns[x_index])
subplot.set_ylabel(data.columns[y_index])
plt.figure(figsize=(16, 12))
#high и low
draw_data_2d(df, 2, 3, subplot=plt.subplot(2, 2, 1))
# high и adj close
draw_data_2d(df, 2, 5, subplot=plt.subplot(2, 2, 2))
# low и adj close
draw_data_2d(df, 3, 5, subplot=plt.subplot(2, 2, 3))
#Volume и adj close
draw_data_2d(df, 6, 5, subplot=plt.subplot(2, 2, 4))
from sklearn.preprocessing import StandardScaler
columns = df.columns
index = df.index
Понижение размерности и визуализации¶
from sklearn.decomposition import PCA
reduced_df = PCA(n_components=2).fit_transform(df)
# Визуализация
plt.figure(figsize=(8, 6))
plt.scatter(reduced_df[:, 0], reduced_df[:, 1], alpha=0.6)
plt.title("Визуализация данных после понижения размерности до 2-х")
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")
plt.show()
Выбор кол-ва кластеров на основе инерции (метод локтя) (неиерархического алгоритма кластеризации)
from sklearn.cluster import KMeans
inertias = []
clusters_range = range(1, 11)
for i in clusters_range:
kmeans = KMeans(n_clusters=i, random_state=42)
kmeans.fit(df)
inertias.append(kmeans.inertia_)
plt.figure(figsize=(10, 6))
plt.plot(clusters_range, inertias, marker='o')
plt.title('Метод локтя для оптимального k')
plt.xlabel('Количество кластеров')
plt.ylabel('Инерция')
plt.grid(True)
plt.show()
Вывод:¶
На основе инерции можно предположить, что оптимально взять 2-4 кластера
Выбор кол-ва кластеров на основе коэф-а силуэта (для неиерархического алгоритма кластеризации)¶
Коэффициент силуэта вычисляется с помощью среднего внутрикластерного расстояния (a) и среднего расстояния до ближайшего кластера (b) по каждому образцу. Силуэт вычисляется как (b - a) / max(a, b). b — это расстояние между a и ближайшим кластером, в который a не входит.
Коэффициент силуэта принимает значения от -1 до 1. Чем ближе значение к 1, тем лучше качество кластеризации. Значение, близкое к 0, может указывать на то, что объекты находятся на границе между кластерами, а отрицательное значение — на неправильное назначение объектов кластерам.
from sklearn.metrics import silhouette_score
silhouette_scores = []
for i in clusters_range[1:]:
kmeans = KMeans(n_clusters=i, random_state=42)
labels = kmeans.fit_predict(df) #reduced_df
score = silhouette_score(reduced_df, labels) #reduced_df
silhouette_scores.append(score)
# Построение диаграммы значений силуэта
plt.figure(figsize=(10, 6))
plt.plot(clusters_range[1:], silhouette_scores, marker='o')
plt.title('Коэффициенты силуэта для разных k')
plt.xlabel('Количество кластеров')
plt.ylabel('Коэффициент силуэта')
plt.grid(True)
plt.show()
Вывод:¶
Значение коэф-а силуэта наибольшее при 2 кластерах, т.е. выгодно взять 2 кластера. Дальше значение коэф-а силуэта будет постепенно снижаться.
Иерархическая кластеризация¶
Иерархический агломеративный алгоритм: последовательное объединение исходных элементов и уменьшение числа кластеров (построение кластеров снизу вверх).
from scipy.cluster import hierarchy
#linkage - иерархич. кластеризация
linkage_matrix = hierarchy.linkage(df, method='ward')
plt.figure(figsize=(10, 7))
hierarchy.dendrogram(linkage_matrix, truncate_mode='lastp', p=16, leaf_rotation=90., leaf_font_size=12., show_contracted=True)
plt.title('Дендрограмма иерархической агломеративной кластеризации')
plt.xlabel('Индексы образцов')
plt.ylabel('Расстояние')
plt.show()
Визуализация кластеризации
linkage_matrix = hierarchy.linkage(df, method='ward')
result = hierarchy.fcluster(linkage_matrix, 60, criterion="distance")
print(result)
def draw_data_2d(data, x_index, y_index, clusters, subplot):
data = pd.DataFrame(data, columns=columns, index=index)
sns.scatterplot(x=data.columns[x_index], y=data.columns[y_index], hue=clusters, palette='viridis', data=data, ax=subplot)
subplot.set_title(f'{data.columns[x_index]} vs {data.columns[y_index]}')
subplot.set_xlabel(data.columns[x_index])
subplot.set_ylabel(data.columns[y_index])
plt.figure(figsize=(16, 12))
#high и low
draw_data_2d(df, 2, 3,result, subplot=plt.subplot(2, 2, 1))
# high и adj close
draw_data_2d(df, 2, 5, result,subplot=plt.subplot(2, 2, 2))
# low и adj close
draw_data_2d(df, 3, 5, result,subplot=plt.subplot(2, 2, 3))
#Volume и adj close
draw_data_2d(df, 6, 5, result,subplot=plt.subplot(2, 2, 4))
Неиерархический алгоритм кластеризации¶
# Применение K-Means для кластеризации
# можно изменить количество кластеров
kmeans = KMeans(n_clusters=2, random_state=42)
# кластеры
clusters = kmeans.fit_predict(df) #reduced_df
# задание названий кластерам
clusters_names = ["Кластер 1" if cluster == 0 else "Кластер 2" for cluster in clusters]
def draw_data_2d(data, x_index, y_index, clusters):
plt.figure(figsize=(10, 8))
sns.scatterplot(x=data.columns[x_index], y=data.columns[y_index], hue=clusters, palette='viridis', data=data)
plt.title(f' KMeans {data.columns[x_index]} vs {data.columns[y_index]}')
plt.xlabel(data.columns[x_index])
plt.ylabel(data.columns[y_index])
plt.legend()
plt.show()
#high и low
draw_data_2d(df, 1, 2, clusters_names)
# high и adj close
draw_data_2d(df, 1, 4,clusters_names)
# low и adj close
draw_data_2d(df, 2,4, clusters_names)
#Volume и adj close
draw_data_2d(df, 5, 4, clusters_names)
Неиерархическая кластеризация на PCA
def draw_data_with_centers_2d(data, x_index, y_index, clusters, centers):
plt.figure(figsize=(10, 8))
sns.scatterplot(x=data.columns[x_index], y=data.columns[y_index], hue=clusters, palette='viridis', data=data)
for i, center in enumerate(centers):
plt.scatter(center[x_index], center[y_index], marker='x', color='red', s=200, label=f'Центр кластера {i+1}')
plt.title(f'KMeans {data.columns[x_index]} vs {data.columns[y_index]}')
plt.xlabel(data.columns[x_index])
plt.ylabel(data.columns[y_index])
plt.legend()
plt.show()
# Применение K-Means для кластеризации
# можно изменить количество кластеров
kmeans = KMeans(n_clusters=2, random_state=42)
# кластеры
clusters = kmeans.fit_predict(reduced_df) #reduced_df
# задание названий кластерам
clusters_names = ["Кластер 1" if cluster == 0 else "Кластер 2" for cluster in clusters]
# координаты центров кластеров
centers = kmeans.cluster_centers_
reduced_df = pd.DataFrame(reduced_df, columns=['PCA1', 'PCA2'])
draw_data_with_centers_2d(reduced_df, 0, 1, clusters_names, centers)
# Применение K-Means для кластеризации
# можно изменить количество кластеров
kmeans = KMeans(n_clusters=3, random_state=42)
# кластеры
clusters = kmeans.fit_predict(reduced_df) #reduced_df
# задание названий кластерам
clusters_names = ["Кластер 1" if cluster == 0 else "Кластер 2" if cluster == 1 else "Кластер 3" for cluster in clusters]
# координаты центров кластеров
centers = kmeans.cluster_centers_
reduced_df = pd.DataFrame(reduced_df, columns=['PCA1', 'PCA2'])
draw_data_with_centers_2d(reduced_df, 0, 1, clusters_names, centers)
from sklearn.metrics import silhouette_samples, silhouette_score
import numpy as np
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(df)
random_state = 9
def get_clusters_silhouettes(data, random_state, max_clusters=5):
silhouettes = []
for n_clusters in range(2, max_clusters + 1):
kmeans = KMeans(n_clusters=n_clusters, random_state=random_state)
cluster_labels = kmeans.fit_predict(data)
silhouette_avg = silhouette_score(data, cluster_labels)
sample_silhouette_values = silhouette_samples(data, cluster_labels)
silhouettes.append((n_clusters, silhouette_avg, sample_silhouette_values, cluster_labels))
return silhouettes
def draw_silhouettes(data, silhouettes):
for n_clusters, silhouette_avg, sample_silhouette_values, cluster_labels in silhouettes:
fig, ax1 = plt.subplots(1, 1)
fig.set_size_inches(18, 7)
# Первый график: Диаграмма силуэтов
ax1.set_xlim([-0.1, 1])
ax1.set_ylim([0, len(data) + (n_clusters + 1) * 10])
y_lower = 10
for i in range(n_clusters):
ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
ith_cluster_silhouette_values.sort()
size_cluster_i = ith_cluster_silhouette_values.shape[0]
y_upper = y_lower + size_cluster_i
color = plt.cm.nipy_spectral(float(i) / n_clusters)
ax1.fill_betweenx(np.arange(y_lower, y_upper), 0, ith_cluster_silhouette_values, facecolor=color, edgecolor=color, alpha=0.7)
ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
y_lower = y_upper + 10
ax1.set_title("Диаграмма силуэтов для %d кластеров" % n_clusters)
ax1.set_xlabel("Коэффициент силуэта")
ax1.set_ylabel("Метка кластера")
ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
ax1.set_yticks([])
ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
plt.suptitle(("Анализ силуэтов для KMeans кластеризации на данных с %d кластерами" % n_clusters), fontsize=14, fontweight='bold')
plt.show()
silhouettes = get_clusters_silhouettes(df, random_state)
draw_silhouettes(df, silhouettes)