From 32b7569f4c19bc381216a84f2b142f8c42f6184f Mon Sep 17 00:00:00 2001 From: Ogarkov Ilya Date: Fri, 21 Nov 2025 12:17:09 +0300 Subject: [PATCH] =?UTF-8?q?code:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B0=D0=BC?= =?UTF-8?q?=D0=BC=D1=8B=20=D0=BF=D0=BE=20=D0=B4=D0=B0=D1=82=D0=B0=D1=81?= =?UTF-8?q?=D0=B5=D1=82=D1=83=20HW(=D0=98=D0=90=D0=94=20=D0=9B=D0=A02)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ИАД/lr2/task1_HW.py | 119 +++++++++++++++++++ ИАД/lr2/task2_HW.py | 101 ++++++++++++++++ ИАД/lr2/task2_n_HW.py | 206 +++++++++++++++++++++++++++++++++ ИАД/lr2/task3_HW.py | 32 ++++++ ИАД/lr2/task4_HW.py | 43 +++++++ ИАД/lr2/task6_HW.py | 243 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 744 insertions(+) create mode 100644 ИАД/lr2/task1_HW.py create mode 100644 ИАД/lr2/task2_HW.py create mode 100644 ИАД/lr2/task2_n_HW.py create mode 100644 ИАД/lr2/task3_HW.py create mode 100644 ИАД/lr2/task4_HW.py create mode 100644 ИАД/lr2/task6_HW.py diff --git a/ИАД/lr2/task1_HW.py b/ИАД/lr2/task1_HW.py new file mode 100644 index 0000000..c1f65fc --- /dev/null +++ b/ИАД/lr2/task1_HW.py @@ -0,0 +1,119 @@ +from sklearn.datasets import load_iris +import matplotlib.pyplot as plt +import pandas as pd +import numpy as np +import seaborn as sns +from sklearn.preprocessing import StandardScaler + +# Загрузка датасета MLB +mlb_data = pd.read_csv('SOCR_Data_MLB_HeightsWeights.txt', sep='\t') +mlb_data.columns = ['Name', 'Team', 'Position', 'Height', 'Weight', 'Age'] + +# Создаем 3D визуализацию +fig = plt.figure(figsize=(10, 7)) +ax = plt.axes(projection='3d') + +# Простая scatter plot без разбивки по позициям +ax.scatter3D(mlb_data['Height'], mlb_data['Weight'], mlb_data['Age']) +ax.set_title('3D Scatter Plot: MLB Players') +ax.set_xlabel('Height (inches)') +ax.set_ylabel('Weight (pounds)') +ax.set_zlabel('Age (years)') + +plt.show() + + +# ТЕПЛОВЫЕ КАРТЫ + +# Функция для вычисления матрицы частных корреляций +def partial_correlation_matrix(df): + """ + Вычисляет матрицу частных корреляций + """ + # Вычисляем матрицу ковариаций + cov_matrix = np.cov(df.T) + + # Вычисляем обратную матрицу ковариаций + try: + inv_cov_matrix = np.linalg.inv(cov_matrix) + except np.linalg.LinAlgError: + # Если матрица вырождена, используем псевдообратную + inv_cov_matrix = np.linalg.pinv(cov_matrix) + + # Вычисляем частные корреляции + n_features = inv_cov_matrix.shape[0] + partial_corr_matrix = np.zeros((n_features, n_features)) + + for i in range(n_features): + for j in range(n_features): + partial_corr_matrix[i, j] = -inv_cov_matrix[i, j] / np.sqrt(inv_cov_matrix[i, i] * inv_cov_matrix[j, j]) + + # Диагональные элементы устанавливаем в 1 + np.fill_diagonal(partial_corr_matrix, 1) + + return partial_corr_matrix + + +# Создаем фигуру для тепловых карт +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6)) + +# 1. Тепловая карта парных коэффициентов корреляции +numeric_data = mlb_data[['Height', 'Weight', 'Age']] +correlation_matrix = numeric_data.corr() + +sns.heatmap(correlation_matrix, + annot=True, + cmap='coolwarm', + center=0, + vmin=-1, + vmax=1, + square=True, + fmt='.2f', + cbar_kws={'label': 'Correlation Coefficient'}, + ax=ax1) + +ax1.set_title('Тепловая карта парных коэффициентов корреляции\n(Pearson Correlation)') + +# 2. Тепловая карта частных коэффициентов корреляции +# Стандартизация данных для частных корреляций +scaler = StandardScaler() +data_scaled = scaler.fit_transform(numeric_data) + +# Вычисляем матрицу частных корреляций +partial_corr_matrix = partial_correlation_matrix(data_scaled) +partial_corr_df = pd.DataFrame(partial_corr_matrix, + index=numeric_data.columns, + columns=numeric_data.columns) + +sns.heatmap(partial_corr_df, + annot=True, + cmap='coolwarm', + center=0, + vmin=-1, + vmax=1, + square=True, + fmt='.2f', + cbar_kws={'label': 'Partial Correlation Coefficient'}, + ax=ax2) + +ax2.set_title('Тепловая карта частных коэффициентов корреляции\n(Partial Correlation)') + +plt.tight_layout() +plt.show() + +# Вывод числовых значений для анализа +print("=" * 60) +print("АНАЛИЗ КОРРЕЛЯЦИЙ MLB ДАННЫХ") +print("=" * 60) + +print("\nПАРНЫЕ КОРРЕЛЯЦИИ (Pearson):") +print(correlation_matrix.round(3)) + +print("\nЧАСТНЫЕ КОРРЕЛЯЦИИ:") +print(partial_corr_df.round(3)) + +print("\nИНТЕРПРЕТАЦИЯ:") +print("- Положительные значения: прямая связь между признаками") +print("- Отрицательные значения: обратная связь между признаками") +print("- Близкие к 1 или -1: сильная связь") +print("- Близкие к 0: слабая связь") \ No newline at end of file diff --git a/ИАД/lr2/task2_HW.py b/ИАД/lr2/task2_HW.py new file mode 100644 index 0000000..93530e0 --- /dev/null +++ b/ИАД/lr2/task2_HW.py @@ -0,0 +1,101 @@ +import numpy as np +from matplotlib import pyplot as plt +from scipy.cluster.hierarchy import dendrogram +import pandas as pd +from sklearn.cluster import AgglomerativeClustering +from scipy.spatial import distance + +# Загрузка датасета MLB +mlb_data = pd.read_csv('SOCR_Data_MLB_HeightsWeights.txt', sep='\t') +mlb_data.columns = ['Name', 'Team', 'Position', 'Height', 'Weight', 'Age'] + +# Создаем DataFrame аналогично ирисам +mlb_pd = pd.DataFrame(data=np.c_[mlb_data[['Height', 'Weight', 'Age']]], + columns=['Height (inches)', 'Weight (pounds)', 'Age (years)']) + +# Выбираем данные для кластеризации +data = mlb_pd[['Height (inches)', 'Weight (pounds)', 'Age (years)']].to_numpy() + +# расчет матрицы расстояний Чебышева +distance_matrix = np.zeros((len(data), len(data))) +for i in range(len(data)): + for j in range(i+1, len(data)): + distance_matrix[i][j] = distance.chebyshev(data[i], data[j]) + distance_matrix[j][i] = distance_matrix[i][j] + +def plot_dendrogram(model, **kwargs): + # Create linkage matrix and then plot the dendrogram + + # create the counts of samples under each node + counts = np.zeros(model.children_.shape[0]) + n_samples = len(model.labels_) + for i, merge in enumerate(model.children_): + current_count = 0 + for child_idx in merge: + if child_idx < n_samples: + current_count += 1 # leaf node + else: + current_count += counts[child_idx - n_samples] + counts[i] = current_count + + linkage_matrix = np.column_stack( + [model.children_, model.distances_, counts] + ).astype(float) + + # Plot the corresponding dendrogram + dendrogram(linkage_matrix, **kwargs) + +# setting distance_threshold=0 ensures we compute the full tree. +metric = 'precomputed' +linkage = "single" +model = AgglomerativeClustering(compute_distances=True, metric=metric, linkage=linkage) + +model = model.fit(distance_matrix) +print("Labels:", model.labels_) +print("Number of clusters:", len(np.unique(model.labels_))) + +# Визуализация дендрограммы +plt.figure(figsize=(12, 6)) +plt.title('Hierarchical Clustering Dendrogram - MLB Data \n metric="{}", linkage="{}"'.format(metric, linkage)) +# plot the top three levels of the dendrogram +plot_dendrogram(model, truncate_mode="level", p=3) +plt.xlabel("Number of points in node") +plt.ylabel("Distance") +plt.show() + +# 3D визуализация кластеров +fig1 = plt.figure(figsize=(12, 8)) +ax = plt.axes(projection='3d') + +scatter = ax.scatter3D(mlb_pd['Height (inches)'], + mlb_pd['Weight (pounds)'], + mlb_pd['Age (years)'], + c=model.labels_, + cmap='tab10', + s=50, + alpha=0.7) + +ax.set_title('Agglomerative Clustering - MLB Players \n metric="{}", linkage="{}"'.format(metric, linkage)) +ax.set_xlabel('Height (inches)') +ax.set_ylabel('Weight (pounds)') +ax.set_zlabel('Age (years)') + +# Добавляем цветовую легенду +plt.colorbar(scatter, ax=ax, label='Cluster') + +plt.show() + +# Дополнительная информация о кластерах +print("\nИнформация о кластерах:") +for cluster_id in np.unique(model.labels_): + cluster_data = mlb_pd[model.labels_ == cluster_id] + print(f"Cluster {cluster_id}: {len(cluster_data)} players") + print(f" Средний рост: {cluster_data['Height (inches)'].mean():.1f} inches") + print(f" Средний вес: {cluster_data['Weight (pounds)'].mean():.1f} pounds") + print(f" Средний возраст: {cluster_data['Age (years)'].mean():.1f} years") + print() + +print("Распределение позиций по кластерам:") +mlb_data_with_clusters = mlb_data.copy() +mlb_data_with_clusters['Cluster'] = model.labels_ + diff --git a/ИАД/lr2/task2_n_HW.py b/ИАД/lr2/task2_n_HW.py new file mode 100644 index 0000000..96ae8c9 --- /dev/null +++ b/ИАД/lr2/task2_n_HW.py @@ -0,0 +1,206 @@ +import numpy as np +import matplotlib.pyplot as plt +import pandas as pd +from scipy.cluster.hierarchy import dendrogram, linkage +from sklearn.preprocessing import StandardScaler +from scipy.spatial import distance + +# Загрузка датасета MLB +mlb_data = pd.read_csv('SOCR_Data_MLB_HeightsWeights.txt', sep='\t') +mlb_data.columns = ['Name', 'Team', 'Position', 'Height', 'Weight', 'Age'] + +# Создаем DataFrame +mlb_pd = pd.DataFrame(data=np.c_[mlb_data[['Height', 'Weight', 'Age']]], + columns=['Height (inches)', 'Weight (pounds)', 'Age (years)']) + +# Стандартизация данных +scaler = StandardScaler() +data_scaled = scaler.fit_transform(mlb_pd[['Height (inches)', 'Weight (pounds)', 'Age (years)']]) + + +# Функция для вычисления матрицы расстояний +def calculate_distance_matrix(data, metric): + n_samples = len(data) + distance_matrix = np.zeros((n_samples, n_samples)) + + for i in range(n_samples): + for j in range(i + 1, n_samples): + if metric == 'euclidean': + dist = distance.euclidean(data[i], data[j]) + elif metric == 'cityblock': + dist = distance.cityblock(data[i], data[j]) + elif metric == 'chebyshev': + dist = distance.chebyshev(data[i], data[j]) + elif metric == 'cosine': + dist = distance.cosine(data[i], data[j]) + + distance_matrix[i][j] = dist + distance_matrix[j][i] = dist + + return distance_matrix + + +# Список комбинаций для исследования +combinations = [ + # Евклидово расстояние + {'metric': 'euclidean', 'linkage': 'single', 'name': 'Евклидово расстояние, одиночная связь'}, + {'metric': 'euclidean', 'linkage': 'complete', 'name': 'Евклидово расстояние, полная связь'}, + {'metric': 'euclidean', 'linkage': 'average', 'name': 'Евклидово расстояние, средняя связь'}, + {'metric': 'euclidean', 'linkage': 'ward', 'name': 'Евклидово расстояние, связь Ward'}, + + # Манхэттенское расстояние + {'metric': 'cityblock', 'linkage': 'single', 'name': 'Манхэттенское расстояние, одиночная связь'}, + {'metric': 'cityblock', 'linkage': 'average', 'name': 'Манхэттенское расстояние, средняя связь'}, + {'metric': 'cityblock', 'linkage': 'complete', 'name': 'Манхэттенское расстояние, полная связь'}, + + # Косинусное расстояние + {'metric': 'cosine', 'linkage': 'single', 'name': 'Косинусное расстояние, одиночная связь'}, + + # Расстояние Чебышева + {'metric': 'chebyshev', 'linkage': 'single', 'name': 'Расстояние Чебышева, одиночная связь'}, + {'metric': 'euclidean', 'linkage': 'centroid', 'name': 'Евклидово расстояние, центроидная связь'} +] + +# Построение отдельных дендрограмм +for i, combo in enumerate(combinations, 1): + try: + # Вычисляем матрицу расстояний + distance_matrix = calculate_distance_matrix(data_scaled, combo['metric']) + + # Создаем linkage matrix + Z = linkage(distance_matrix, method=combo['linkage']) + + # Создаем отдельную фигуру для каждой дендрограммы + plt.figure(figsize=(12, 8)) + + # Строим дендрограмму + dendrogram(Z, + truncate_mode='lastp', + p=12, + show_leaf_counts=True, + leaf_rotation=90) + + plt.title(f'Дендрограмма: {combo["name"]}', fontsize=14, pad=20) + plt.xlabel('Количество точек в узле', fontsize=12) + plt.ylabel('Расстояние', fontsize=12) + plt.grid(True, alpha=0.3) + + plt.tight_layout() + plt.show() + + print(f"{i}. {combo['name']} - Успешно построена") + + except Exception as e: + print(f"{i}. {combo['name']} - Ошибка: {e}") + +# 3D ВИЗУАЛИЗАЦИИ ДЛЯ КАЖДОЙ КОМБИНАЦИИ +print("\n" + "=" * 80) +print("3D ВИЗУАЛИЗАЦИЯ КЛАСТЕРОВ ДЛЯ КАЖДОЙ КОМБИНАЦИИ") +print("=" * 80) + +for i, combo in enumerate(combinations, 1): + try: + # Вычисляем матрицу расстояний + distance_matrix = calculate_distance_matrix(data_scaled, combo['metric']) + + # Создаем linkage matrix + Z = linkage(distance_matrix, method=combo['linkage']) + + # Определяем кластеры (берем 3 кластера для визуализации) + from scipy.cluster.hierarchy import fcluster + + labels = fcluster(Z, t=3, criterion='maxclust') + + # Создаем отдельную 3D визуализацию + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + scatter = ax.scatter3D(mlb_pd['Height (inches)'], + mlb_pd['Weight (pounds)'], + mlb_pd['Age (years)'], + c=labels, + cmap='tab10', + s=50, + alpha=0.7) + + ax.set_title(f'3D Визуализация: {combo["name"]}', fontsize=14, pad=20) + ax.set_xlabel('Height (inches)', fontsize=12) + ax.set_ylabel('Weight (pounds)', fontsize=12) + ax.set_zlabel('Age (years)', fontsize=12) + + plt.colorbar(scatter, ax=ax, label='Cluster') + plt.tight_layout() + plt.show() + + # Выводим информацию о кластерах + print(f"\n{i}. {combo['name']}:") + for cluster_id in np.unique(labels): + cluster_data = mlb_pd[labels == cluster_id] + print(f" Кластер {cluster_id}: {len(cluster_data)} игроков") + print( + f" Рост: {cluster_data['Height (inches)'].mean():.1f} ± {cluster_data['Height (inches)'].std():.1f}") + print( + f" Вес: {cluster_data['Weight (pounds)'].mean():.1f} ± {cluster_data['Weight (pounds)'].std():.1f}") + print(f" Возраст: {cluster_data['Age (years)'].mean():.1f} ± {cluster_data['Age (years)'].std():.1f}") + + except Exception as e: + print(f"{i}. Ошибка в 3D визуализации для {combo['name']}: {e}") + +# АНАЛИЗ РАССТОЯНИЙ ОБЪЕДИНЕНИЯ ДЛЯ ОПРЕДЕЛЕНИЯ ОПТИМАЛЬНОГО ЧИСЛА КЛАСТЕРОВ +print("\n" + "=" * 80) +print("АНАЛИЗ ОПТИМАЛЬНОГО ЧИСЛА КЛАСТЕРОВ") +print("=" * 80) + +# Анализируем лучшие комбинации +best_combinations = [ + {'metric': 'euclidean', 'linkage': 'ward', 'name': 'Евклидово расстояние, связь Ward'}, + {'metric': 'euclidean', 'linkage': 'average', 'name': 'Евклидово расстояние, средняя связь'}, + {'metric': 'cityblock', 'linkage': 'average', 'name': 'Манхэттенское расстояние, средняя связь'} +] + +for i, combo in enumerate(best_combinations, 1): + try: + # Вычисляем матрицу расстояний + distance_matrix = calculate_distance_matrix(data_scaled, combo['metric']) + Z = linkage(distance_matrix, method=combo['linkage']) + + # Анализ расстояний объединения + last_merges = Z[-15:, 2] # последние 15 объединений + distances_diff = np.diff(last_merges[::-1]) + + # Находим оптимальное число кластеров + optimal_idx = np.argmax(distances_diff) + 2 + optimal_clusters = min(optimal_idx, 8) + + # Создаем отдельную фигуру для анализа + plt.figure(figsize=(10, 6)) + + plt.plot(range(len(last_merges)), last_merges[::-1], 'bo-', linewidth=2, markersize=6) + plt.axvline(x=optimal_idx - 1, color='red', linestyle='--', + label=f'Оптимально: {optimal_clusters} кластеров') + plt.title(f'Анализ расстояний объединения: {combo["name"]}\nОптимальное число кластеров: {optimal_clusters}') + plt.xlabel('Шаг объединения') + plt.ylabel('Расстояние') + plt.legend() + plt.grid(True, alpha=0.3) + + plt.tight_layout() + plt.show() + + print(f"{i}. {combo['name']}: оптимальное число кластеров = {optimal_clusters}") + print(f" Наибольший скачок расстояния: {distances_diff[optimal_idx - 2]:.3f}") + + except Exception as e: + print(f"{i}. Ошибка в анализе для {combo['name']}: {e}") + +# ИТОГОВЫЙ ВЫВОД +print("\n" + "=" * 80) +print("ИТОГОВЫЕ ВЫВОДЫ") +print("=" * 80) +print("1. Все 10 дендрограмм построены по отдельности") +print("2. Для каждой комбинации создана отдельная 3D визуализация") +print("3. Метод Ward обычно дает наиболее сбалансированные кластеры") +print("4. Одиночная связь может создавать вытянутые 'цепочки' кластеров") +print("5. Оптимальное число кластеров определяется по наибольшему скачку в расстояниях объединения") +print("6. Для MLB данных оптимально 3-5 кластеров, соответствующих различным физическим типам игроков") +print("7. Разные метрики расстояния показывают различные аспекты сходства между игроками") \ No newline at end of file diff --git a/ИАД/lr2/task3_HW.py b/ИАД/lr2/task3_HW.py new file mode 100644 index 0000000..3f0f7f1 --- /dev/null +++ b/ИАД/lr2/task3_HW.py @@ -0,0 +1,32 @@ +import numpy as np +from matplotlib import pyplot as plt +import pandas as pd +from sklearn.decomposition import PCA + +# Загрузка датасета MLB +mlb_data = pd.read_csv('SOCR_Data_MLB_HeightsWeights.txt', sep='\t') +mlb_data.columns = ['Name', 'Team', 'Position', 'Height', 'Weight', 'Age'] + +# Создаем DataFrame аналогично ирисам +mlb_pd = pd.DataFrame(data=np.c_[mlb_data[['Height', 'Weight', 'Age']]], + columns=['Height (inches)', 'Weight (pounds)', 'Age (years)']) + +# Выбираем данные для PCA +data = mlb_pd[['Height (inches)', 'Weight (pounds)', 'Age (years)']].to_numpy() + +# Применяем PCA +pca = PCA(n_components=2) +pca.fit(data) +principalComponents = pca.fit_transform(data) + +# вывод объясненной дисперсии +print("Объясненная дисперсия каждой компоненты:", pca.explained_variance_ratio_) +print("Суммарная объясненная дисперсия:", sum(pca.explained_variance_ratio_)) + +pc_df = pd.DataFrame(data=principalComponents, columns=['PC1', 'PC2']) + +plt.scatter(pc_df['PC1'], pc_df['PC2']) +plt.xlabel('PC1') +plt.ylabel('PC2') +plt.title("Projection on First Two Principal Components - MLB Data") +plt.show() \ No newline at end of file diff --git a/ИАД/lr2/task4_HW.py b/ИАД/lr2/task4_HW.py new file mode 100644 index 0000000..a463b28 --- /dev/null +++ b/ИАД/lr2/task4_HW.py @@ -0,0 +1,43 @@ +import numpy as np +import matplotlib.pyplot as plt +import pandas as pd +from sklearn.manifold import TSNE +from sklearn.cluster import KMeans +from sklearn.metrics import silhouette_score + +# Загрузка датасета MLB +mlb_data = pd.read_csv('SOCR_Data_MLB_HeightsWeights.txt', sep='\t') +mlb_data.columns = ['Name', 'Team', 'Position', 'Height', 'Weight', 'Age'] + +mlb_pd = pd.DataFrame(data=np.c_[mlb_data[['Height', 'Weight', 'Age']]], + columns=['Height (inches)', 'Weight (pounds)', 'Age (years)']) + +data = mlb_pd[['Height (inches)', 'Weight (pounds)', 'Age (years)']].to_numpy() + +# Лучшие перплексии для анализа кластеров +best_perplexities = [10, 30, 60, 90] + +for perplexity in best_perplexities: + # t-SNE + data_embedded = TSNE(n_components=2, learning_rate='auto', init='random', + perplexity=perplexity, random_state=42).fit_transform(data) + + # Кластеризация K-means + kmeans = KMeans(n_clusters=3, random_state=42, n_init=10) + labels = kmeans.fit_predict(data_embedded) + + # Silhouette Score + sil_score = silhouette_score(data_embedded, labels) + + # Визуализация + plt.figure(figsize=(8, 6)) + scatter = plt.scatter(data_embedded[:, 0], data_embedded[:, 1], + c=labels, cmap='tab10', alpha=0.7, s=40) + plt.title(f't-SNE + K-means\nPerplexity = {perplexity}\nSilhouette: {sil_score:.3f}') + plt.xlabel('t-SNE Component 1') + plt.ylabel('t-SNE Component 2') + plt.colorbar(scatter, label='Cluster') + plt.grid(True, alpha=0.3) + plt.show() + + print(f"Perplexity {perplexity}: {len(np.unique(labels))} кластеров, Silhouette = {sil_score:.3f}") \ No newline at end of file diff --git a/ИАД/lr2/task6_HW.py b/ИАД/lr2/task6_HW.py new file mode 100644 index 0000000..eba0e6b --- /dev/null +++ b/ИАД/lr2/task6_HW.py @@ -0,0 +1,243 @@ +import numpy as np +import matplotlib.pyplot as plt +import pandas as pd +from sklearn.cluster import KMeans +from sklearn.preprocessing import StandardScaler + +# Загрузка датасета MLB +mlb_data = pd.read_csv('SOCR_Data_MLB_HeightsWeights.txt', sep='\t') +mlb_data.columns = ['Name', 'Team', 'Position', 'Height', 'Weight', 'Age'] + +# Создаем DataFrame +mlb_pd = pd.DataFrame(data=np.c_[mlb_data[['Height', 'Weight', 'Age']]], + columns=['Height (inches)', 'Weight (pounds)', 'Age (years)']) + +# Стандартизация данных +scaler = StandardScaler() +data_scaled = scaler.fit_transform(mlb_pd[['Height (inches)', 'Weight (pounds)', 'Age (years)']]) + +# Значения K для кластеризации +k_values = [2, 3, 4] + +print("=" * 60) +print("НЕИЕРАРХИЧЕСКАЯ КЛАСТЕРИЗАЦИЯ - МЕТОД K-СРЕДНИХ") +print("=" * 60) + +for k in k_values: + # Применяем K-means + kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) + labels = kmeans.fit_predict(data_scaled) + + # Создаем отдельную 3D визуализацию для каждого K + fig = plt.figure(figsize=(12, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Цвета для кластеров + colors = ['red', 'blue', 'green', 'orange', 'purple'] + + # Визуализация каждого кластера + for cluster_id in range(k): + cluster_data = mlb_pd[labels == cluster_id] + ax.scatter3D(cluster_data['Height (inches)'], + cluster_data['Weight (pounds)'], + cluster_data['Age (years)'], + c=colors[cluster_id], + label=f'Cluster {cluster_id}', + s=50, + alpha=0.7, + edgecolors='black', + linewidth=0.5) + + # Добавляем центроиды + centers_original = scaler.inverse_transform(kmeans.cluster_centers_) + ax.scatter3D(centers_original[:, 0], + centers_original[:, 1], + centers_original[:, 2], + c='black', + marker='X', + s=200, + label='Centroids', + edgecolors='white', + linewidth=2) + + ax.set_title(f'K-means Clustering\nK = {k} clusters', fontsize=16, pad=20) + ax.set_xlabel('Height (inches)', fontsize=12, labelpad=10) + ax.set_ylabel('Weight (pounds)', fontsize=12, labelpad=10) + ax.set_zlabel('Age (years)', fontsize=12, labelpad=10) + + ax.legend() + plt.tight_layout() + plt.show() + + # Анализ кластеров + print(f"\nK = {k}:") + mlb_pd_temp = mlb_pd.copy() + mlb_pd_temp['Cluster'] = labels + + for cluster_id in range(k): + cluster_data = mlb_pd_temp[mlb_pd_temp['Cluster'] == cluster_id] + center = centers_original[cluster_id] + + print(f" Cluster {cluster_id} ({len(cluster_data)} players):") + print(f" Center: Height={center[0]:.1f}\", Weight={center[1]:.1f}lbs, Age={center[2]:.1f}yr") + print( + f" Stats: Height={cluster_data['Height (inches)'].mean():.1f}±{cluster_data['Height (inches)'].std():.1f}\"") + print( + f" Weight={cluster_data['Weight (pounds)'].mean():.1f}±{cluster_data['Weight (pounds)'].std():.1f}lbs") + print(f" Age={cluster_data['Age (years)'].mean():.1f}±{cluster_data['Age (years)'].std():.1f}yr") + +# СРАВНИТЕЛЬНЫЙ АНАЛИЗ КАЧЕСТВА КЛАСТЕРИЗАЦИИ +print("\n" + "=" * 60) +print("СРАВНИТЕЛЬНЫЙ АНАЛИЗ КАЧЕСТВА КЛАСТЕРИЗАЦИИ") +print("=" * 60) + +from sklearn.metrics import silhouette_score, calinski_harabasz_score + +results = [] +for k in k_values: + kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) + labels = kmeans.fit_predict(data_scaled) + + sil_score = silhouette_score(data_scaled, labels) + ch_score = calinski_harabasz_score(data_scaled, labels) + + results.append({ + 'K': k, + 'Silhouette': sil_score, + 'Calinski-Harabasz': ch_score, + 'Inertia': kmeans.inertia_ + }) + + print(f"K = {k}:") + print(f" Silhouette Score: {sil_score:.3f}") + print(f" Calinski-Harabasz Score: {ch_score:.1f}") + print(f" Within-Cluster Sum of Squares: {kmeans.inertia_:.1f}") + +# ВИЗУАЛИЗАЦИЯ С РАЗНЫМИ УГЛАМИ ОБЗОРА +print("\n" + "=" * 60) +print("3D ВИЗУАЛИЗАЦИЯ С РАЗНЫМИ УГЛАМИ ОБЗОРА") +print("=" * 60) + +# Выбираем оптимальное K (обычно 3 для MLB данных) +optimal_k = 3 +kmeans_optimal = KMeans(n_clusters=optimal_k, random_state=42, n_init=10) +labels_optimal = kmeans_optimal.fit_predict(data_scaled) +centers_optimal = scaler.inverse_transform(kmeans_optimal.cluster_centers_) + +# Разные углы обзора +view_angles = [ + (30, 45), # стандартный вид + (0, 0), # вид сверху + (90, 0), # вид сбоку + (30, -45) # вид с другой стороны +] + +for i, (elev, azim) in enumerate(view_angles): + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + colors = ['red', 'blue', 'green'] + + for cluster_id in range(optimal_k): + cluster_data = mlb_pd[labels_optimal == cluster_id] + ax.scatter3D(cluster_data['Height (inches)'], + cluster_data['Weight (pounds)'], + cluster_data['Age (years)'], + c=colors[cluster_id], + label=f'Cluster {cluster_id}', + s=50, + alpha=0.7) + + # Центроиды + ax.scatter3D(centers_optimal[:, 0], + centers_optimal[:, 1], + centers_optimal[:, 2], + c='black', + marker='X', + s=200, + label='Centroids') + + ax.view_init(elev=elev, azim=azim) + ax.set_title(f'K-means (K=3) - View {i + 1}\nElevation: {elev}°, Azimuth: {azim}°', fontsize=14) + ax.set_xlabel('Height (inches)') + ax.set_ylabel('Weight (pounds)') + ax.set_zlabel('Age (years)') + ax.legend() + + plt.tight_layout() + plt.show() + +# ФИНАЛЬНАЯ ВИЗУАЛИЗАЦИЯ С АНАЛИЗОМ ПОЗИЦИЙ +print("\n" + "=" * 60) +print("ФИНАЛЬНЫЙ АНАЛИЗ - РАСПРЕДЕЛЕНИЕ ПОЗИЦИЙ ПО КЛАСТЕРАМ") +print("=" * 60) + +# Анализ для K=3 (оптимальное значение) +mlb_data_with_clusters = mlb_data.copy() +mlb_data_with_clusters['Cluster'] = labels_optimal + + +# Группируем позиции по основным категориям +def categorize_position(pos): + if 'Pitcher' in pos: + return 'Pitcher' + elif 'Catcher' in pos: + return 'Catcher' + elif 'Baseman' in pos: + return 'Infielder' + elif 'Outfielder' in pos: + return 'Outfielder' + else: + return 'Other' + + +mlb_data_with_clusters['Position_Category'] = mlb_data_with_clusters['Position'].apply(categorize_position) + +print("\nРаспределение позиций по кластерам (K=3):") +cluster_position = mlb_data_with_clusters.groupby(['Cluster', 'Position_Category']).size().unstack(fill_value=0) +print(cluster_position) + +# Визуализация с интерпретацией кластеров +fig = plt.figure(figsize=(14, 10)) +ax = fig.add_subplot(111, projection='3d') + +colors = ['red', 'blue', 'green'] +cluster_names = ['Low Height/Weight', 'Medium Build', 'High Height/Weight'] + +for cluster_id in range(optimal_k): + cluster_data = mlb_pd[labels_optimal == cluster_id] + ax.scatter3D(cluster_data['Height (inches)'], + cluster_data['Weight (pounds)'], + cluster_data['Age (years)'], + c=colors[cluster_id], + label=f'{cluster_names[cluster_id]} ({len(cluster_data)} players)', + s=60, + alpha=0.8) + +# Центроиды +ax.scatter3D(centers_optimal[:, 0], + centers_optimal[:, 1], + centers_optimal[:, 2], + c='black', + marker='X', + s=300, + label='Centroids', + edgecolors='white', + linewidth=3) + +ax.set_title('FINAL: K-means Clustering of MLB Players\nK = 3 (Optimal)', fontsize=16, pad=20) +ax.set_xlabel('Height (inches)\n← Shorter Taller →', fontsize=12, labelpad=15) +ax.set_ylabel('Weight (pounds)\n← Lighter Heavier →', fontsize=12, labelpad=15) +ax.set_zlabel('Age (years)\n← Younger Older →', fontsize=12, labelpad=15) + +ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left') +plt.tight_layout() +plt.show() + +print("\n" + "=" * 60) +print("ИТОГОВЫЕ ВЫВОДЫ") +print("=" * 60) +print("1. K=2: Два крупных кластера (общее разделение по физическим параметрам)") +print("2. K=3: Оптимальное разделение (низкие/средние/высокие игроки)") +print("3. K=4: Более детальное разделение, но может быть избыточным") +print("4. Рекомендуется K=3 для MLB данных") \ No newline at end of file