Files
AIM-PIbd-32-kuznetsov-A-V/lab5/lab5.ipynb
2024-12-10 22:25:14 +04:00

3.2 MiB
Raw Blame History

Анализ цен на акции золота с применением метода кластеризации

In [18]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score

df = pd.read_csv(".//static//csv//Yamana_Gold_Inc._AUY.csv")
print(df.columns)
Index(['Date', 'Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'], dtype='object')
In [19]:
df.head()
Out[19]:
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
Date Open High Low Close Adj Close Volume
0 6/22/2001 3.428571 3.428571 3.428571 3.428571 2.806002 0
1 6/25/2001 3.428571 3.428571 3.428571 3.428571 2.806002 0
2 6/26/2001 3.714286 3.714286 3.714286 3.714286 3.039837 0
3 6/27/2001 3.714286 3.714286 3.714286 3.714286 3.039837 0
4 6/28/2001 3.714286 3.714286 3.714286 3.714286 3.039837 0
In [20]:
df.describe()
Out[20]:
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
Open High Low Close Adj Close Volume
count 5251.000000 5251.000000 5251.000000 5251.000000 5251.000000 5.251000e+03
mean 6.863639 6.986071 6.720615 6.850606 5.895644 9.081991e+06
std 4.753836 4.832010 4.662891 4.746055 3.941634 7.623285e+06
min 1.142857 1.142857 1.142857 1.142857 0.935334 0.000000e+00
25% 2.857143 2.880000 2.810000 2.857143 2.537094 2.845900e+06
50% 4.600000 4.710000 4.490000 4.600000 4.337419 8.216200e+06
75% 10.650000 10.860000 10.425000 10.640000 8.951945 1.327245e+07
max 20.420000 20.590000 20.090000 20.389999 17.543156 7.671400e+07
In [21]:
# Процент пропущенных значений признаков
for i in df.columns:
    null_rate = df[i].isnull().sum() / len(df) * 100
    if null_rate > 0:
        print(f'{i} Процент пустых значений: %{null_rate:.2f}')

print(df.isnull().sum())

print(df.isnull().any())
Date         0
Open         0
High         0
Low          0
Close        0
Adj Close    0
Volume       0
dtype: int64
Date         False
Open         False
High         False
Low          False
Close        False
Adj Close    False
Volume       False
dtype: bool
In [22]:
# Проверка типов столбцов
df.dtypes
Out[22]:
Date          object
Open         float64
High         float64
Low          float64
Close        float64
Adj Close    float64
Volume         int64
dtype: object

Цель: Кластеризация цен на акции для определения схожих характеристик.

Очистка данных

In [23]:
# Удалим несущественные столбцы
columns_to_drop = ["Date", "Adj Close"]
df_cleaned = df.drop(columns=columns_to_drop)

print(df_cleaned.head())  # Вывод очищенного DataFrame
       Open      High       Low     Close  Volume
0  3.428571  3.428571  3.428571  3.428571       0
1  3.428571  3.428571  3.428571  3.428571       0
2  3.714286  3.714286  3.714286  3.714286       0
3  3.714286  3.714286  3.714286  3.714286       0
4  3.714286  3.714286  3.714286  3.714286       0

Визуализация парных взаимосвязей

In [24]:
# Настройка стиля графиков
sns.set(style="whitegrid")

# Создание фигуры
plt.figure(figsize=(15, 10))


plt.subplot(2, 2, 1)
sns.scatterplot(x=df_cleaned["Open"], y=df_cleaned["Volume"], alpha=0.6, color="purple")
plt.title("Open vs Volume")
plt.xlabel('Open')
plt.ylabel("Volume")


plt.subplot(2, 2, 2)
sns.scatterplot(
    x=df_cleaned["High"],
    y=df_cleaned["Volume"],
    alpha=0.6,
    color="green",
)
plt.title("High vs Volume")
plt.xlabel("High")
plt.ylabel("Volume")

plt.subplot(2, 2, 3)
sns.scatterplot(
    x=df_cleaned["High"], y=df_cleaned["Close"], alpha=0.6, color="red"
)
plt.title("High vs Close")
plt.xlabel("High")
plt.ylabel("Close")


plt.subplot(2, 2, 4)
sns.scatterplot(x=df_cleaned["High"], y=df_cleaned["Low"], alpha=0.6, color="red")
plt.title("High vs Low")
plt.xlabel("High")
plt.ylabel("Low")


plt.tight_layout()
plt.show()
No description has been provided for this image

Стандартизация данных для кластеризации

In [25]:
# Нормализация данных
scaler = StandardScaler()
data_scaled = scaler.fit_transform(df_cleaned)

# Преобразование в DataFrame для удобства
df_scaled = pd.DataFrame(data_scaled, columns=df_cleaned.columns)

# Понижение размерности до 2 компонент
pca = PCA(n_components=2)
kc_pca = pca.fit_transform(df_scaled)

