1ʳᵉ spé · Chapitre 11 · Probabilités et statistiques

Échantillonnage et simulation

Simulation d'échantillons en Python, moyenne d'échantillon et espérance, écart en 2σ/√n.

Ce qu'il faut savoir faire

  • Simulation d'échantillons en Python
  • Moyenne d'échantillon et espérance
  • Écart en 2σ/√n

Comment un institut de sondage annonce-t-il le salaire moyen des Français en n'interrogeant que quelques milliers de personnes ? Comment un fabricant contrôle-t-il le poids moyen de ses paquets de café sans peser toute la production ? Comment vérifier qu'un dé est équilibré sans démonstration possible, seulement en le lançant ? Ces trois questions reposent sur la même idée : observer un échantillon pour estimer une grandeur inaccessible. Ce chapitre met cette idée à l'épreuve de l'expérience, grâce à un outil que le hasard ne peut pas fatiguer : la simulation en Python. Au passage, il introduit la seule nouveauté algorithmique de l'année, la structure de liste, indispensable pour stocker et exploiter les résultats de milliers d'expériences aléatoires.

Simuler le hasard avec Python

Lancer un dé 10000 fois à la main est fastidieux ; demander à un ordinateur de le faire prend quelques millisecondes. Le module random de Python fournit les briques de base de toute simulation.

Définition

Fonctions du module random. Après l'instruction from random import random, randint, choice :

  • random() renvoie un réel « au hasard » dans l'intervalle [0;1[, chaque zone de l'intervalle étant équitablement servie ;
  • randint(a, b) renvoie un entier au hasard entre a et b inclus, tous équiprobables ;
  • choice(liste) renvoie un élément de la liste choisi au hasard, tous équiprobables.

Exemple

  • randint(1, 6) simule le lancer d'un dé équilibré à six faces.
  • choice(["pile", "face"]) simule le lancer d'une pièce équilibrée.
  • randint(0, 36) simule un tour de roulette (numéros de 0 à 36).

Méthode

