Épreuve pratique · 2026 · Sujet 18

Bac NSI pratique 2026 sujet 18 : Températures en Polynésie, anomalies et décennies

Des bouées mesurent la température de surface de l’océan pour les archipels Société, Tuamotu, Marquises et Australes. Le fichier contient des dictionnaires date, zone et temperature. Le petit jeu fourni couvre trois zones : aucune mesure ne concerne Australes. On veut une moyenne par zone, des anomalies relatives à cette moyenne puis des moyennes par décennie. Une anomalie au sens de l’exercice signifie un écart statistique au seuil choisi, pas une mesure automatiquement invalide.

Dans ce sujet

Un corrigé pédagogique pour comprendre et justifier vos réponses. Les conseils de rédaction ne constituent pas un barème officiel détaillé.

Application

Températures en Polynésie, anomalies et décennies

Des bouées mesurent la température de surface de l’océan pour les archipels Société, Tuamotu, Marquises et Australes. Le fichier contient des dictionnaires date, zone et temperature. Le petit jeu fourni couvre trois zones : aucune mesure ne concerne Australes. On veut une moyenne par zone, des anomalies relatives à cette moyenne puis des moyennes par décennie. Une anomalie au sens de l’exercice signifie un écart statistique au seuil choisi, pas une mesure automatiquement invalide.

ZoneDatesTempératures (°C)
Societe2010-01-15,2010-06-20,2011-03-10,2020-02-14,2020-08-22,2021-05-3027 ;26,5 ;27,5 ;28 ;28,5 ;29
Tuamotu2015-04-10,2020-07-15,2021-09-2026,8 ;27,5 ;28
Marquises2020-03-15,2021-07-10,2022-11-2525,5 ;26 ;26,5

Le dossier comporte le sujet et les fichiers listés dans les téléchargements. Les codes de départ sont reproduits ci-dessous. Travaillez sur une copie. Les appels professeur signalés dans les questions servent à présenter votre démarche ; vous pouvez aussi demander de l’aide en cas de difficulté.

Code fourni : analyse_temperatures_polynesie.py
# =================================================================================#
# Données de test
donnees_test = [
    # Société - Données sur 2010 et 2020
    {'date': '2010-01-15', 'zone': 'Societe', 'temperature': 27.0},
    {'date': '2010-06-20', 'zone': 'Societe', 'temperature': 26.5},
    {'date': '2011-03-10', 'zone': 'Societe', 'temperature': 27.5},
    {'date': '2020-02-14', 'zone': 'Societe', 'temperature': 28.0},
    {'date': '2020-08-22', 'zone': 'Societe', 'temperature': 28.5},
    {'date': '2021-05-30', 'zone': 'Societe', 'temperature': 29.0},

    # Tuamotu - Données sur 2010 et 2020
    {'date': '2015-04-10', 'zone': 'Tuamotu', 'temperature': 26.8},
    {'date': '2020-07-15', 'zone': 'Tuamotu', 'temperature': 27.5},
    {'date': '2021-09-20', 'zone': 'Tuamotu', 'temperature': 28.0},

    # Marquises - Données uniquement sur 2020
    {'date': '2020-03-15', 'zone': 'Marquises', 'temperature': 25.5},
    {'date': '2021-07-10', 'zone': 'Marquises', 'temperature': 26.0},
    {'date': '2022-11-25', 'zone': 'Marquises', 'temperature': 26.5},
]

# =================================================================================#
#  Question 1 : Ecrire le code de votre fonction température_moyenne


# =================================================================================#
#  Question 2 : Ecrire le code de votre fonction detection_anomalies


# =================================================================================#
# code de la fonction evolution_par_decennie à corriger dans la question 4:


def evolution_par_decennie(zone, donnees):
    """
    Calcule l'évolution des températures moyennes par décennie pour une zone.

    ATTENTION: Cette fonction contient un bug volontaire à détecter et corriger.

    Arguments:
        zone (str): Nom de l'archipel (ex: 'Societe', 'Tuamotu')
        donnees (list): Liste de dictionnaires de relevés

    Renvoie:
        dict: Dictionnaire {décennie : température_moyenne}
              ex: {2010: 27.5, 2020: 28.3}
              Renvoie un dictionnaire vide si la zone n'existe pas
    """
    # Filtrage des relevés pour la zone
    releves_zone = [r for r in donnees if r['zone'] == zone]

    if len(releves_zone) == 0:
        return {}

    # Regroupement par décennie
    temperatures_par_decennie = {}

    for releve in releves_zone:
        # Extraction de l'année de la date (format: 'YYYY-MM-DD')
        annee = int(releve['date'].split('-')[0])

        # Calcul de la décennie
        decennie = (annee // 10)

        if decennie not in temperatures_par_decennie:
            temperatures_par_decennie[decennie] = []

        temperatures_par_decennie[decennie].append(releve['temperature'])

    # Calcul des moyennes
    moyennes = {}
    for decennie, temperatures in temperatures_par_decennie.items():
        moyennes[decennie] = round(sum(temperatures) / len(temperatures), 2)

    return moyennes


# =================================================================================#
#  Exercice 2.1 :
"""
Tests
À compléter par le candidat dans le cadre de la question 3
"""


def test_zone_inexistante():
    """
    Test 1 : Tester une zone qui n'existe pas

    À compléter:
    1. Appeler evolution_par_decennie avec une zone inexistante
    2. Vérifier que le résultat est un dictionnaire vide
    """
    pass  # à remplacer par le code du candidat


def test_une_seule_decennie():
    """
    Test 2: Tester une zone avec données sur une seule décennie

    À compléter:
    1. Appeler evolution_par_decennie avec la zone appropriée
    2. Vérifier que le résultat ne contient qu'une seule décennie (2020)
    3. Vérifier la température moyenne
    """
    pass  # à remplacer par le code du candidat


def test_plusieurs_decennies():
    """
    Test 3 : Tester une zone avec données sur plusieurs décennies

    À compléter:
    1. Appeler evolution_par_decennie avec la zone appropriée
    2. Vérifier que le résultat contient bien les clés 2010 et 2020
    3. Vérifier que les températures moyennes sont cohérentes
    """
    pass  # à remplacer par le code du candidat

Question 1

#

Écrire temperature_moyenne(zone, donnees), qui renvoie la moyenne flottante de la zone, ou None sans relevé. L’exemple Societe avec 28.5 et 29.0 donne 28.75 ; Marquises absente de cet exemple donne None.

Indice

Filtrer à la fois le numérateur et le dénominateur.

Comprendre la correction
def temperature_moyenne(zone, donnees):
    somme = 0
    nombre = 0
    for releve in donnees:
        if releve['zone'] == zone:
            somme += releve['temperature']
            nombre += 1
    return somme / nombre if nombre else None

Le compteur ne compte que la zone demandée. Le return après le parcours utilise ce compteur pour la division et distingue absence de données et valeur moyenne. Dans le jeu complet de test, Societe donne 27.75°C. La présence d’autres zones ne doit pas modifier ce résultat.

assert temperature_moyenne('Societe',donnees_test) == 27.75
assert temperature_moyenne('Australes',donnees_test) is None
Voir la question dans le sujet PDF, p. 2 (nouvel onglet)

Question 2

#

Écrire detecter_anomalies(zone, seuil, donnees) : calculer la moyenne de la zone puis renvoyer les dates dont l’écart absolu dépasse strictement le seuil. Renvoyer[] pour une zone absente. Societe avec seuil 1.0 doit donner 2010-06-20 et 2021-05-30.

PythonRepérer les anomalies chaudes et froidesÉcrivez votre solution et mettez-la à l’épreuve

Écrivez detecter_anomalies(zone, seuil, donnees). La moyenne de la zone est calculée par temperature_moyenne, fournie. Renvoyez les dates dont l’écart absolu à cette moyenne dépasse strictement le seuil, dans l’ordre des données. Pour une zone absente, renvoyez []. Les jeux complémentaires permettent de vérifier la moyenne mentalement.

def detecter_anomalies(zone, seuil, donnees):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Une anomalie de chaque côté : La moyenne est 27,75 ; les deux écarts absolus valent 1,25.
  • Égalité au seuil : Dépasser strictement n’inclut pas une égalité.
  • Une autre zone ne change rien : La moyenne et la sélection sont toutes deux filtrées sur A.
  • Zone absente : None doit être traité avant de calculer une soustraction.
  • Mesure unique : Une valeur est égale à sa propre moyenne : son écart nul ne dépasse pas zéro.
Indice

Un écart froid est négatif avant abs.

Comprendre la correction
def detecter_anomalies(zone, seuil, donnees):
    moyenne = temperature_moyenne(zone, donnees)
    if moyenne is None:
        return []
    dates = []
    for releve in donnees:
        if releve['zone'] == zone and abs(releve['temperature'] - moyenne) > seuil:
            dates.append(releve['date'])
    return dates

La moyenne est calculée une seule fois, avant la boucle. La valeur absolue retient aussi bien un relevé plus froid qu’un relevé plus chaud. Pour Societe,26.5 et 29.0 sont chacun à 1.25°C de 27.75 : ils dépassent 1°C. À un seuil exactement 1.25, aucun des deux ne serait retenu. On préserve l’ordre des dates du fichier.

assert detecter_anomalies('Societe',1.0,donnees_test) == ['2010-06-20','2021-05-30']
assert detecter_anomalies('Societe',1.25,donnees_test) == []
assert detecter_anomalies('Australes',1.0,donnees_test) == []
Voir la question dans le sujet PDF, p. 2 (nouvel onglet)

Question 3

#

Compléter les trois fonctions de test : zone inexistante, zone avec une seule décennie, zone avec plusieurs décennies. Les assertions doivent révéler le bug du code fourni.

Comprendre la correction
def test_zone_inexistante():
    assert evolution_par_decennie('Australes',donnees_test) == {}
def test_une_seule_decennie():
    assert evolution_par_decennie('Marquises',donnees_test) == {2020:26.0}
def test_plusieurs_decennies():
    assert evolution_par_decennie('Societe',donnees_test) == {2010:27.0,2020:28.5}
    assert evolution_par_decennie('Tuamotu',donnees_test) == {2010:26.8,2020:27.75}

Australes vérifie le dictionnaire vide ; Marquises ne possède que la décennie 2020 et sa moyenne vaut 26. Societe possède 2010 avec(27+26.5+27.5)/3=27 et 2020 avec(28+28.5+29)/3=28.5. L’égalité de dictionnaires vérifie à la fois les clés et les valeurs. Tester seulement les moyennes laisserait passer le bug de décennie.

Un bon test vérifie aussi les clésUn atelier pour expérimenter

Choisissez une réponse puis confrontez votre intuition à l’explication. Vous pouvez modifier vos choix.

Lire les réponses et leurs explications
  1. Que vaut 2021//10 ?

    202. La division entière enlève le chiffre des unités.

  2. Le dictionnaire{202:26} convient-il pour les années 2020 ?

    Non, la clé doit être 2020.. Le contrat impose l’année de début de décennie.

Appuyez votre explication sur les valeurs et les conditions exactes du programme.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 4

#

Corriger evolution_par_decennie après identification du défaut par les tests.

PythonRegrouper 2019 et 2020 dans les bonnes décenniesÉcrivez votre solution et mettez-la à l’épreuve

Corrigez evolution_par_decennie(zone, donnees). Regroupez les températures de la zone par année de début de décennie, puis calculez la moyenne de chaque groupe arrondie à deux décimales. Par exemple, 2010 à 2019 ont la clé entière 2010. Les dates suivent AAAA-MM-JJ.

def evolution_par_decennie(zone, donnees):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Une frontière de décennie : La clé exprime une année, pas le quotient 201 ou 202.
  • Plusieurs valeurs dans un groupe : La première température ne doit pas être écrasée par les suivantes.
  • Deux décimales : L’arrondi est appliqué à la moyenne finale, pas à chaque contribution.
  • Filtrer avant de regrouper : Le même groupe temporel peut contenir plusieurs zones, mais une seule est demandée.
  • Zone absente et données vides : Une zone sans relevé ne doit pas créer un groupe artificiel.
Comprendre la correction

annee//10 donne 201 ou 202, pas 2010 ou 2020. Il faut multiplier le quotient entier par 10. La répartition des relevés était correcte, mais les clés renvoyées ne respectaient pas le contrat. Voici une version complète qui garde la même moyenne arrondie à deux décimales :

def evolution_par_decennie(zone, donnees):
    temperatures_par_decennie = {}
    for releve in donnees:
        if releve['zone'] == zone:
            annee = int(releve['date'].split('-')[0])
            decennie = (annee // 10) * 10
            if decennie not in temperatures_par_decennie:
                temperatures_par_decennie[decennie] = []
            temperatures_par_decennie[decennie].append(releve['temperature'])
    moyennes = {}
    for decennie, temperatures in temperatures_par_decennie.items():
        moyennes[decennie] = round(sum(temperatures) / len(temperatures), 2)
    return moyennes
test_zone_inexistante()
test_une_seule_decennie()
test_plusieurs_decennies()

L’initialisation d’une liste lors de la première rencontre d’une décennie évite d’écraser les températures précédentes. Les moyennes sont calculées après le regroupement pour utiliser toutes les observations.2019 appartient à 2010 ;2020 appartient à 2020 : ces deux années sont de bons tests supplémentaires de frontière.

Le regroupement transforme une date en clé de période, puis ajoute une température au groupe. Pour 2010 et 2019, la division entière par 10 donne dans les deux cas 201 ; multiplier par 10 reconstruit le début de période 2010. Pour 2020, le quotient devient 202 et la clé 2020. Le calcul des moyennes arrive après ce regroupement : une clé créée à chaque relevé sans conserver sa liste précédente perdrait l’historique. Le test d’une zone couvrant plusieurs décennies contrôle donc à la fois la transformation des dates et l’accumulation des températures.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)
Régler le seuil d’anomalie en SociétéUn atelier pour expérimenter

Modifiez le seuil. Les six relevés et leur moyenne 27,75°C sont ceux du sujet ; les deux côtés de la moyenne sont étudiés.

Lire le résultat de l’expérience initiale

2 dates dépassent strictement1°C d’écart.

L’écart absolu mesure la distance à27,75. Une égalité au seuil n’est pas retenue.

DateTempératureÉcart absoluAnomalie ?
2010-01-15270.75Non
2010-06-2026.51.25Oui
2011-03-1027.50.25Non
2020-02-14280.25Non
2020-08-2228.50.75Non
2021-05-30291.25Oui

Les tests de seuil doivent inclure une valeur exactement à la frontière.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Vérifier autant les clés du résultat que les moyennes.
  • Une observation éloignée de la moyenne reste une donnée à interpréter.

Retrouver ces notions dans d’autres sujets

Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027

Énoncé : sujet 26_BCG_NSI_18 (PDF). Corrigé et explications pédagogiques proposés par Sofien.