Skip to article frontmatterSkip to article content

🎯 But pédagogique

Montrer les biais introduits par certaines situations courantes.

Idées :

  • Effet de domaines géologiques
    Deux champs avec structures différentes → le variogramme global peut être bien structuré

    💡 Note : Nous verrons en classe le moyen de détecter ces domaines (Validations croisée - Krigeage).

  • Erreur de position des forages
    Ajouter une perturbation spatiale → augmenter nugget

    💡 Note : Démontre l’importance des mesures de déviations et d’arpentage

  • Erreur de mesure (À travailler)

    Bruit gaussien sur les données → augmenter nugget aussi

    💡 Note : Démontre l’importance de la théorie de Gy sur la justesse des données

  • Détection des anisotropies (À travailler)

    Champs anisotropes, mais mal analysés → sous-estimation de l’anisotropie

    💡 Piège : Il est difficile de détecter des anisotropies lorsque l’on manque de données ou la tolérance est grande

  • Isotrope versus anisotrope (À travailler)

    Champs anisotropes - identification du variogramme expériemental

    💡 Piège : ce n’est pas parce qu’il y a une anisotropie géométrique que le variogramme expérimental omnidirectionnel n’est pas joli.

  • Géologie du gisement (À travailler)

    Souvent les teneurs se mettent en place avant les évènements tectoniques.

    💡 Note : Dans certains cas simples, on peut déplier le tout pour retrouver les corrélations originales.

  • Effet de pépite apparent (À travailler)

    Est-ce qu’il y un bruit réel dans les données ou est-ce lié à un manque de données ?

    💡 Piège : Avec moins de données, parfois on voit un effet de pépite apparent alors qu’en vrai il n’y en a pas.

  • Présence de données extrêmes (À travailler)

    Les données extrêmes modifient significativement l’analyse des variogrammes expérimanteux

    💡 Piège : Il faut savoir diminuer l’influence de ces valeurs extrêmes (transformation, écrêtage, éliminer, ...)

Source
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import pdist, squareform
import ipywidgets as widgets
from IPython.display import display, clear_output

# Fonction pour simuler un champ gaussien simple 1D et 2D avec covariance sphérique
def simulate_gaussian_field_1D(nx, range_, sill, nugget=0, seed=None):
    np.random.seed(seed)
    x = np.linspace(0, 100, nx)
    coords = x.reshape(-1, 1)  # Coordonnées 1D
    
    # Calcul de la matrice de distances 1D
    dist_matrix = squareform(pdist(coords))
    
    # Covariance sphérique 1D
    cov = np.piecewise(dist_matrix,
                       [dist_matrix <= range_, dist_matrix > range_],
                       [lambda h: sill*(1 - 1.5*(h/range_) + 0.5*(h/range_)**3),
                        0])
    # Ajouter nugget à la diagonale
    cov += nugget * np.eye(len(cov))
    
    # Générer le champ gaussien 1D
    field = np.random.multivariate_normal(np.zeros(len(coords)), cov)
    return coords.flatten(), field

def simulate_gaussian_field(nx, ny, range_, sill, nugget=0, seed=None):
    np.random.seed(seed)
    x = np.linspace(0, 100, nx)
    y = np.linspace(0, 100, ny)
    xx, yy = np.meshgrid(x, y)
    coords = np.vstack([xx.ravel(), yy.ravel()]).T
    
    # Calcul de la matrice de distances
    dist_matrix = squareform(pdist(coords))
    
    # Covariance sphérique
    cov = np.piecewise(dist_matrix,
                       [dist_matrix <= range_, dist_matrix > range_],
                       [lambda h: sill*(1 - 1.5*(h/range_) + 0.5*(h/range_)**3),
                        0])
    # Ajouter nugget à la diagonale
    cov += nugget * np.eye(len(cov))
    
    # Générer le champ gaussien
    field = np.random.multivariate_normal(np.zeros(len(coords)), cov)
    return coords, field

