"""
Module: Random Forest Regressor
Catégorie : Apprentissage supervisé - Régression
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('housing_simple.csv')

# Explorer les données
# Type: Code exécutable
# Fonction pour formater les nombres au style francais (espace comme separateur)
def fmt(n, decimals=0):
    return f"{n:,.{decimals}f}".replace(",", " ")

print("=" * 70)
print("   EXPLORATION DU DATASET IMMOBILIER")
print("   Pour l'entrainement de notre Random Forest")
print("=" * 70)

# =================================================================
# 1. APERCU DES DONNEES
# =================================================================
print("\n" + "-" * 40)
print("1. APERCU DU DATASET")
print("-" * 40)
print("""
Ce dataset contient des biens immobiliers avec leurs caracteristiques
et prix de vente. Nous allons utiliser Random Forest pour predire
le prix a partir de la surface et du nombre de pieces.
""")
display(df.head(10), title="Dataset Immobilier")

# =================================================================
# 2. DIMENSIONS
# =================================================================
print("\n" + "-" * 40)
print("2. DIMENSIONS DU DATASET")
print("-" * 40)
n_samples = len(df)
n_features = 2
print(f"""
Nombre de biens immobiliers : {n_samples}
Nombre de features          : {n_features} (surface, nb_rooms)
Variable cible              : price (prix en euros)

Ce dataset est adapte pour une foret aleatoire:
- Assez d'echantillons pour le bootstrap sampling
- Features numeriques directement utilisables
""")

# =================================================================
# 3. STATISTIQUES DESCRIPTIVES
# =================================================================
print("\n" + "-" * 40)
print("3. STATISTIQUES DESCRIPTIVES")
print("-" * 40)
print("""
Comprendre la distribution des donnees aide a interpreter
les predictions du modele.
""")
display(df.describe().round(2), title="Statistiques")

# Interpretation
print(f"""
INTERPRETATION:

SURFACE:
  - Moyenne: {df['surface'].mean():.0f} m2
  - Etendue: {df['surface'].min():.0f} a {df['surface'].max():.0f} m2
  - Ecart-type: {df['surface'].std():.0f} m2

NOMBRE DE PIECES:
  - Moyenne: {df['nb_rooms'].mean():.1f} pieces
  - Etendue: {df['nb_rooms'].min():.0f} a {df['nb_rooms'].max():.0f} pieces

PRIX:
  - Moyenne: {fmt(df['price'].mean())} EUR
  - Etendue: {fmt(df['price'].min())} a {fmt(df['price'].max())} EUR
  - Prix median: {fmt(df['price'].median())} EUR
""")

# =================================================================
# 4. CORRELATIONS
# =================================================================
print("\n" + "-" * 40)
print("4. MATRICE DE CORRELATION")
print("-" * 40)
print("""
Les correlations montrent les liens entre variables.
Plus la correlation avec le prix est forte, plus la feature
est potentiellement predictive.
""")
corr_matrix = df.corr().round(3)
display(corr_matrix, title="Correlations")

# Interpreter les correlations
corr_surface = corr_matrix.loc['surface', 'price']
corr_rooms = corr_matrix.loc['nb_rooms', 'price']

print(f"""
INTERPRETATION DES CORRELATIONS:

Surface <-> Prix: {corr_surface:.3f}
  - {'Forte' if abs(corr_surface) > 0.7 else 'Moderee' if abs(corr_surface) > 0.4 else 'Faible'} correlation {'positive' if corr_surface > 0 else 'negative'}
  - {'La surface est un bon predicteur du prix' if abs(corr_surface) > 0.5 else 'Relation moderee'}

Nb_rooms <-> Prix: {corr_rooms:.3f}
  - {'Forte' if abs(corr_rooms) > 0.7 else 'Moderee' if abs(corr_rooms) > 0.4 else 'Faible'} correlation {'positive' if corr_rooms > 0 else 'negative'}

Surface <-> Nb_rooms: {corr_matrix.loc['surface', 'nb_rooms']:.3f}
  - Les grandes surfaces ont generalement plus de pieces