# Визуализация
plt.figure(figsize=(8, 6))
plt.scatter(kc_pca[:, 0], kc_pca[:, 1], alpha=0.6)
plt.title("PCA Visualization")
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")
plt.show()
No description has been provided for this image

Агломеративная (иерархическая) кластеризация

In [26]:
# Построение дендрограммы
linkage_matrix = linkage(data_scaled, method='ward')
plt.figure(figsize=(10, 7))
dendrogram(linkage_matrix)
plt.title('Дендрограмма агломеративной кластеризации')
plt.xlabel('Индекс образца')
plt.ylabel('Расстояние')
plt.show()

# Получение результатов кластеризации с заданным порогом
result = fcluster(linkage_matrix, t=60, criterion='distance')
print(result)  # Вывод результатов кластеризации
No description has been provided for this image
[3 3 3 ... 4 4 4]
In [27]:
# Выбираем подмножество данных для кластеризации
features = df[["Open", "High", "Low", "Close", "Volume"]]

scaled_features = scaler.fit_transform(features)

# Построение дендрограммы
linkage_matrix = linkage(scaled_features, method='ward')  # Метод "Ward"

plt.figure(figsize=(12, 8))
dendrogram(linkage_matrix, labels=df.index, leaf_rotation=90, leaf_font_size=10)
plt.title('Иерархическая кластеризация (дендрограмма)')
plt.xlabel('Индекс')
plt.ylabel('Евклидово расстояние')
plt.tight_layout()
plt.show()
No description has been provided for this image

Визуализация распределения кластеров

In [28]:
# Визуализация кластеров
plt.figure(figsize=(14, 12))


plt.subplot(2, 2, 1)
sns.scatterplot(
    x=df_cleaned["High"],
    y=df_cleaned["Low"],
    hue=result,
    palette="Set1",
    alpha=0.6,
)
plt.title("High vs Low")
plt.xlabel("High")
plt.ylabel("Low")


plt.subplot(2, 2, 2)
sns.scatterplot(
    x=df_cleaned["Open"],
    y=df_cleaned["Close"],
    hue=result,
    palette="Set1",
    alpha=0.6,
)
plt.title("Open vs Close")
plt.xlabel("Open")
plt.ylabel("Close")

plt.subplot(2, 2, 3)
sns.scatterplot(
    x=df_cleaned["Open"],
    y=df_cleaned["Volume"],
    hue=result,
    palette="Set1",
    alpha=0.6,
)
plt.title("Open vs Volume")
plt.xlabel("Open")
plt.ylabel("Volume")


plt.subplot(2, 2, 4)
sns.scatterplot(
    x=df_cleaned["Low"],
    y=df_cleaned["Close"],
    hue=result,
    palette="Set1",
    alpha=0.6,
)
plt.title("Low vs Close")
plt.xlabel("Low")
plt.ylabel("Close")

# Настройка графиков
plt.tight_layout()
plt.show()
No description has been provided for this image

KMeans (неиерархическая кластеризация) для сравнения

In [29]:
features_used = [
    'Open','High','Low','Close','Volume'
]
data_to_scale = df_cleaned[features_used]
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data_to_scale)

random_state = 42
kmeans = KMeans(n_clusters=4, random_state=random_state)
labels = kmeans.fit_predict(data_scaled)
centers = kmeans.cluster_centers_

# Отображение центроидов
centers_original = scaler.inverse_transform(centers)  # Обратная стандартизация
print("Центры кластеров:\n", centers_original)

# Визуализация результатов кластеризации KMeans
plt.figure(figsize=(16, 12))
plt.subplot(2, 2, 1)
sns.scatterplot(
    x=df_cleaned["High"],
    y=df_cleaned["Volume"],
    hue=labels,
    palette="Set1",
    alpha=0.6,
)
plt.scatter(centers[:, 0], centers[:, 1], s=300, c='cyan', label='Centroids')
plt.title("KMeans Clustering: High vs Volume")
plt.legend()

plt.subplot(2, 2, 2)
sns.scatterplot(
    x=df_cleaned["Low"],
    y=df_cleaned["Volume"],
    hue=labels,
    palette="Set1",
    alpha=0.6,
)
plt.scatter(centers[:, 2], centers[:, 3], s=300, c='cyan', label='Centroids')
plt.title("KMeans Clustering: Low vs Volume")
plt.legend()

plt.subplot(2, 2, 3)
sns.scatterplot(
    x=df_cleaned["Open"],
    y=df_cleaned["Close"],
    hue=labels,
    palette="Set1",
    alpha=0.6,
)
plt.scatter(centers[:, 1], centers[:, 3], s=300, c='cyan', label='Centroids')
plt.title("KMeans Clustering: Open vs Close")
plt.legend()


