W3docs

Distribution Normale des Données

Apprenez la distribution normale (gaussienne) en Python : PDF, CDF, Z-scores, règle empirique, tests de normalité et transformations logarithmiques.

La distribution normale (aussi appelée distribution gaussienne) est la loi de probabilité la plus importante en statistiques et en apprentissage automatique. La comprendre en profondeur — pas seulement reconnaître la forme de sa courbe en cloche, mais savoir comment la mesurer, la tester et l'utiliser en Python — vous donne une base solide pour la préparation des données, la sélection des modèles et l'interprétation des résultats.

Ce chapitre aborde :

  • Ce qu'est la distribution normale et pourquoi sa forme est importante
  • La règle empirique (règle 68–95–99,7) et comment la vérifier
  • La génération de données normalement distribuées avec NumPy
  • L'évaluation de la fonction de densité de probabilité (PDF) et de la fonction de répartition (CDF) avec SciPy
  • Le calcul et l'interprétation des Z-scores
  • Comment tester si vos données suivent une distribution normale
  • Que faire lorsque les données ne sont pas normales

Qu'est-ce que la Distribution Normale ?

Une loi de probabilité décrit la probabilité d'apparition de chaque valeur dans un jeu de données. La distribution normale est une distribution continue, symétrique, en forme de cloche, définie par deux paramètres :

  • Moyenne (μ) — le centre de la cloche ; l'emplacement du pic
  • Écart-type (σ) — la largeur ou l'étroitesse de la cloche ; un σ plus grand éloigne les valeurs du centre

Comme la courbe est symétrique autour de la moyenne, la moyenne, la médiane et le mode sont tous égaux pour une distribution parfaitement normale.

La formule de la fonction de densité de probabilité est :

f(x) = (1 / (σ√(2π))) × e^(−(x−μ)² / (2σ²))

Vous n'avez pas besoin d'appliquer cette formule manuellement — stats.norm de SciPy s'en charge — mais il est utile de savoir que la forme dépend uniquement de μ et σ.

La Règle Empirique (68–95–99,7)

L'une des propriétés les plus pratiques de la distribution normale est la règle empirique, aussi appelée règle 68–95–99,7 :

Plage depuis la moyennePourcentage de valeurs
Dans ±1 écart-type~68%
Dans ±2 écarts-types~95%
Dans ±3 écarts-types~99,7%

Cela signifie que dans un jeu de données normalement distribué, presque toutes les valeurs se situent dans trois écarts-types de la moyenne. Les valeurs au-delà de ±3σ sont véritablement rares — environ 1 observation sur 370.

L'exemple suivant simule 10 000 valeurs issues d'une distribution normale standard (μ=0, σ=1) et vérifie la règle :

import numpy as np

rng = np.random.default_rng(seed=42)
mu, sigma = 0, 1
data = rng.normal(loc=mu, scale=sigma, size=10000)

w1 = np.mean(np.abs(data - mu) < 1 * sigma) * 100
w2 = np.mean(np.abs(data - mu) < 2 * sigma) * 100
w3 = np.mean(np.abs(data - mu) < 3 * sigma) * 100

print(f"Within 1 std: {w1:.1f}%  (expected ~68%)")
print(f"Within 2 std: {w2:.1f}%  (expected ~95%)")
print(f"Within 3 std: {w3:.1f}%  (expected ~99.7%)")

Résultat :

Within 1 std: 67.8%  (expected ~68%)
Within 2 std: 95.4%  (expected ~95%)
Within 3 std: 99.7%  (expected ~99.7%)

La règle empirique est immédiatement utile : si le résultat d'un test est supérieur de plus de deux écarts-types à la moyenne, il se situe dans le top ~2,5% de tous les résultats.

Générer des Données Normalement Distribuées avec NumPy

Le générateur de nombres aléatoires de NumPy produit des échantillons qui approximent une distribution normale. La méthode rng.normal() accepte la moyenne (loc), l'écart-type (scale) et le nombre d'échantillons (size) :

import numpy as np

rng = np.random.default_rng(seed=7)

# Simulate IQ scores: mean=100, std=15
samples = rng.normal(loc=100, scale=15, size=1000)