""")

# =================================================================
# CONCLUSION
# =================================================================
print("\n" + "=" * 70)
print("SYNTHESE POUR LE RANDOM FOREST")
print("=" * 70)
print(f"""
- {n_samples} echantillons disponibles (suffisant pour le bootstrap)
- Les deux features sont correlees au prix
- Pas de normalisation necessaire (Random Forest y est insensible)
- Les donnees sont pretes pour l'entrainement!
""")


# Visualiser les relations
# Type: Code exécutable
# Fonction pour formater les nombres au style francais (espace comme separateur)
def fmt(n, decimals=0):
    return f"{n:,.{decimals}f}".replace(",", " ")

print("=" * 70)
print("   VISUALISATION DES RELATIONS")
print("   Comprendre les liens entre features et prix")
print("=" * 70)

# =================================================================
# 1. CREATION DES GRAPHIQUES
# =================================================================
print("\n" + "-" * 40)
print("1. SCATTER PLOTS: FEATURES vs PRIX")
print("-" * 40)
print("""
Ces graphiques montrent comment le prix varie en fonction de chaque feature.
Un Random Forest peut capturer des relations non-lineaires!
""")

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Surface vs Prix
axes[0].scatter(df['surface'], df['price'], alpha=0.6, color='#9B7AC4',
                edgecolors='black', linewidth=0.5)
axes[0].set_xlabel('Surface (m2)', fontsize=12)
axes[0].set_ylabel('Prix (euros)', fontsize=12)
axes[0].set_title('Surface vs Prix', fontsize=14, fontweight='bold')
axes[0].grid(True, alpha=0.3)

# Ajouter une tendance
z = np.polyfit(df['surface'], df['price'], 1)
p = np.poly1d(z)
axes[0].plot(df['surface'].sort_values(), p(df['surface'].sort_values()),
             'r--', linewidth=2, label=f'Tendance lineaire')
axes[0].legend()

# Nb rooms vs Prix
axes[1].scatter(df['nb_rooms'], df['price'], alpha=0.6, color='#C09CF0',
                edgecolors='black', linewidth=0.5)
axes[1].set_xlabel('Nombre de pieces', fontsize=12)
axes[1].set_ylabel('Prix (euros)', fontsize=12)
axes[1].set_title('Nb Pieces vs Prix', fontsize=14, fontweight='bold')
axes[1].grid(True, alpha=0.3)

# Moyenne par nombre de pieces
mean_by_rooms = df.groupby('nb_rooms')['price'].mean()
axes[1].plot(mean_by_rooms.index, mean_by_rooms.values, 'ro-',
             linewidth=2, markersize=8, label='Moyenne par nb pieces')
axes[1].legend()

plt.tight_layout()
plt.show()

# =================================================================
# 2. INTERPRETATION DES GRAPHIQUES
# =================================================================
print("\n" + "-" * 40)
print("2. INTERPRETATION")
print("-" * 40)

# Calculer le prix moyen par m2
prix_m2 = df['price'].mean() / df['surface'].mean()

print(f"""
GRAPHIQUE 1 - Surface vs Prix:
- Relation {'lineaire' if df['surface'].corr(df['price']) > 0.9 else 'globalement lineaire avec dispersion'}
- Prix moyen par m2: {fmt(prix_m2)} EUR/m2
- La surface est le principal determinant du prix

GRAPHIQUE 2 - Nb Pieces vs Prix:
- Tendance croissante avec le nombre de pieces
- Dispersion plus importante (a nb pieces egal, les prix varient)
- Relation moins directe qu'avec la surface

POUR LE RANDOM FOREST:
- Peut capturer les interactions entre surface et nb_rooms
- N'est pas limite aux relations lineaires
- Chaque arbre peut trouver des regles differentes
""")

# =================================================================
# 3. DISTRIBUTION DES PRIX
# =================================================================
print("\n" + "-" * 40)
print("3. DISTRIBUTION DES PRIX")
print("-" * 40)

plt.figure(figsize=(10, 5))
plt.hist(df['price'], bins=20, color='#9B7AC4', edgecolor='black', alpha=0.7)
plt.axvline(df['price'].mean(), color='#F7E64D', linewidth=2,
            linestyle='--', label=f'Moyenne: {fmt(df["price"].mean())} EUR')
plt.axvline(df['price'].median(), color='#27ae60', linewidth=2,
            linestyle='--', label=f'Mediane: {fmt(df["price"].median())} EUR')
plt.xlabel('Prix (euros)', fontsize=12)
plt.ylabel('Nombre de biens', fontsize=12)
plt.title('Distribution des prix immobiliers', fontsize=14, fontweight='bold')
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.show()

print(f"""
La distribution des prix permet de comprendre:
- Le marche couvre une gamme de {fmt(df['price'].min())} a {fmt(df['price'].max())} EUR
- La majorite des biens sont proches de la moyenne
- {'Distribution a peu pres symetrique' if abs(df['price'].mean() - df['price'].median()) < df['price'].std()*0.1 else 'Distribution legerement asymetrique'}
""")


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