# Fonction calcul variogramme expérimental isotrope
def empirical_variogram(coords, values, max_dist, nbins):
    dists = pdist(coords)
    diffs = pdist(values[:, None])
    bins = np.linspace(0, max_dist, nbins + 1)
    gamma = np.zeros(nbins)
    counts = np.zeros(nbins)
    for i in range(nbins):
        mask = (dists >= bins[i]) & (dists < bins[i+1])
        counts[i] = np.sum(mask)
        if counts[i] > 0:
            gamma[i] = np.mean(0.5 * diffs[mask]**2)
        else:
            gamma[i] = np.nan
    bin_centers = (bins[:-1] + bins[1:]) / 2
    return bin_centers, gamma, counts

# Fonction variogramme selon axe Y uniquement (distances en Y)
def empirical_variogram_Y(coords, values, max_dist, nbins):
    # Distances sur axe Y uniquement
    dists_y = pdist(coords[:, 1].reshape(-1,1))
    diffs = pdist(values[:, None])
    bins = np.linspace(0, max_dist, nbins + 1)
    gamma = np.zeros(nbins)
    counts = np.zeros(nbins)
    for i in range(nbins):
        mask = (dists_y >= bins[i]) & (dists_y < bins[i+1])
        counts[i] = np.sum(mask)
        if counts[i] > 0:
            gamma[i] = np.mean(0.5 * diffs[mask]**2)
        else:
            gamma[i] = np.nan
    bin_centers = (bins[:-1] + bins[1:]) / 2
    return bin_centers, gamma, counts

# Scénarios

def apply_domain_effect(coords, field):
    # Simuler un deuxième champ avec paramètres différents
    coords2, field2 = simulate_gaussian_field(nx=20, ny=20, range_=50, sill=3, nugget=0.5, seed=42)
    # Fusionner les deux champs spatialement côte à côte (domaine 1 à gauche, domaine 2 à droite)
    coords_combined = np.vstack([coords, coords2 + np.array([110,0])]) # décale domaine 2 à droite
    field_combined = np.hstack([field, field2])
    return coords, field, coords2, field2, coords_combined, field_combined

def apply_position_error(coords, field, sigma=2):
    # Ajouter un bruit gaussien sur les coordonnées (erreur position forage)
    noisy_coords = coords + np.random.normal(scale=sigma, size=coords.shape)
    return noisy_coords, field

def apply_measurement_error(coords, field, sigma=0.5):
    # Ajouter un bruit gaussien sur les valeurs (erreur mesure)
    noisy_field = field + np.random.normal(scale=sigma, size=field.shape)
    return coords, noisy_field

def empirical_variogram_1D(coords, values, max_dist, nbins):
    # Calcul simplifié du variogramme 1D
    dists = np.abs(coords[:, None] - coords[None, :])
    diffs = 0.5 * (values[:, None] - values[None, :])**2
    bins = np.linspace(0, max_dist, nbins + 1)
    gamma = np.zeros(nbins)
    counts = np.zeros(nbins)
    for i in range(nbins):
        mask = (dists >= bins[i]) & (dists < bins[i+1])
        counts[i] = np.sum(mask)
        if counts[i] > 0:
            gamma[i] = np.mean(diffs[mask])
        else:
            gamma[i] = np.nan
    bin_centers = (bins[:-1] + bins[1:]) / 2
    return bin_centers, gamma, counts
    
def apply_masking_effect(coords, field):
    # Sous-estimation de l'anisotropie :
    # Créer un champ anisotrope en étirant les coordonnées
    anisotropic_coords = coords.copy()
    anisotropic_coords[:,0] /= 3  # étirement en x (ex: portée réduite)
    return anisotropic_coords, field

def apply_sinusoidal_folding(coords, field, amplitude=20, wavelength=200):
    folded_coords = coords.copy()
    folded_coords[:, 1] += amplitude * np.sin(2 * np.pi * folded_coords[:, 0] / wavelength)
    return folded_coords, field

# Interface interactive