plt.tight_layout()
plt.show()
Центры кластеров:
 [[1.02022923e+01 1.03814308e+01 9.99013846e+00 1.01815769e+01
  8.65023215e+06]
 [2.77026286e+00 2.80709714e+00 2.72500000e+00 2.76552000e+00
  2.94078457e+06]
 [1.52312121e+01 1.54824369e+01 1.49392172e+01 1.52041414e+01
  8.61184167e+06]
 [4.16388219e+00 4.26791341e+00 4.04695529e+00 4.15552874e+00
  1.73720960e+07]]
No description has been provided for this image

PCA для визуализации сокращенной размерности

In [30]:
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(data_scaled)

# Визуализация сокращенных данных
plt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=result, palette='Set1', alpha=0.6)
plt.title('PCA reduced data: Agglomerative Clustering')

plt.subplot(1, 2, 2)
sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=labels, palette='Set1', alpha=0.6)
plt.title('PCA reduced data: KMeans Clustering')

plt.tight_layout()
plt.show()
No description has been provided for this image

Анализ инерции для метода локтя (метод оценки суммы квадратов расстояний)

In [31]:
inertias = []
clusters_range = range(1, 11)
for i in clusters_range:
    kmeans = KMeans(n_clusters=i, random_state=random_state)
    kmeans.fit(data_scaled)
    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()
No description has been provided for this image

Расчет коэффициентов силуэта

In [32]:
silhouette_scores = []
for i in clusters_range[1:]:  
    kmeans = KMeans(n_clusters=i, random_state=random_state)
    labels = kmeans.fit_predict(data_scaled)
    score = silhouette_score(data_scaled, labels)
    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()
No description has been provided for this image
In [33]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans

# ========================
# Применение K-Means
# ========================
kmeans = KMeans(n_clusters=3, random_state=42)  
df_clusters = kmeans.fit_predict(df_scaled)

# ========================
# Оценка качества кластеризации
# ========================
silhouette_avg = silhouette_score(df_scaled, df_clusters)
print(f'Средний коэффициент силуэта: {silhouette_avg:.3f}')

# ========================
# Визуализация кластеров
# ========================
from sklearn.decomposition import PCA

pca = PCA(n_components=2)
df_pca = pca.fit_transform(df_scaled)

plt.figure(figsize=(10, 7))
sns.scatterplot(x=df_pca[:, 0], y=df_pca[:, 1], hue=df_clusters, palette='viridis', alpha=0.7)
plt.title('Визуализация кластеров с помощью K-Means')
plt.xlabel('Первая компонентa PCA')
plt.ylabel('Вторая компонентa PCA')
plt.legend(title='Кластер', loc='upper right')
plt.show()
Средний коэффициент силуэта: 0.491
No description has been provided for this image

Средний коэффициент силуэта, равный 0.491, указывает на умеренно хорошую кластеризацию.

Средний коэффициент силуэта (silhouette score) указывает на качество кластеризации, измеряя, насколько хорошо точки внутри одного кластера близки друг к другу по сравнению с точками из других кластеров. Значения коэффициента силуэта находятся в диапазоне от -1 до 1:

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

In [34]:
from sklearn.cluster import AgglomerativeClustering

# ========================
# Агломеративная кластеризация
# ========================
agg_cluster = AgglomerativeClustering(n_clusters=3)  
labels_agg = agg_cluster.fit_predict(df_scaled)

# ========================
# Оценка качества кластеризации
# ========================
silhouette_avg_agg = silhouette_score(df_scaled, labels_agg)
print(f'Средний коэффициент силуэта (агломеративная кластеризация): {silhouette_avg_agg:.3f}')

# ========================
# Визуализация кластеров
# ========================
pca = PCA(n_components=2)
df_pca = pca.fit_transform(df_scaled)

plt.figure(figsize=(10, 7))
sns.scatterplot(x=df_pca[:, 0], y=df_pca[:, 1], hue=labels_agg, palette='viridis', alpha=0.7)
plt.title('Визуализация кластеров с помощью агломеративной кластеризации')
plt.xlabel('Первая компонентa PCA')
plt.ylabel('Вторая компонентa PCA')
plt.legend(title='Кластер', loc='upper right')
plt.show()
Средний коэффициент силуэта (агломеративная кластеризация): 0.494
No description has been provided for this image

Значение коэффициента силуэта лежит в диапазоне от -1 до 1. Ближе к 1: Хорошо сформированные, плотные кластеры, четко отделенные друг от друга.

Ближе к 0: Кластеры пересекаются или слабо разделены, не имеют четких границ. Точки расположены одинаково близко как к своему кластеру, так и к соседним. Ближе к -1 (Отрицательные значения): Некоторые точки скорее относятся к другим кластерам, чем к текущему (ближе к центрам других кластеров). Очень плохая кластеризация.
Ближе к 1: Все точки внутри каждого кластера плотно сгруппированы и значительно удалены от точек других кластеров. Свидетельствует о четкой и хорошо разделенной структуре данных. Единица говорит об идеальной кластеризации.