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 данных")