effect_selector = widgets.Dropdown(
    options=["Données extrêmes", "Erreur de localisation des forages", "Pas d'échantillonnage",  "Effet domaines géologiques", "Ré-échantillonnage",  "Erreur de mesure", "Masquage directionnel", "Plissement sinusoïdal"],
    description='Effet:',
    value="Effet domaines géologiques"
)

out = widgets.Output()

def update_plot(change):
    with out:
        clear_output(wait=True)
        nx, ny = 20, 20
        coords, field = simulate_gaussian_field(nx, ny, range_=20, sill=1, nugget=0.1, seed=1)
        
        effect = change['new']
        
        if effect == "Effet domaines géologiques":
            coords1, field1, coords2, field2, _, _ = apply_domain_effect(coords, field)

            # Décalage du domaine 2 vers la droite
            decalage_x = coords1[:, 0].max() + 10
            coords2_shift = coords2.copy()
            coords2_shift[:, 0] += decalage_x

            # Variogrammes empiriques
            max_dist, nbins = 100, 15
            bins1, gamma1, _ = empirical_variogram(coords1, field1, max_dist, nbins)
            bins2, gamma2, _ = empirical_variogram(coords2, field2, max_dist, nbins)
            bins_all, gamma_all, _ = empirical_variogram(
                np.vstack([coords1, coords2_shift]),
                np.hstack([field1, field2]),
                max_dist, nbins
            )

            # --- modèles théoriques sphériques avec nugget ---
            def spherical_model(h, nugget, sill, range_):
                h = np.asarray(h)
                gamma = np.where(
                    h <= range_,
                    nugget + (sill - nugget) * (1.5 * h / range_ - 0.5 * (h / range_)**3),
                    sill
                )
                return gamma

            h_values = np.linspace(0, max_dist, 200)
            model1 = spherical_model(h_values, nugget=0.1, sill=1, range_=20)
            model2 = spherical_model(h_values, nugget=0.5, sill=3, range_=50)

            # --- Figure -----------------
            fig, axs = plt.subplots(1, 2, figsize=(14, 6))

            # Carte des domaines (colormap unique + colorbar partagée)
            all_coords = np.vstack([coords1, coords2_shift])
            all_field = np.hstack([field1, field2])
            norm = plt.Normalize(all_field.min(), all_field.max())

            sc = axs[0].scatter(all_coords[:, 0], all_coords[:, 1],
                        c=all_field, cmap='viridis', norm=norm,
                        s=60, edgecolors='k')

            x_sep = (coords1[:, 0].max() + coords2_shift[:, 0].min()) / 2
            axs[0].axvline(x_sep, color='k', linewidth=2, label='Limite domaines')

            cbar = plt.colorbar(sc, ax=axs[0], fraction=0.046, pad=0.04)
            cbar.set_label('Valeur du champ')

            axs[0].set_title("Deux domaines géologiques côte à côte")
            axs[0].set_xlabel("X")
            axs[0].set_ylabel("Y")
            axs[0].legend(loc='upper right')
            axs[0].grid(True)

            # Variogrammes
            axs[1].plot(bins1, gamma1, 'o-', label='Empirique Domaine 1')
            axs[1].plot(h_values, model1, 'b--', label='Modèle Domaine 1 (sphérique)')

            axs[1].plot(bins2, gamma2, 's-', label='Empirique Domaine 2')
            axs[1].plot(h_values, model2, 'r--', label='Modèle Domaine 2 (sphérique)')

            axs[1].plot(bins_all, gamma_all, 'd-.', color='gray', label='Empirique Global')

            axs[1].set_ylim(0, 3)
            axs[1].set_xlim(0, 100)
            axs[1].set_xlabel('Distance h')
            axs[1].set_ylabel('Variogramme γ(h)')
            axs[1].set_title('Variogrammes – Effet domaines géologiques')
            axs[1].legend()
            axs[1].grid(True)

            plt.tight_layout()
            plt.show()


        elif effect == "Erreur de localisation des forages":
            noisy_coords, noisy_field = apply_position_error(coords, field, sigma=2)
            max_dist = 70
            nbins = 30
            bins, gamma, counts = empirical_variogram(coords, field, max_dist, nbins)
            bins_y, gamma_y, counts_y = empirical_variogram(noisy_coords, field, max_dist, nbins)

            fig, axs = plt.subplots(1, 2, figsize=(14,6))

            # 1. Affichage spatial : positions originales en noir
            axs[0].scatter(coords[:, 0], coords[:, 1], facecolors='none', edgecolors='k', s=50, label='Forages originaux')

            # 2. Forages bruités : couleurs selon la valeur du champ
            sc = axs[0].scatter(noisy_coords[:, 0], noisy_coords[:, 1], c=noisy_field, cmap='viridis', s=60, label='Forages bruités')

            # Colorbar pour les valeurs du champ bruité
            cbar = plt.colorbar(sc, ax=axs[0])
            cbar.set_label('Valeur du champ bruité')

            axs[0].set_title("Erreur de localisation des forages")
            axs[0].set_xlabel("X")
            axs[0].set_ylabel("Y")
            axs[0].set_ylim(-5, 120)
            axs[0].set_xlim(-5, 105)
            axs[0].legend(loc='upper right')
            axs[0].grid(True)

            # 3. Variogrammes : isotrope (original) et axe Y (bruité)
            axs[1].plot(bins, gamma, 'o-', label='Variogramme isotrope (original)')
            axs[1].plot(bins_y, gamma_y, 's-', label='Variogramme isotrope (bruité)')
            axs[1].set_ylim(bottom=0)
            axs[1].set_xlabel('Distance h')
            axs[1].set_ylabel('Variogramme γ(h)')
            axs[1].set_title('Effet d\'erreur de localisation sur le variogramme')
            axs[1].legend(loc='lower right')
            axs[1].grid(True)

            plt.tight_layout()
            plt.show()

            
        elif effect == "Erreur de mesure":
            # Données bruitées
            noisy_coords, noisy_field = apply_measurement_error(coords, field, sigma=0.8)
            max_dist = 70
            nbins = 15

            # Variogrammes
            bins_clean, gamma_clean, _ = empirical_variogram(coords, field, max_dist, nbins)
            bins_noisy, gamma_noisy, _ = empirical_variogram(noisy_coords, noisy_field, max_dist, nbins)

            fig, axs = plt.subplots(1, 2, figsize=(14,6))

            # --------- SPATIAL : montrer le bruit ---------
            # 1. Points noirs = vraies valeurs
            axs[0].scatter(coords[:,0], coords[:,1], c='k', s=20, label='Valeurs vraies')

            # 2. Points colorés = valeurs bruitées
            sc = axs[0].scatter(noisy_coords[:,0], noisy_coords[:,1], c=noisy_field, cmap='viridis', s=80, edgecolors='none', label='Valeurs bruitées')

            # 3. Erreurs : flèches ou lignes entre vraie valeur et bruitée
            for (x0, y0), (x1, y1) in zip(coords, noisy_coords):
                axs[0].plot([x0, x1], [y0, y1], color='gray', lw=0.8, alpha=0.5)

            axs[0].set_title("Erreur de mesure (visualisation des erreurs)")
            axs[0].set_xlabel("X")
            axs[0].set_ylabel("Y")
            axs[0].legend()
            axs[0].grid(True)
            plt.colorbar(sc, ax=axs[0], label='Valeur bruitée')

            # --------- VARIOGRAMMES ---------
            axs[1].plot(bins_clean, gamma_clean, 'o-', label='Données originales')
            axs[1].plot(bins_noisy, gamma_noisy, 's--', label='Données bruitées')
            axs[1].set_xlabel('Distance h')
            axs[1].set_ylabel('Variogramme γ(h)')
            axs[1].set_ylim(0, 2)
            axs[1].set_title('Variogrammes - Effet de l\'erreur de mesure')
            axs[1].legend()
            axs[1].grid(True)

            plt.tight_layout()
            plt.show()

        
        elif effect == "Masquage directionnel":
            anisotropic_coords, anisotropic_field = apply_masking_effect(coords, field)
            max_dist = 70
            nbins = 15
            bins, gamma, counts = empirical_variogram(coords, field, max_dist, nbins)
            bins_aniso, gamma_aniso, counts_aniso = empirical_variogram(anisotropic_coords, anisotropic_field, max_dist, nbins)
            
            fig, axs = plt.subplots(1, 2, figsize=(14,6))
            
            # Graphique spatial (avant/après)
            sc = axs[0].scatter(coords[:,0], coords[:,1], c=field, cmap='viridis', s=80, label='Champ isotrope')
            sc2 = axs[0].scatter(anisotropic_coords[:,0], anisotropic_coords[:,1], c=anisotropic_field, cmap='plasma', s=80, alpha=0.6, label='Champ anisotrope étiré')
            axs[0].set_title("Masquage directionnel (anisotropie sous-estimée)")
            axs[0].set_xlabel("X")
            axs[0].set_ylabel("Y")
            axs[0].legend()
            plt.colorbar(sc, ax=axs[0], label='Valeur champ isotrope')
            
            # Variogrammes
            axs[1].plot(bins, gamma, 'o-', label='Variogramme isotrope')
            axs[1].plot(bins_aniso, gamma_aniso, 's-', label='Variogramme anisotrope étiré')
            axs[1].set_xlabel('Distance h')
            axs[1].set_ylabel('Variogramme γ(h)')
            axs[1].set_title('Variogrammes - Masquage directionnel')
            axs[1].legend()
            axs[1].grid(True)
            
            plt.tight_layout()
            plt.show()

        elif effect == "Plissement sinusoïdal":
            folded_coords, folded_field = apply_sinusoidal_folding(coords, field, amplitude=50, wavelength=20)

            max_dist = 70
            nbins = 15
            bins_orig, gamma_orig, _ = empirical_variogram(coords, field, max_dist, nbins)
            bins_folded, gamma_folded, _ = empirical_variogram(folded_coords, folded_field, max_dist, nbins)

            fig, axs = plt.subplots(1, 2, figsize=(14, 6))

            # 1. Carte du champ original vs plissé
            axs[0].scatter(coords[:, 0], coords[:, 1], facecolors='none', edgecolors='k', s=50, label='Original')
            sc = axs[0].scatter(folded_coords[:, 0], folded_coords[:, 1], c=folded_field, cmap='viridis', s=60, label='Plissé')
            cbar = plt.colorbar(sc, ax=axs[0])
            cbar.set_label("Valeur du champ")
            axs[0].set_title("Plissement sinusoïdal des couches")
            axs[0].set_xlabel("X")
            axs[0].set_ylabel("Y")
            axs[0].legend(loc='upper right')
            axs[0].grid(True)

            # 2. Comparaison des variogrammes
            axs[1].plot(bins_orig, gamma_orig, 'o-', label='Données Déposition')
            axs[1].plot(bins_folded, gamma_folded, 's-', label='Données plissées')
            axs[1].set_ylim(bottom=0)
            axs[1].set_xlabel("Distance h")
            axs[1].set_ylabel("Variogramme γ(h)")
            axs[1].set_title("Effet du plissement sinusoïdal")
            axs[1].legend()
            axs[1].grid(True)

            plt.tight_layout()
            plt.show()
        elif effect == "Données extrêmes":
            # Paramètres de simulation
            nx = 20
            range_ = 10
            sill = 1
            nugget = 0.1
            seed = 1

            # Générer le champ 1D gaussien
            x1d, field1d = simulate_gaussian_field_1D(nx, range_, sill, nugget, seed=seed)

            # --- Extrêmes au centre ---
            extreme_mask_center = (x1d > 45) & (x1d < 52)
            field_center = field1d.copy()
            field_center[extreme_mask_center] += 15  # Ajout de valeurs extrêmes

            var_orig = np.var(field1d)  # Variance originale avant modification
            var_center = np.var(field_center)  # Variance après ajout extrêmes

            # Rescaler pour conserver la même variance globale
            field_center = (field_center - np.mean(field_center)) * np.sqrt(var_orig / var_center)

            # --- Extrêmes sur les bords ---
            extreme_mask_edge = (x1d < 5) 
            field_edge = field1d.copy()
            field_edge[extreme_mask_edge] += 15

            var_edge = np.var(field_edge)  # Variance après ajout extrêmes aux bords
            # Rescaler pour conserver la même variance globale
            field_edge = (field_edge - np.mean(field_edge)) * np.sqrt(var_orig / var_edge)

            # Calcul des variogrammes 1D
            max_dist = 80
            nbins = 10
            bins_base, gamma_base, _ = empirical_variogram_1D(x1d, field1d, max_dist, nbins)
            bins_center, gamma_center, _ = empirical_variogram_1D(x1d, field_center, max_dist, nbins)
            bins_edge, gamma_edge, _ = empirical_variogram_1D(x1d, field_edge, max_dist, nbins)

            # Affichage
            fig, axs = plt.subplots(2, 1, figsize=(7, 7))

            axs[0].plot(x1d, field1d, label="Champ original")
            axs[0].plot(x1d, field_center, label="Extrêmes au centre")
            axs[0].plot(x1d, field_edge, label="Extrêmes sur les bords")
            axs[0].set_title("Champ 1D - Données extrêmes localisées")
            axs[0].legend()
            axs[0].grid(True)

            # Variogrammes - tous sur le même graphique
            axs[1].plot(bins_base, gamma_base, 'o-', label="Variogramme original")
            axs[1].plot(bins_center, gamma_center, 's-', label="Extrêmes au centre")
            axs[1].plot(bins_edge, gamma_edge, 'd-', label="Extrêmes sur les bords")
            axs[1].set_xlabel("Distance h")
            axs[1].set_ylabel("Variogramme γ(h)")
            axs[1].set_title("Comparaison des variogrammes")
            axs[1].legend()
            axs[1].grid(True)

            plt.tight_layout()
            plt.show()

        elif effect == "Pas d'échantillonnage":
            # --- Données par zone ---
            x_A = np.arange(0, 18, 2)  # Zone A : pas de 2 m
            z_A = np.array([4, 4, 5, 6, 6, 7, 6, 5, 4])  # Peu variable

            x_B = np.arange(20, 39)  # Zone B : pas de 1 m
            z_B = np.array([8, 6, 8, 10, 12, 8, 10, 12, 14, 10,
                            8, 6, 12, 8, 10, 10, 8, 10, 0])  # Plus variable

            x_AB = np.concatenate([x_A, x_B])
            z_AB = np.concatenate([z_A, z_B])

            # --- Calcul des variogrammes 1D ---
            max_dist = 12
            nbins = 12
            bins_A, gamma_A, _ = empirical_variogram_1D(x_A, z_A, max_dist, nbins)
            bins_B, gamma_B, _ = empirical_variogram_1D(x_B, z_B, max_dist, nbins)
            bins_AB, gamma_AB, _ = empirical_variogram_1D(x_AB, z_AB, max_dist, nbins)

            # --- Affichage ---
            fig, axs = plt.subplots(2, 1, figsize=(7, 6))

            # Données par zone
            axs[0].plot(x_A, z_A, 'o-', label='Zone A (pas 2 m)')
            axs[0].plot(x_B-20, z_B, 's-', label='Zone B (pas 1 m)')
            axs[0].set_title("Échantillonnage variable par zone")
            axs[0].set_xlabel("Position (m)")
            axs[0].set_ylabel("Valeur")
            axs[0].legend()
            axs[0].grid(True)

            # Variogrammes comparés
            axs[1].plot(bins_A, gamma_A, 'o-', label='Variogramme Zone A')
            axs[1].plot(bins_B, gamma_B, 's-', label='Variogramme Zone B')
            axs[1].plot(bins_AB, gamma_AB, 'd-', label='Variogramme A + B')
            axs[1].set_title("Effet du pas d'échantillonnage sur le variogramme")
            axs[1].set_xlabel("Distance h (m)")
            axs[1].set_ylabel("Variogramme γ(h)")
            axs[1].legend()
            axs[1].grid(True)

            plt.tight_layout()
            plt.show()

        elif effect == "Ré-échantillonnage":
            # ---------------- Simulation ----------------

            nx = ny = 15
            coords, field = simulate_gaussian_field(nx, ny, range_=20, sill=1, nugget=0.1, seed=123)

            # Ré-échantillonnage : ajout de 10 points avec valeurs perturbées (faibles doublons)
            # --- 10 doublons faibles ---
            n_doublons = 10
            indices_extremes = np.argsort(field)[-n_doublons:]
            coords_perturb = coords[indices_extremes] + np.random.normal(scale=0.1, size=(n_doublons, 2))
            fields_perturb = field[indices_extremes] - np.random.uniform(1.5, 3.5, size=n_doublons)
            coords_10 = np.vstack([coords, coords_perturb])
            field_10 = np.hstack([field, fields_perturb])

            # --- 215 points ajoutés (environ 10% des valeurs originales), + les 10 doublons ---
            n_ajouts = 215
            indices_ajouts = np.random.choice(len(coords), n_ajouts, replace=True)
            coords_ajouts = coords[indices_ajouts] + np.random.normal(scale=0.1, size=(n_ajouts, 2))
            valeurs_orig = field[indices_ajouts]
            multiplicateurs = np.random.normal(loc=0.1, scale=0.02, size=n_ajouts)  # ~10% ± 2%
            fields_ajouts = valeurs_orig * (0.9+multiplicateurs)

            # Combine coords originaux + 10 doublons + 245 nouveaux points
            coords_225 = np.vstack([coords, coords_perturb, coords_ajouts])
            field_225 = np.hstack([field, fields_perturb, fields_ajouts])

            # ---------------- Variogrammes ----------------

            max_dist = 50
            nbins = 10

            bins0, gamma0, _ = empirical_variogram(coords, field, max_dist, nbins)
            bins1, gamma1, _ = empirical_variogram(coords_10, field_10, max_dist, nbins)
            bins2, gamma2, _ = empirical_variogram(coords_225, field_225, max_dist, nbins)

            # ---------------- Affichage ----------------

            fig, axs = plt.subplots(1, 2, figsize=(14, 6))

            # Carte des valeurs
            sc = axs[0].scatter(coords[:, 0], coords[:, 1], c=field, cmap='viridis', s=80, edgecolors='k', label='Champ original')
            axs[0].scatter(coords_perturb[:, 0], coords_perturb[:, 1], c=fields_perturb, cmap='coolwarm', marker='x', s=80, label='Doublons (faibles)')
            axs[0].set_title("Ré-échantillonnage des zones riches (10 points)")
            axs[0].legend()
            plt.colorbar(sc, ax=axs[0], label="Valeur du champ")
            axs[0].grid(True)

            # Variogrammes
            axs[1].plot(bins0, gamma0, 'o-', label='Champ original')
            axs[1].plot(bins1, gamma1, 's--', label='+ 10 doublons faibles')
            axs[1].plot(bins2, gamma2, 'd-.', label='+ 225 doublons faibles')
            axs[1].set_xlabel('Distance h')
            axs[1].set_ylabel('Variogramme γ(h)')
            axs[1].set_title("Effet du ré-échantillonnage préférentiel")
            axs[1].legend()
            axs[1].grid(True)

            plt.tight_layout()
            plt.show()            
        else:
            print("Effet non reconnu")

effect_selector.observe(update_plot, names='value')
display(effect_selector, out)

# Initial plot
update_plot({'new': effect_selector.value})



Loading...