print(f"Sample mean:  {samples.mean():.1f}")
print(f"Sample std:   {samples.std():.1f}")
print(f"Min:          {samples.min():.1f}")
print(f"Max:          {samples.max():.1f}")
print(f"Sample size:  {len(samples)}")

Résultat :

Sample mean:  98.9
Sample std:   14.1
Min:          51.2
Max:          138.6
Sample size:  1000

La moyenne et l'écart-type de l'échantillon sont proches — mais pas exactement — de 100 et 15, car un échantillon fini présente une variation aléatoire naturelle. Avec 10 000 échantillons, les estimations convergent vers les vrais paramètres.

Pourquoi utiliser une graine ? Passer seed=7 à default_rng rend les résultats reproductibles. Toute personne exécutant le même code obtient les mêmes chiffres, ce qui est essentiel pour le débogage et le partage des résultats. L'interface default_rng (introduite dans NumPy 1.17) est préférable à l'ancienne np.random.normal() car elle évite l'état global partagé.

Évaluer la PDF et la CDF avec SciPy

Fonction de Densité de Probabilité (PDF)

La PDF indique la vraisemblance relative d'une valeur. Une valeur PDF plus élevée en x signifie que x est plus susceptible d'apparaître. Pour une distribution normale, le pic se trouve à la moyenne :

from scipy import stats

mu, sigma = 0, 1

x_values = [-2, -1, 0, 1, 2]
for x in x_values:
    pdf = stats.norm.pdf(x, loc=mu, scale=sigma)
    print(f"  pdf({x:2d}) = {pdf:.4f}")

Résultat :

  pdf(-2) = 0.0540
  pdf(-1) = 0.2420
  pdf( 0) = 0.3989
  pdf( 1) = 0.2420
  pdf( 2) = 0.0540

La PDF est symétrique : pdf(-1) est égal à pdf(1), et pdf(0) est le maximum (le pic de la courbe en cloche). La valeur PDF elle-même n'est pas une probabilité — c'est une densité. Pour obtenir une probabilité, vous intégrez la PDF sur un intervalle, ce que fait la CDF.

Fonction de Répartition (CDF)

La CDF en une valeur x donne la probabilité qu'une observation tirée au hasard soit inférieure ou égale à x. Utilisez stats.norm.cdf() pour répondre à des questions pratiques de probabilité :

from scipy import stats

mu, sigma = 170, 10  # adult heights in cm

# Probability that a randomly chosen person is shorter than 180 cm
p_below_180 = stats.norm.cdf(180, loc=mu, scale=sigma)
print(f"P(height < 180 cm) = {p_below_180:.4f}")

# Probability of being taller than 185 cm
p_above_185 = 1 - stats.norm.cdf(185, loc=mu, scale=sigma)
print(f"P(height > 185 cm) = {p_above_185:.4f}")

# Probability of falling between 160 cm and 180 cm
p_range = stats.norm.cdf(180, loc=mu, scale=sigma) - stats.norm.cdf(160, loc=mu, scale=sigma)
print(f"P(160 < height < 180 cm) = {p_range:.4f}")

Résultat :

P(height < 180 cm) = 0.8413
P(height > 185 cm) = 0.0668
P(160 < height < 180 cm) = 0.6827

Le troisième résultat confirme la règle empirique : l'intervalle μ±σ (160–180 cm) contient environ 68% des observations.

Z-Scores : Standardiser la Distribution

Un Z-score (aussi appelé score standard) mesure de combien d'écarts-types une valeur s'écarte de la moyenne :

Z = (x − μ) / σ

Les Z-scores permettent de comparer des valeurs issues de distributions avec des moyennes et des écarts-types différents sur une échelle commune. Un Z-score de +2.0 signifie qu'une valeur est deux écarts-types au-dessus de la moyenne, quelle que soit l'unité d'origine.

import numpy as np
from scipy import stats

heights = np.array([171.3, 168.7, 176.4, 171.0, 164.6, 173.6, 183.0, 179.5])

# Calculate Z-scores manually
mean = heights.mean()
std  = heights.std(ddof=1)   # ddof=1 for the sample standard deviation
z_manual = (heights - mean) / std
print("Z-scores (manual):", np.round(z_manual, 2))