Simuler une expérience à deux issues. Pour simuler une expérience dont l'issue « succès » a pour probabilité p, on tire un réel random() et on teste s'il est inférieur à p : le test random() < p vaut True avec probabilité p, car les réels favorables occupent l'intervalle [0;p[, de longueur p, dans l'intervalle [0;1[ de longueur 1.

from random import random

def tir_reussi():
    # une basketteuse reussit ses lancers francs avec probabilite 0.8
    return random() < 0.8

Pour simuler une variable aléatoire quelconque, on découpe l'intervalle [0;1[ en zones dont les longueurs sont les probabilités de la loi : c'est la méthode du cumul des probabilités.

Méthode

Simuler une variable aléatoire de loi donnée avec random(). On tire un réel r = random(), puis on parcourt les valeurs de la variable dans l'ordre en cumulant leurs probabilités : la première fois que r passe sous le cumul, on renvoie la valeur correspondante. Chaque valeur est ainsi obtenue avec une probabilité égale à la longueur de sa zone, c'est-à-dire exactement sa probabilité dans la loi.

Exemple

Une loterie fait gagner une somme X (en euros) dont la loi est :

xi 0 2 10
P(X=xi) 0,7 0,25 0,05

Les cumuls successifs valent 0,7 puis 0,7+0,25=0,95 puis 1. D'où la simulation :

from random import random

def gain():
    r = random()
    if r < 0.7:
        return 0
    elif r < 0.95:
        return 2
    else:
        return 10

Le test r < 0.7 réussit avec probabilité 0,7 ; sinon, r est dans [0,7;1[ et le test r < 0.95 isole la zone [0,7;0,95[, de longueur 0,25 ; la zone restante [0,95;1[ a bien pour longueur 0,05.

Lorsque les probabilités sont des fractions simples de même dénominateur, choice offre un raccourci : il suffit de faire apparaître chaque valeur dans la liste proportionnellement à sa probabilité.

Exemple

Soit Y la variable aléatoire de loi P(Y=0)=14, P(Y=1)=12, P(Y=5)=14. Sur une liste de quatre éléments équiprobables où 1 apparaît deux fois, choice reproduit exactement cette loi :

from random import choice

def simule_Y():
    return choice([0, 1, 1, 5])

Remarque

Un ordinateur ne produit pas du « vrai » hasard : random() calcule des nombres pseudo-aléatoires, engendrés par une formule déterministe partant d'une valeur initiale. La suite obtenue est en pratique indiscernable du hasard, ce qui suffit largement pour nos simulations.

Les listes en Python

Simuler mille lancers de dé produit mille résultats : il faut une structure pour les ranger tous dans une seule variable. C'est le rôle de la liste, suite finie et ordonnée de valeurs, notée entre crochets.

Définition

Liste. Une liste Python est une collection ordonnée de valeurs, appelées éléments, écrites entre crochets et séparées par des virgules. Chaque élément est repéré par sa position, ou indice, en commençant à 0 : dans la liste L = [2, 7, 5], l'élément L[0] vaut 2, L[1] vaut 7 et L[2] vaut 5.

Une liste de longueur n a donc des indices allant de 0 à n1 : demander L[3] dans la liste précédente provoque une erreur. Deux fonctions accompagnent constamment les listes : len(L) renvoie le nombre d'éléments de L, et sum(L) renvoie la somme de ses éléments (lorsqu'ils sont des nombres).

L = [2, 7, 5]
print(L[0])     # affiche 2
print(len(L))   # affiche 3
print(sum(L))   # affiche 14
L[1] = 10       # modifie le deuxieme element
print(L)        # affiche [2, 10, 5]

Méthode

Trois façons de générer une liste.

  • En extension : écrire directement tous les éléments. carres = [0, 1, 4, 9, 16].
  • Par ajouts successifs : partir de la liste vide [] et ajouter les éléments un à un avec append dans une boucle.
  • En compréhension : décrire les éléments par une formule appliquée à un indice qui parcourt un range, éventuellement filtré par une condition.

Les trois méthodes ci-dessous construisent la même liste des carrés des entiers de 0 à 9 :

# en extension (penible au-dela de quelques elements)
carres = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# par ajouts successifs
carres = []
for n in range(10):
    carres.append(n**2)

# en comprehension
carres = [n**2 for n in range(10)]

Une condition peut filtrer les éléments retenus :

# carres des entiers pairs entre 0 et 9
carres_pairs = [n**2 for n in range(10) if n % 2 == 0]
print(carres_pairs)  # affiche [0, 4, 16, 64]

Remarque

La syntaxe en compréhension est calquée sur la description d'un ensemble en mathématiques : la liste [n**2 for n in range(10) if n % 2 == 0] se lit comme l'ensemble {n2n{0,1,,9}, n pair}. Une différence tout de même : une liste est ordonnée et peut contenir des répétitions, contrairement à un ensemble.

Méthode

Parcourir une liste. Deux écritures sont possibles.

  • Itération directe sur les éléments : for x in L fait prendre à x successivement chaque valeur de la liste. C'est l'écriture à privilégier quand seules les valeurs comptent.
  • Parcours par indices : for i in range(len(L)) fait parcourir à i les indices de 0 à len(L) - 1, et on accède aux éléments par L[i]. Indispensable quand la position des éléments intervient, ou pour les modifier.
notes = [12, 8, 15, 10, 17]

# iteration directe : compter les notes au-dessus de 10
compteur = 0
for note in notes:
    if note > 10:
        compteur = compteur + 1
print(compteur)  # affiche 3

# parcours par indices : afficher chaque note avec sa position
for i in range(len(notes)):
    print("Note numero", i, ":", notes[i])

Exemple

La moyenne d'une liste de nombres se calcule en une ligne grâce à sum et len :

def moyenne(L):
    return sum(L) / len(L)

print(moyenne([12, 8, 15, 10, 17]))  # affiche 12.4

Échantillons et moyenne d'échantillon

Les outils sont en place : le hasard se simule, les résultats se rangent dans des listes. Reste à définir l'objet central du chapitre.

Définition

Échantillon. Soit X une variable aléatoire associée à une expérience aléatoire. Un échantillon de taille n de X est la liste des n résultats obtenus en répétant n fois l'expérience dans les mêmes conditions et de façon indépendante : les répétitions n'ont aucune influence les unes sur les autres.

Définition

Moyenne d'un échantillon. La moyenne d'un échantillon (x1,x2,,xn) de taille n est le réel

m=x1+x2++xnn.

Attention à ne pas confondre deux nombres de natures différentes : l'espérance μ=E(X) est un paramètre théorique, calculé une fois pour toutes à partir de la loi de X ; la moyenne m est une valeur observée, qui dépend de l'échantillon obtenu et change d'un échantillon à l'autre.

Méthode

Écrire une fonction Python renvoyant la moyenne d'un échantillon de taille n. Le schéma est toujours le même :

  1. générer l'échantillon : une liste de n simulations de X (en compréhension ou par append) ;
  2. renvoyer sa moyenne : sum(echantillon) / n.

Exemple

Pour un dé équilibré, la variable aléatoire X égale au résultat du lancer prend les valeurs 1 à 6 avec la probabilité 16 chacune, et son espérance vaut

μ=E(X)=1+2+3+4+5+66=216=3,5.
from random import randint

def moyenne_echantillon(n):
    # simule n lancers d'un de equilibre et renvoie leur moyenne
    echantillon = [randint(1, 6) for k in range(n)]
    return sum(echantillon) / n

print(moyenne_echantillon(100))  # par exemple : 3.42

Trois appels de moyenne_echantillon(100) ont renvoyé 3,42, puis 3,61, puis 3,55 : aucune moyenne n'est exactement égale à 3,5, mais toutes en sont proches.

Fluctuation d'échantillonnage et estimation de l'espérance

L'exemple du dé le montre : deux échantillons de même taille donnent en général deux moyennes différentes. Ce phénomène porte un nom.

Définition

Fluctuation d'échantillonnage. La variation de la moyenne (ou de toute autre quantité calculée sur un échantillon) d'un échantillon à l'autre s'appelle la fluctuation d'échantillonnage. Elle ne traduit aucune erreur de mesure : c'est la manifestation normale du hasard.

Que devient cette fluctuation quand la taille de l'échantillon grandit ? Simulons un très long échantillon de lancers de dé et traçons la moyenne des n premiers lancers en fonction de n :

Moyenne des n premiers lancers d'un dé équilibré, pour n allant de 1 à 1000, et droite d'ordonnée mu = 3,5

Au début, la moyenne oscille fortement : elle ne dépend que de quelques lancers. Mais à mesure que n grandit, les oscillations s'amortissent et la courbe se stabilise au voisinage de l'espérance μ=3,5. En classe de seconde, le même constat avait été fait pour la fréquence d'une issue dans une expérience à deux issues ; il se prolonge ici à la moyenne d'une variable aléatoire quelconque.

Propriété

Loi des grands nombres (version vulgarisée, admise). Lorsque la taille n de l'échantillon est grande, sauf exception, la moyenne m d'un échantillon de taille n d'une variable aléatoire X est proche de l'espérance μ=E(X).

La formulation précise de ce résultat, et sa démonstration, sont des objectifs de la classe de terminale.

Ce résultat fonde le principe d'estimation : pour connaître une espérance, ou une moyenne dans une population trop vaste pour être observée entièrement, on l'estime par la moyenne calculée sur un grand échantillon.

Méthode

Estimer une espérance ou une moyenne inconnue.

  1. Constituer un échantillon de grande taille n (mesures réelles ou simulation), en veillant à ce que les observations soient faites dans les mêmes conditions et de façon indépendante.
  2. Calculer la moyenne m de l'échantillon.
  3. Prendre m comme estimation de la valeur inconnue μ, en gardant à l'esprit que m n'est pas exactement μ : un autre échantillon aurait donné une estimation légèrement différente.

Exemple

  • Sondage. Pour estimer le temps d'écran quotidien moyen des lycéens français, on interroge 2000 lycéens choisis au hasard : la moyenne observée, par exemple 4,1 heures, sert d'estimation de la moyenne dans la population entière. Interroger les 2,2 millions de lycéens serait matériellement impossible.
  • Contrôle qualité. Une machine remplit des paquets de café. Le réglage affiché promet 250 g en moyenne. Chaque heure, l'usine pèse 50 paquets prélevés au hasard : si la moyenne de l'échantillon s'écarte nettement de 250 g, la machine est vraisemblablement déréglée.
  • Dé suspect. Pour tester un dé, on le lance 1000 fois. Si la moyenne des résultats vaut 4,2, bien loin des 3,5 attendus pour un dé équilibré, on a de sérieuses raisons de penser que le dé est pipé.

Reste une question, cruciale dans les trois situations : à partir de quel écart entre m et la valeur attendue doit-on s'étonner ? Autrement dit, quelle est l'ampleur normale de la fluctuation d'échantillonnage ? C'est l'objet de la dernière section.

Mesurer la dispersion : l'écart 2σ/n

Pour mesurer la fluctuation, une seule moyenne ne suffit pas : il en faut beaucoup. On simule donc N échantillons de taille n, on calcule les N moyennes obtenues, et on les range... dans une liste, naturellement.

from random import randint

def moyenne_echantillon(n):
    echantillon = [randint(1, 6) for k in range(n)]
    return sum(echantillon) / n

# liste des moyennes de N = 1000 echantillons de taille n = 50
moyennes = [moyenne_echantillon(50) for k in range(1000)]

L'histogramme de ces 1000 moyennes révèle une structure remarquable :

Histogramme des moyennes de 1000 échantillons de taille 50 d'un dé équilibré, avec traits verticaux en mu et en mu plus ou moins 2 sigma sur racine de n

Les moyennes s'accumulent autour de μ=3,5 et se raréfient à mesure qu'on s'en éloigne. Les traits en pointillés délimitent la zone [μ2σn;μ+2σn], où σ désigne l'écart type de X : la grande majorité des moyennes s'y trouve. Calculons ces bornes pour le dé.

Exemple

Écart type d'un dé équilibré (révision du chapitre sur les variables aléatoires). Avec μ=3,5 :

E(X2)=12+22+32+42+52+626=916,V(X)=E(X2)μ2=91612,25=91614712=3512,σ=35121,708.

Pour des échantillons de taille n=50 :

2σn=2×35/12500,483.

La zone en pointillés de l'histogramme est donc environ [3,017;3,983].

Quelle proportion des moyennes tombe dans cette zone ? Écrivons la fonction qui répond à la question, conformément au programme.

Méthode

Calculer la proportion des écarts inférieurs ou égaux à 2σ/n. On simule N échantillons de taille n d'une variable aléatoire d'espérance μ et d'écart type σ ; pour chacun, on teste si sa moyenne m vérifie mμ2σn, et on compte les succès. La proportion est le nombre de succès divisé par N.

from random import randint

def moyenne_echantillon(n):
    echantillon = [randint(1, 6) for k in range(n)]
    return sum(echantillon) / n

def proportion_ecarts(N, n):
    # proportion des N echantillons de taille n dont la moyenne m
    # verifie |m - mu| <= 2*sigma/racine(n), pour un de equilibre
    mu = 3.5
    sigma = (35 / 12)**0.5
    compteur = 0
    for k in range(N):
        m = moyenne_echantillon(n)
        if abs(m - mu) <= 2 * sigma / n**0.5:
            compteur = compteur + 1
    return compteur / N

print(proportion_ecarts(1000, 50))  # par exemple : 0.957

En exécutant ce programme plusieurs fois, avec diverses valeurs de N et de n, on obtient des proportions comme 0,957, 0,948, 0,951... Le constat est frappant, et il se reproduit avec d'autres variables aléatoires que le dé.

Propriété

Constat expérimental (admis). Soit X une variable aléatoire d'espérance μ et d'écart type σ. En simulant un grand nombre N d'échantillons de taille n de X, on constate que pour la plupart des échantillons — environ 95% d'entre eux — la moyenne m vérifie

mμ2σn.

Ce constat est expérimental : il sera précisé et justifié en classe de terminale.

Ce résultat quantifie la fluctuation d'échantillonnage : l'écart « normal » entre une moyenne observée et l'espérance est de l'ordre de 2σn. Deux enseignements pratiques en découlent.

La précision se paie au carré. Le dénominateur n grandit lentement : pour diviser l'écart 2σn par 2, il faut multiplier n par 2, donc multiplier la taille de l'échantillon par 4. Pour le dé, l'écart vaut environ 0,483 avec n=50, environ 0,242 avec n=200, et il faut n=800 pour descendre vers 0,121. C'est pourquoi les sondages, coûteux, se contentent souvent de quelques milliers de personnes : gagner un chiffre de précision multiplierait le coût par 100.

Un critère pour s'étonner. Reprenons le dé suspect de la section précédente : sur 1000 lancers, l'écart normal vaut 2σ10002×1,70831,60,108. Une moyenne observée de 4,2, soit un écart de 0,7 par rapport à 3,5, est plus de six fois supérieure à cet écart : un tel échantillon est exceptionnel pour un dé équilibré. La conclusion raisonnable est que le dé n'est pas équilibré.

Exemple

Bilan sur la loterie de la première section. La variable aléatoire X (gain en euros) a pour espérance

μ=0×0,7+2×0,25+10×0,05=1.

Pour estimer cette espérance par simulation, on peut réutiliser la fonction gain :

def moyenne_gains(n):
    echantillon = [gain() for k in range(n)]
    return sum(echantillon) / n

print(moyenne_gains(10000))  # par exemple : 1.0134

La moyenne observée sur 10000 parties, ici 1,0134, estime l'espérance μ=1 : le joueur gagne en moyenne 1 euro par partie. Si le billet coûte 2 euros, la simulation confirme que le jeu est défavorable au joueur.

L'essentiel en un coup d'œil

  • Simuler : random() (réel uniforme dans [0;1[), randint(a, b) (entier entre a et b inclus), choice(liste) ; une expérience de probabilité de succès p se simule par le test random() < p ; une loi quelconque, par cumul des probabilités ou par choice sur une liste aux valeurs répétées proportionnellement à leurs probabilités.
  • Listes : génération en extension [1, 2, 3], par append dans une boucle, ou en compréhension [f(k) for k in range(n)] (avec condition éventuelle) ; indices à partir de 0 ; len, sum ; parcours par indices ou itération directe sur les éléments.
  • Échantillon de taille n : liste des n résultats de n répétitions indépendantes de l'expérience, dans les mêmes conditions ; sa moyenne m varie d'un échantillon à l'autre (fluctuation d'échantillonnage).
  • Estimation : lorsque n est grand, sauf exception, m est proche de l'espérance μ — on estime donc μ (ou une moyenne inconnue dans une population) par la moyenne observée sur un grand échantillon.
  • Dispersion : en simulant N échantillons de taille n, environ 95% des moyennes vérifient mμ2σn (constat expérimental) ; diviser cet écart par 2 exige un échantillon 4 fois plus grand.

Bloqué sur « Échantillonnage et simulation » ?

On peut le travailler ensemble dès cette semaine. La première heure est offerte — on fait le point honnêtement, et vous repartez au minimum avec une méthode.