"""
Module: Support Vector Machine (SVM)
Catégorie : Apprentissage supervisé - Classification
Difficulté : Intermédiaire

Généré depuis la plateforme ML Formation
"""

# Imports
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, mean_squared_error, r2_score

# Charger le dataset
df = pd.read_csv('binary_classification.csv')

# Explorer les données
# Type: Code exécutable
print("=" * 70)
print("   EXPLORATION DES DONNEES DE CLASSIFICATION")
print("   Pour l'entrainement du SVM")
print("=" * 70)

# =================================================================
# 1. APERCU DES DONNEES
# =================================================================
print("\n" + "-" * 40)
print("1. APERCU DU DATASET")
print("-" * 40)
print("""
Ce dataset contient des exemples pour un probleme de classification binaire.
Le SVM va trouver l'hyperplan qui separe le mieux les deux classes.
""")
display(df.head(10), title="Dataset de Classification")

# =================================================================
# 2. DISTRIBUTION DES CLASSES
# =================================================================
print("\n" + "-" * 40)
print("2. DISTRIBUTION DES CLASSES")
print("-" * 40)

class_counts = df['label'].value_counts().sort_index()
total = len(df)

print(f"""
Classe 0 : {class_counts[0]:3d} echantillons ({class_counts[0]/total*100:.1f}%)
Classe 1 : {class_counts[1]:3d} echantillons ({class_counts[1]/total*100:.1f}%)

Total    : {total} echantillons
""")

# Verification de l'equilibre
ratio = min(class_counts) / max(class_counts)
if ratio > 0.8:
    print("  [OK] Classes bien equilibrees!")
    print("       Le SVM ne sera pas biaise.")
elif ratio > 0.5:
    print("  [ATTENTION] Leger desequilibre.")
    print("              Considerer class_weight='balanced'.")
else:
    print("  [ATTENTION] Desequilibre significatif!")
    print("              Utiliser class_weight='balanced' recommande.")

# =================================================================
# 3. STATISTIQUES PAR CLASSE
# =================================================================
print("\n" + "-" * 40)
print("3. STATISTIQUES PAR CLASSE")
print("-" * 40)
print("""
Comparons les moyennes des features entre les deux classes.
Si elles sont differentes, le SVM pourra les separer facilement.
""")
display(df.groupby('label').mean().round(3), title="Moyennes par classe")

# Interpreter les differences
mean_class0 = df[df['label'] == 0][['feature1', 'feature2']].mean()
mean_class1 = df[df['label'] == 1][['feature1', 'feature2']].mean()

print(f"""
DIFFERENCES ENTRE CLASSES:

Feature 1:
  - Classe 0: moyenne = {mean_class0['feature1']:.3f}
  - Classe 1: moyenne = {mean_class1['feature1']:.3f}
  - Difference: {abs(mean_class0['feature1'] - mean_class1['feature1']):.3f}

Feature 2:
  - Classe 0: moyenne = {mean_class0['feature2']:.3f}
  - Classe 1: moyenne = {mean_class1['feature2']:.3f}
  - Difference: {abs(mean_class0['feature2'] - mean_class1['feature2']):.3f}

Plus les differences sont grandes, plus la separation sera facile!
""")

# =================================================================
# 4. PLAGE DES VALEURS
# =================================================================
print("\n" + "-" * 40)
print("4. ECHELLE DES FEATURES (IMPORTANT POUR SVM!)")
print("-" * 40)

print(f"""
Feature 1: min = {df['feature1'].min():.3f}, max = {df['feature1'].max():.3f}
Feature 2: min = {df['feature2'].min():.3f}, max = {df['feature2'].max():.3f}

ATTENTION: Le SVM est sensible a l'echelle des features!
Si les echelles sont differentes, la feature avec les plus grandes
valeurs dominera le calcul de distance.

SOLUTION: Toujours normaliser les features avant SVM!
(StandardScaler ou MinMaxScaler)
""")


# Visualiser les données
# Type: Code exécutable
print("=" * 70)
print("   VISUALISATION DES DONNEES")
print("   Comprendre la separabilite des classes")
print("=" * 70)

# =================================================================
# 1. SCATTER PLOT DES CLASSES
# =================================================================
print("\n" + "-" * 40)
print("1. DISTRIBUTION SPATIALE DES CLASSES")
print("-" * 40)
print("""
Ce graphique montre la position de chaque echantillon dans l'espace 2D.
Le SVM va trouver la meilleure frontiere pour separer les couleurs.
""")

plt.figure(figsize=(10, 6))

# Separer par classe
class_0 = df[df['label'] == 0]
class_1 = df[df['label'] == 1]

plt.scatter(class_0['feature1'], class_0['feature2'],
           c='#9B7AC4', label='Classe 0', alpha=0.7, s=60, edgecolors='black')
plt.scatter(class_1['feature1'], class_1['feature2'],
           c='#F7E64D', label='Classe 1', alpha=0.7, s=60, edgecolors='black')

plt.xlabel('Feature 1', fontsize=12)
plt.ylabel('Feature 2', fontsize=12)
plt.title('Donnees de Classification Binaire', fontsize=14, fontweight='bold')
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

# =================================================================
# 2. INTERPRETATION
# =================================================================
print("\n" + "-" * 40)
print("2. INTERPRETATION DU GRAPHIQUE")
print("-" * 40)

# Calculer le chevauchement approximatif
overlap_f1 = max(0, min(class_0['feature1'].max(), class_1['feature1'].max()) -
                   max(class_0['feature1'].min(), class_1['feature1'].min()))
overlap_f2 = max(0, min(class_0['feature2'].max(), class_1['feature2'].max()) -
                   max(class_0['feature2'].min(), class_1['feature2'].min()))

print(f"""
ANALYSE VISUELLE:

Separabilite des classes:
""")

if overlap_f1 < 0.5 and overlap_f2 < 0.5:
    print("  [FACILE] Les classes sont bien separees!")
    print("           Un SVM lineaire devrait suffire.")
elif overlap_f1 < 1 or overlap_f2 < 1:
    print("  [MODERE] Les classes se chevauchent partiellement.")
    print("           Un kernel RBF pourrait etre necessaire.")
else:
    print("  [DIFFICILE] Les classes sont fortement melangees.")
    print("              Essayez differents kernels et regularisation.")

print(f"""
POUR LE SVM:
- Chercher une droite (kernel linear) ou courbe (kernel rbf)
  qui passe entre les deux groupes de couleurs
- Les points les plus proches de la frontiere = Support Vectors
- La marge = espace vide entre la frontiere et les points
""")


# ----------------------------------------------------------------------
# La suite de ce module demande un compte.
# Les cellules de code restantes ne sont pas dans ce fichier.
# ----------------------------------------------------------------------