# Or use scipy directly
z_scipy = stats.zscore(heights, ddof=1)
print("Z-scores (scipy): ", np.round(z_scipy, 2))

Résultat :

Z-scores (manual): [-0.37 -0.81  0.49 -0.42 -1.5   0.01  1.59  1.01]
Z-scores (scipy):  [-0.37 -0.81  0.49 -0.42 -1.5   0.01  1.59  1.01]

Une personne mesurant 183,0 cm a un Z-score de +1.59, ce qui signifie qu'elle se trouve à 1,59 écart-type au-dessus de la moyenne. Dans une distribution normale, cela la place approximativement dans le top 6% de la population.

Quand les Z-scores sont utiles

  • Détection des valeurs aberrantes : les valeurs avec |Z| > 3 sont presque certainement des valeurs aberrantes dans une distribution normale.
  • Mise à l'échelle des caractéristiques : convertir les caractéristiques en Z-scores (moyenne nulle, variance unitaire) s'appelle la standardisation, et est requise pour les algorithmes sensibles à l'amplitude des caractéristiques, tels que SVM et k-plus-proches-voisins. Consultez le chapitre sur la mise à l'échelle des caractéristiques pour savoir comment l'appliquer avec StandardScaler de scikit-learn.
  • Comparer des valeurs de natures différentes : si vous souhaitez comparer le score de mathématiques d'un élève (moyenne 70, écart-type 10) avec son score de lecture (moyenne 50, écart-type 5), les Z-scores permettent une comparaison équitable.

Tester si les Données Suivent une Distribution Normale

Avant d'appliquer des algorithmes qui supposent la normalité, vérifiez cette hypothèse. Les deux approches les plus courantes sont le test de Shapiro-Wilk (pour des échantillons jusqu'à ~5 000) et une inspection visuelle rapide.

Test de Shapiro-Wilk

Le test de Shapiro-Wilk renvoie une statistique W et une p-valeur. Une p-valeur supérieure à 0,05 signifie qu'il n'y a pas suffisamment de preuves pour rejeter la normalité. Une p-valeur inférieure ou égale à 0,05 indique que les données sont peu susceptibles d'être normales.

import numpy as np
from scipy import stats

rng = np.random.default_rng(seed=42)

# Normally distributed sample
normal_sample = rng.normal(loc=0, scale=1, size=50)
stat_n, p_n = stats.shapiro(normal_sample)
print(f"Normal sample  — W={stat_n:.3f}, p={p_n:.3f}")
if p_n > 0.05:
    print("  => Cannot reject normality.")
else:
    print("  => Reject normality.")

# Skewed sample (exponential distribution)
skewed_sample = rng.exponential(scale=1, size=50)
stat_s, p_s = stats.shapiro(skewed_sample)
print(f"Skewed sample  — W={stat_s:.3f}, p={p_s:.3f}")
if p_s > 0.05:
    print("  => Cannot reject normality.")
else:
    print("  => Reject normality.")

Résultat :

Normal sample  — W=0.984, p=0.730
  => Cannot reject normality.
Skewed sample  — W=0.808, p=0.000
  => Reject normality.

L'échantillon normal réussit facilement (p=0,730). L'échantillon exponentiel échoue de façon décisive (p≈0).

Quand le test de normalité est important

Tous les algorithmes ne nécessitent pas des caractéristiques normalement distribuées. Les modèles basés sur les arbres (arbres de décision, forêts aléatoires, gradient boosting) et les réseaux de neurones sont entièrement indépendants de la distribution. La normalité est surtout importante pour :

  • Les tests statistiques paramétriques (test t, ANOVA, corrélation de Pearson) — les p-valeurs de ces tests ne sont valides que si les données sont approximativement normales.
  • L'analyse discriminante linéaire (LDA) — suppose que chaque classe est normalement distribuée.
  • Naive Bayes gaussien — modélise explicitement chaque caractéristique comme une gaussienne.
  • Les intervalles de confiance et de prédiction en régression linéaire — dérivés de la normalité des résidus.

Quand les Données ne Sont pas Normales : Transformation Logarithmique

