Родитель
d5b4146974
Сommit
32b7569f4c
@ -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: слабая связь")
|
||||
@ -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_
|
||||
|
||||
@ -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. Разные метрики расстояния показывают различные аспекты сходства между игроками")
|
||||
@ -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()
|
||||
@ -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}")
|
||||
@ -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 данных")
|
||||
Загрузка…
Ссылка в новой задаче