Un remède courant pour les données asymétriques à droite (par exemple, revenus, prix immobiliers, montants de transactions) est la transformation logarithmique, qui compresse les grandes valeurs et étire les petites. Le résultat est souvent proche de la normale :

import numpy as np
from scipy import stats

rng = np.random.default_rng(seed=7)

# Simulate log-normally distributed incomes (a realistic pattern)
incomes = rng.lognormal(mean=10.5, sigma=0.5, size=1000)
log_incomes = np.log(incomes)

sk_before = stats.skew(incomes)
sk_after  = stats.skew(log_incomes)

stat_before, p_before = stats.shapiro(incomes[:50])
stat_after,  p_after  = stats.shapiro(log_incomes[:50])

print(f"Before transform — skewness: {sk_before:.2f},  Shapiro p={p_before:.3f}")
print(f"After log transform — skewness: {sk_after:.2f}, Shapiro p={p_after:.3f}")

Résultat :

Before transform — skewness: 1.44,  Shapiro p=0.000
After log transform — skewness: 0.01, Shapiro p=0.940

Après la transformation logarithmique, l'asymétrie passe de 1.44 à presque zéro, et la p-valeur du test de Shapiro-Wilk monte de 0.000 à 0.940 — les données transformées réussissent facilement le test de normalité.

Points d'attention :

  • La transformation logarithmique exige que toutes les valeurs soient strictement positives. Si vos données contiennent des zéros, utilisez np.log(x + 1) (transformation log-plus-un).
  • Si vos données présentent une asymétrie à gauche (longue queue à gauche), essayez plutôt une transformation par carré ou par réflexion.
  • Appliquez toujours la même transformation à la fois à l'ensemble d'entraînement et à toute nouvelle donnée au moment de l'inférence.

Pourquoi la Distribution Normale est Importante en Apprentissage Automatique

La distribution normale apparaît partout en ML, souvent implicitement :

  • Théorème central limite : la moyenne d'un grand échantillon aléatoire suit approximativement une distribution normale, quelle que soit la distribution d'origine. Cela sous-tend les intervalles de confiance et les tests d'hypothèses sur les métriques des modèles.
  • Analyse des résidus : les résidus d'une régression linéaire bien ajustée doivent être approximativement normaux. Des écarts signalent une mauvaise spécification du modèle.
  • Initialisation des poids dans les réseaux de neurones : des schémas comme l'initialisation de Xavier et He tirent les poids initiaux de distributions normales pour éviter la disparition ou l'explosion des gradients.
  • Processus gaussiens : une famille de modèles probabilistes qui placent une distribution normale sur des fonctions, utilisés en optimisation bayésienne et en quantification de l'incertitude.
  • Détection des valeurs aberrantes : dans de nombreux domaines, les données au-delà de ±3σ par rapport à la moyenne sont signalées comme anomalies.

Comprendre où la distribution normale est supposée — et quand cette hypothèse est mise en défaut — vous aide à éviter des erreurs de modélisation silencieuses.

Résumé

  • La distribution normale est définie par sa moyenne (μ) et son écart-type (σ) ; sa courbe en cloche est symétrique autour de la moyenne.
  • La règle empirique stipule que 68%, 95% et 99,7% des valeurs se situent dans 1, 2 et 3 écarts-types de la moyenne.
  • Utilisez numpy.random.default_rng().normal() pour générer des échantillons normalement distribués et scipy.stats.norm.pdf() / .cdf() pour évaluer les probabilités.
  • Les Z-scores standardisent les valeurs sur une échelle commune ; ils sont essentiels pour la détection des valeurs aberrantes et la mise à l'échelle des caractéristiques.
  • Utilisez scipy.stats.shapiro() pour tester formellement la normalité — mais gardez à l'esprit que de nombreux algorithmes ML modernes ne l'exigent pas.
  • Lorsque les données sont asymétriques à droite, une transformation logarithmique les rend souvent approximativement normales.

Pour une comparaison plus large des types de distributions (uniforme, asymétrique, multimodale), consultez le chapitre Distribution des Données. Pour savoir comment mesurer la dispersion et le centre, consultez Écart-Type et Moyenne, Médiane et Mode.

Was this page helpful?