Aller au contenu

Exercices#

Les données en tables

Mode d’emploi

  • Niveau : ★ échauffement, ★★ classique (niveau bac), ★★★ défi ; les coups de pouce et les corrigés se déplient sous chaque exercice.

  • ▶ À programmer et tester en Python. Le symbole >>> figure une saisie dans la console.

  • On travaille sur le fichier prenoms.csv (extrait du Fichier des prénoms de l’INSEE, data.gouv.fr) : descripteurs prenom, sexe (F/G), annee (2023 ou 2025), nombre. Et sur communes.csv / departements.csv.

  • Réflexe no 1 : les valeurs lues dans un CSV sont des chaînes. int(...) avant tout calcul ou tri numérique.

  • Usage de l’IA, indiqué sur chaque exercice : aucune ; en appui (déboguer, reformuler, vérifier ; la réponse finale est écrite et comprise par vous) ; l’exercice s’appuie sur une réponse d’IA. Voir la charte d’usage de l’IA.

On suppose disponible dans tous les exercices la fonction d’importation du cours :

import csv
def charger(fichier):
    with open(fichier, encoding="utf-8", newline="") as f:
        return [ligne for ligne in csv.DictReader(f)]

prenoms = charger("prenoms.csv")

Lire une table, lire un CSV#

★ Exercice 1 — Vocabulaire #

• essentiel

On donne le début d’un fichier CSV public (jeux de la médiathèque) :

titre;editeur;duree;age_min
Les Aventuriers du Rail;Days of Wonder;45;8
Dixit;Libellud;30;8
7 Wonders;Repos Prod;30;10
  1. Quel est le séparateur ? Combien y a-t-il de descripteurs ? d’enregistrements ?

  2. Citer les descripteurs. Donner la valeur du descripteur duree pour l’enregistrement Dixit.

  3. Ce fichier est-il « à la française » ou « anglo-saxon » ? Justifier.

Coup de pouce

La première ligne contient les descripteurs (les noms de colonnes) ; chaque ligne suivante est un enregistrement. Quel caractère sépare les valeurs d’une même ligne ?

Corrigé

Remarque

On donne une version simple ; d’autres écritures sont possibles.

1. Le séparateur est le point-virgule ;. Il y a 4 descripteurs et 3 enregistrements. 2. Descripteurs : titre, editeur, duree, age_min. Pour Dixit, duree vaut 30. 3. « À la française » : le séparateur est le point-virgule, comme il se doit quand on veut réserver la virgule aux décimaux.

▷ Erreur fréquente. compter la ligne d’en-tête parmi les enregistrements (4 au lieu de 3) : elle contient les noms des descripteurs, pas des données.

Représenter une table en Python#

★ Exercice 2 — Une table à la main #

• essentiel

Écrire la variable jeux représentant les trois lignes de l’exercice 1 sous forme d’une liste de dictionnaires.

Coup de pouce

Une fiche par enregistrement : un dictionnaire dont les clés sont les descripteurs. Comme csv.DictReader, écrire les valeurs sous forme de chaînes.

Corrigé

Une liste de trois dictionnaires, un par enregistrement, avec les descripteurs pour clés ; les valeurs sont des chaînes, comme après une lecture par csv.DictReader.

jeux = [
    {"titre": "Les Aventuriers du Rail", "editeur": "Days of Wonder",
     "duree": "45", "age_min": "8"},
    {"titre": "Dixit", "editeur": "Libellud", "duree": "30", "age_min": "8"},
    {"titre": "7 Wonders", "editeur": "Repos Prod",
     "duree": "30", "age_min": "10"},
]

★ Exercice 3 — Accès #

▶

Avec la variable jeux précédente, que renvoient ces expressions ?

>>> jeux[1]["titre"]
>>> jeux[0]["age_min"]
>>> len(jeux)

Écrire une expression qui donne l’éditeur de 7 Wonders.

Coup de pouce

On choisit d’abord la fiche par son indice dans la liste (à partir de 0), puis la valeur par le nom du descripteur.

Corrigé

jeux[1]["titre"] \(\rightarrow\) ’Dixit’ ; jeux[0]["age_min"] \(\rightarrow\) ’8’ ; len(jeux) \(\rightarrow\) 3.
Éditeur de 7 Wonders : jeux[2]["editeur"].

▷ Erreur fréquente. inverser les deux accès, jeux["editeur"][2] : jeux est une liste, on y choisit d’abord une fiche par son indice ; c’est la fiche, un dictionnaire, qui se lit par clé.

★★ Exercice 4 — Descripteurs automatiques #

▶

Écrire descripteurs(table) qui renvoie la liste des noms de colonnes d’une table (on suppose la table non vide).

Coup de pouce

Les noms de colonnes sont les clés de n’importe quelle fiche, par exemple de la première, table[0]. Une boucle for sur un dictionnaire parcourt ses clés : il reste à les ranger dans une liste, par exemple avec une compréhension.

>>> descripteurs(prenoms)
['prenom', 'sexe', 'annee', 'nombre']
Corrigé

Les noms de colonnes sont les clés de la première fiche ; une compréhension sur ce dictionnaire parcourt ses clés.

def descripteurs(table):
    return [cle for cle in table[0]]

Importer, et le piège du texte#

★ Exercice 5 — Un fichier mal formé #

Un camarade obtient prenoms.csv avec une seule colonne géante quand il l’ouvre. Quelle est l’erreur la plus probable ? Comment la corriger dans l’appel à csv.DictReader ?

Coup de pouce

Par défaut, csv.DictReader sépare les valeurs par une virgule. Quel séparateur utilisent les fichiers « à la française » ? Le paramètre s’appelle delimiter.

Corrigé

Le fichier est « à la française » (séparateur ;) mais lu avec le séparateur par défaut (la virgule) : chaque ligne entière est prise pour une seule valeur. On corrige en précisant le séparateur :

csv.DictReader(f, delimiter=";")

★★ Exercice 6 — Encodage #

En ouvrant un fichier, l’élève voit Raphaël au lieu de Raphaël. Quel réglage a été oublié ? Écrire la ligne open(...) corrigée.

Coup de pouce

Quel paramètre de open indique comment lire les caractères accentués ? Quel est l’encodage standard aujourd’hui ?

Corrigé

L’encoding n’a pas été précisé (le fichier est en UTF-8 mais lu autrement). On corrige :

open(fichier, encoding="utf-8", newline="")

★ Exercice 7 — Lecture de trace #

Sans machine, dire ce que renvoie mystere(prenoms).

def mystere(table):
    r = 0
    for ligne in table:
        if ligne["sexe"] == "F":
            r = r + 1
    return r

Que calcule cette fonction, en une phrase ?

Coup de pouce

C’est le patron du compteur : r augmente de 1 pour chaque fiche qui vérifie la condition. Combien de fiches de filles contient prenoms (deux années, un top 10 par année) ?

Corrigé

Elle renvoie 20. Elle compte le nombre de fiches de filles dans la table (10 en 2023 \(+\) 10 en 2025).

★ Exercice 8 — Le piège du texte #

▶ • essentiel

Prédire, puis vérifier, la valeur de chaque expression :

>>> prenoms[0]["nombre"]
>>> prenoms[0]["nombre"] + prenoms[1]["nombre"]
>>> int(prenoms[0]["nombre"]) + int(prenoms[1]["nombre"])

Expliquer la différence entre les deux dernières lignes.

Coup de pouce

Quel est le type de prenoms[0]["nombre"] ? Que fait + entre deux chaînes ?

Corrigé
>>> prenoms[0]["nombre"]
'3177'
>>> prenoms[0]["nombre"] + prenoms[1]["nombre"]
'31773168'
>>> int(prenoms[0]["nombre"]) + int(prenoms[1]["nombre"])
6345

Sur des chaînes, + colle les textes (concaténation). Il faut int(...) pour obtenir une vraie addition.

▷ Erreur fréquente. convertir après l’addition, int(prenoms[0]["nombre"] + prenoms[1]["nombre"]) : les chaînes sont déjà collées, on obtient 31773168. On convertit chaque valeur avant de calculer.

Rechercher et filtrer#

★ Exercice 9 — Filtrer #

▶ • essentiel

Écrire selection(table, sexe, annee) qui renvoie la liste des fiches correspondant à ce sexe et cette annee. Vérifier :

>>> filles23 = selection(prenoms, "F", "2023")
>>> [l["prenom"] for l in filles23]
['Louise', 'Ambre', 'Alba', 'Jade', 'Emma', 'Rose',
 'Alma', 'Alice', 'Romy', 'Anna']
Coup de pouce

Partir d’une liste vide et y ajouter (append) chaque fiche qui vérifie les deux conditions. Attention : l’année est lue comme une chaîne, d’où "2023" entre guillemets.

Corrigé

On part d’une liste vide et on y ajoute chaque fiche qui vérifie les deux conditions, reliées par and.

def selection(table, sexe, annee):
    resultat = []
    for ligne in table:
        if ligne["sexe"] == sexe and ligne["annee"] == annee:
            resultat.append(ligne)
    return resultat

▷ Erreur fréquente. appeler selection(prenoms, "F", 2023) avec l’entier 2023 : "2023" == 2023 est faux, et la sélection est vide, sans aucun message d’erreur.

★★ Exercice 10 — En compréhension #

▶

Réécrire selection en une seule ligne avec une compréhension de liste.

Coup de pouce

Une compréhension avec filtre a la forme [expression for element in liste if condition] : quelle condition reprendre de selection ?

Corrigé

La condition du if de selection devient le filtre de la compréhension, et l’élément gardé est la fiche elle-même.

def selection(table, sexe, annee):
    return [l for l in table if l["sexe"] == sexe and l["annee"] == annee]

★★ Exercice 11 — Rechercher une fiche #

▶ • essentiel

Écrire chercher(table, prenom, annee) qui renvoie la fiche de ce prénom pour cette année, ou None si elle n’existe pas. On s’arrêtera dès qu’on l’a trouvée.

Coup de pouce

C’est une recherche séquentielle : return dès que les deux critères sont vérifiés. À quel endroit placer le return None ?

>>> chercher(prenoms, "Jade", "2025")["nombre"]
'2925'
>>> chercher(prenoms, "Kevin", "2025")
None
Corrigé

Recherche séquentielle : on renvoie la fiche dès que les deux critères sont vérifiés, et None seulement après avoir tout parcouru.

def chercher(table, prenom, annee):
    for ligne in table:
        if ligne["prenom"] == prenom and ligne["annee"] == annee:
            return ligne
    return None

▷ Erreur fréquente. placer return None dans un else, à l’intérieur de la boucle : la fonction répond dès la première fiche et ne trouve que le premier prénom de la table.

★★ Exercice 12 — Deux critères #

▶

Écrire un appel (ou une compréhension) qui donne la liste des prénoms de garçons de 2025 attribués plus de 3000 fois. (Attention au piège no 1.)

Coup de pouce

Trois conditions reliées par and (sexe, année, nombre). Sur quel type doit porter la comparaison avec 3000 ?

Corrigé
[l["prenom"] for l in prenoms
 if l["sexe"] == "G" and l["annee"] == "2025" and int(l["nombre"]) > 3000]
# -> ['Gabriel', 'Noah', 'Leo', 'Raphael', 'Louis', 'Jules']

Le int(...) est indispensable : sans lui, on comparerait une chaîne à un entier (erreur), ou un tri de texte donnerait un résultat faux.

Calculer sur une colonne#

★ Exercice 13 — Compter #

▶

Écrire compter(table, sexe) qui renvoie le nombre de fiches pour ce sexe. Combien pour "G" ?

Coup de pouce

Même patron que mystere dans l’exercice « Lecture de trace » ; seule la valeur comparée change.

Corrigé

Patron du compteur : n part de 0 et augmente de 1 pour chaque fiche de ce sexe.

def compter(table, sexe):
    n = 0
    for ligne in table:
        if ligne["sexe"] == sexe:
            n += 1
    return n

compter(prenoms, "G") \(\rightarrow\) 20.

★★ Exercice 14 — Total et moyenne #

▶ • essentiel

  1. Écrire total(table, sexe, annee) qui renvoie la somme des nombre des fiches correspondantes.

    Coup de pouce

    Patron de l’accumulateur : un total initialisé à 0, augmenté pour chaque fiche retenue. On peut réutiliser selection.

  2. En déduire combien de garçons ont reçu l’un de ces 10 prénoms en 2023.

  3. Écrire nombre_moyen(table) (moyenne des nombre). Que vaut-elle sur filles23 ?

    Coup de pouce

    La moyenne, c’est la somme des valeurs divisée par le nombre de fiches, len(table).

Corrigé

Patron de l’accumulateur : on ajoute int(ligne["nombre"]) pour chaque fiche retenue ; la moyenne divise la somme par len(table).

def total(table, sexe, annee):
    s = 0
    for ligne in table:
        if ligne["sexe"] == sexe and ligne["annee"] == annee:
            s = s + int(ligne["nombre"])
    return s

def nombre_moyen(table):
    total_val = 0
    for ligne in table:
        total_val = total_val + int(ligne["nombre"])
    return total_val / len(table)

total(prenoms, "G", "2023") \(\rightarrow\) 32684. nombre_moyen(filles23) \(\rightarrow\) 2628.7.

▷ Erreur fréquente. oublier int dans l’accumulateur : s + ligne["nombre"] ajoute une chaîne à un entier, et Python lève une TypeError.

★★ Exercice 15 — Champion et lanterne rouge #

▶

  1. Écrire plus_donne(table) qui renvoie le couple (prénom, nombre) de la fiche au plus grand nombre (invariant du champion).

    Coup de pouce

    Prendre la première fiche comme championne provisoire, puis la remplacer dès qu’une fiche a un nombre plus grand (comparer des entiers). Renvoyer à la fin un p-uplet construit à partir de la championne.

  2. Écrire moins_donne(table) sur le même modèle.

  3. Tester sur prenoms entier. Pourquoi faut-il convertir avec int pour que ce soit correct ?

Corrigé
def plus_donne(table):
    champion = table[0]
    for ligne in table:
        if int(ligne["nombre"]) > int(champion["nombre"]):
            champion = ligne
    return champion["prenom"], int(champion["nombre"])

def moins_donne(table):
    champion = table[0]
    for ligne in table:
        if int(ligne["nombre"]) < int(champion["nombre"]):
            champion = ligne
    return champion["prenom"], int(champion["nombre"])

plus_donne(prenoms) \(\rightarrow\) (’Gabriel’, 4625) ; moins_donne(prenoms) \(\rightarrow\) (’Anna’, 2129). Sans int, on comparerait des chaînes. Sur ce fichier, le champion serait le bon par chance : tous les effectifs ont 4 chiffres. Mais dès que les longueurs diffèrent, "900" serait jugé « plus grand » que "3070" (comparaison caractère par caractère) et le champion serait faux.

★★★ Exercice 16 — Coût #

≈ 10 min

On veut, pour chaque fiche, afficher son nombre et la moyenne de la table. Un élève écrit :

for ligne in table:
    print(ligne["nombre"], nombre_moyen(table))   # <-- ?

Pourquoi le coût devient-il quadratique ? Comment le rendre linéaire ? (Rappel du chapitre Algorithmique : le parcours séquentiel.)

Coup de pouce

Combien de fiches nombre_moyen parcourt-elle à chaque appel ? Combien de fois est-elle appelée ? La moyenne change-t-elle d’un tour à l’autre ?

Coup de pouce 2 (début de solution)

Calculer la moyenne une seule fois, avant la boucle : m = nombre_moyen(table), puis utiliser m dans la boucle.

Corrigé

À chaque tour de boucle (il y en a \(n\)), nombre_moyen(table) re-parcourt toute la table (\(n\) opérations) : coût total \(n \times n\), quadratique. On rend le coût linéaire en calculant la moyenne une seule fois, avant la boucle :

m = nombre_moyen(table)
for ligne in table:
    print(ligne["nombre"], m)

Trier une table#

★★ Exercice 17 — Trier par nombre #

▶ • essentiel

Écrire trier_par_nombre(table) qui renvoie la table triée du plus donné au moins donné. Afficher les 3 premiers prénoms de garçons de 2025.

Coup de pouce

sorted avec key (sur quoi comparer ?) et reverse=True ; pour la clé, penser au piège no 1. Pour les 3 premiers : filtrer avec selection, trier, puis une tranche [:3].

Corrigé

sorted avec une clé qui convertit le nombre en entier, et reverse=True pour aller du plus donné au moins donné ; on filtre d’abord, on garde les 3 premiers à la fin.

def trier_par_nombre(table):
    return sorted(table, key=lambda ligne: int(ligne["nombre"]), reverse=True)

top = trier_par_nombre(selection(prenoms, "G", "2025"))
print([l["prenom"] for l in top][:3])   # ['Gabriel', 'Noah', 'Leo']

★★ Exercice 18 — Alphabétique #

▶

Trier filles23 par ordre alphabétique des prénoms. Quelle est la seule ligne à changer par rapport à l’exercice précédent ?

Coup de pouce

Seule la clé du tri change : sur quel descripteur trier ? Faut-il convertir ?

Corrigé

On change seulement la clé (et on enlève reverse) :

sorted(filles23, key=lambda l: l["prenom"])
# -> Alba, Alice, Alma, Ambre, Anna, Emma, Jade, Louise, Romy, Rose

★★ Exercice 19 — Le tri qui ment #

▶

Trier filles23 avec key=lambda l: l["nombre"] (sans int) et reverse=True. Le résultat est-il le bon classement ? Expliquer précisément pourquoi, dans ce tri de chaînes avec reverse=True, "900" passerait avant "3070".

Coup de pouce

Deux chaînes se comparent caractère par caractère, de gauche à droite, comme les mots d’un dictionnaire : comparer d’abord le premier caractère de chacune.

Corrigé

Avec key=lambda l: l["nombre"], on trie les nombre comme des mots. Deux chaînes se comparent caractère par caractère : "900" commence par ’9’, "3070" par ’3’ ; comme ’9’ > ’3’, la chaîne "900" est jugée « plus grande » que "3070". Sur filles23, le résultat est pourtant le bon classement, mais par chance : tous les effectifs ont 4 chiffres, et pour des chaînes de chiffres de même longueur, l’ordre des mots coïncide avec l’ordre des nombres. Il suffirait d’un prénom donné 900 fois pour que le classement devienne faux : il faut int(...).

Fusionner deux tables#

★ Exercice 20 — Remettre la jointure dans l’ordre #

Voici les lignes de la fonction jointure du cours, mélangées et sans indentation. Les remettre dans le bon ordre et les indenter correctement. Pour chaque ligne, dire en quelques mots son rôle.

resultat.append(fusion)
for ld in droite:
def jointure(gauche, droite, cle):
fusion[c] = ld[c]
return resultat
if lg[cle] == ld[cle]:
resultat = []
fusion = {c: lg[c] for c in lg}
for lg in gauche:
for c in ld:
Coup de pouce

Deux boucles imbriquées (gauche, puis droite) et un test d’égalité sur la clé. Quand les clés sont égales : copier la fiche de gauche, y ajouter les colonnes de droite, ranger le résultat.

Corrigé
def jointure(gauche, droite, cle):
    resultat = []                 # la table fusionnee, vide au depart
    for lg in gauche:             # chaque fiche de gauche...
        for ld in droite:         # ... est comparee a chaque fiche de droite
            if lg[cle] == ld[cle]:          # meme valeur de la cle
                fusion = {c: lg[c] for c in lg}   # copie de la fiche de gauche
                for c in ld:
                    fusion[c] = ld[c]       # on ajoute les colonnes de droite
                resultat.append(fusion)     # on range la fiche combinee
    return resultat

À noter : resultat = [] est avant les boucles (sinon on le vide à chaque tour) ; resultat.append(fusion) est dans le if, après la boucle sur c (sinon on ajouterait des fiches incomplètes) ; return resultat est au niveau de la fonction, après toutes les boucles.

★★ Exercice 21 — Jointure #

▶ • essentiel

On charge communes = charger("communes.csv") et departements = charger("departements.csv"), reliées par le descripteur dep.

  1. Écrire jointure(gauche, droite, cle) (patron du cours).

  2. Vérifier que la fiche de Nice obtient bien sa region.

  3. Le fichier communes.csv contient Ajaccio (code 2A), absent de departements.csv. Que devient Ajaccio après la jointure ? Pourquoi ?

    Coup de pouce

    Pour la fiche d’Ajaccio, la condition lg[cle] == ld[cle] est-elle vraie pour au moins une fiche de droite ? Que se passe-t-il alors dans la boucle ?

Corrigé
def jointure(gauche, droite, cle):
    resultat = []
    for lg in gauche:
        for ld in droite:
            if lg[cle] == ld[cle]:
                fusion = {c: lg[c] for c in lg}
                for c in ld:
                    fusion[c] = ld[c]
                resultat.append(fusion)
    return resultat

villes = jointure(communes, departements, "dep")

La fiche de Nice obtient region \(=\) "Provence-Alpes-Cote d’Azur". Ajaccio disparaît : son code dep "2A" n’a aucune correspondance dans departements, donc la condition lg[cle] == ld[cle] n’est jamais vraie et aucune ligne n’est produite pour lui.

★★ Exercice 22 — Compter par groupe #

▶

À l’aide de la jointure précédente, écrire un code qui affiche, pour la région "Provence-Alpes-Cote d’Azur" (écrite sans accent dans departements.csv), le nombre de communes du fichier qui s’y trouvent.

Coup de pouce

Parcourir le résultat de la jointure avec un compteur, comme dans l’exercice « Compter ».

Corrigé
villes = jointure(communes, departements, "dep")
n = 0
for ligne in villes:
    if ligne["region"] == "Provence-Alpes-Cote d'Azur":
        n += 1
print(n)   # 3  (Nice, Menton, Marseille)

Autre méthode : construire par compréhension la liste des communes de la région, puis prendre sa longueur.

paca = [l["nom"] for l in villes if l["region"] == "Provence-Alpes-Cote d'Azur"]
print(len(paca))   # 3

Mini-projets de synthèse (données réelles)#

★★★ Exercice 23 — Le palmarès à la loupe #

▶ ≈ 20 min

Écrire un programme qui, à partir de prenoms.csv, affiche pour l’année 2025 :

  1. le prénom de fille et le prénom de garçon les plus donnés ;

  2. la moyenne des attributions chez les filles, puis chez les garçons ;

  3. le top 3 des garçons, dans l’ordre.

Coup de pouce

Inutile de tout réécrire : chaque question combine des fonctions déjà écrites (selection, plus_donne, nombre_moyen, trier_par_nombre).

Coup de pouce 2 (début de solution)

filles25 = selection(prenoms, "F", "2025")
garcons25 = selection(prenoms, "G", "2025")
print(plus_donne(filles25), plus_donne(garcons25))

Corrigé
filles25 = selection(prenoms, "F", "2025")
garcons25 = selection(prenoms, "G", "2025")

print("Fille la plus donnee :", plus_donne(filles25))     # ('Louise', 3070)
print("Garcon le plus donne :", plus_donne(garcons25))    # ('Gabriel', 4625)
print("Moyenne filles :", nombre_moyen(filles25))       # 2489.0
print("Moyenne garcons :", nombre_moyen(garcons25))     # 3231.5
top3 = trier_par_nombre(garcons25)[:3]
print("Top 3 garcons :", [l["prenom"] for l in top3])   # Gabriel, Noah, Leo

★★★ Exercice 24 — Qui monte, qui descend ? #

▶ ≈ 20 min

On veut comparer 2023 et 2025 chez les garçons.

  1. Écrire rang(table, sexe, annee) qui renvoie un dictionnaire {prenom : rang} (rang 1 \(=\) le plus donné), obtenu en triant puis en numérotant.

  2. En comparant les rangs 2023 et 2025, déterminer un prénom qui a gagné des places et un qui en a perdu. (Sur les vraies données, Noah passe 6e\(\to\)2e et Mael, écrit sans accent dans le fichier, 5e\(\to\)10e.)

Coup de pouce

Après le tri décroissant, la fiche d’indice i a le rang i + 1. Pour comparer, parcourir les prénoms de 2023 et regarder s’ils ont aussi un rang en 2025 (test in sur les clés).

Coup de pouce 2 (début de solution)

lignes = trier_par_nombre(selection(table, sexe, annee))
resultat = {}
for i in range(len(lignes)):
...

Corrigé
def rang(table, sexe, annee):
    lignes = trier_par_nombre(selection(table, sexe, annee))
    resultat = {}
    for i in range(len(lignes)):
        resultat[lignes[i]["prenom"]] = i + 1
    return resultat

r23 = rang(prenoms, "G", "2023")
r25 = rang(prenoms, "G", "2025")
for prenom in r23:
    if prenom in r25:
        print(prenom, r23[prenom], "->", r25[prenom])
# Noah 6 -> 2 (monte), Mael 5 -> 10 (descend)

★★★ Exercice 25 — Entrants et sortants #

▶ ≈ 15 min

On note noms2023 et noms2025 les listes des prénoms du top 10 des filles en 2023 et en 2025. À l’aide de tests d’appartenance (in / not in, par exemple dans une compréhension), déterminer : les prénoms présents en 2023 et en 2025, ceux qui en sont sortis (en 2023 mais plus en 2025), ceux qui y sont entrés (en 2025 mais pas en 2023). (Résultat attendu : ’Anna’ sort, ’Adele’ entre — les prénoms sont écrits sans accents dans le fichier.)

Coup de pouce

Construire d’abord les deux listes de prénoms (par compréhension avec filtre), puis, pour chaque question, une compréhension qui garde les prénoms d’une liste selon qu’ils sont in ou not in l’autre.

Coup de pouce 2 (début de solution)

noms2023 = [l["prenom"] for l in selection(prenoms, "F", "2023")]
sortis = [p for p in noms2023 if p not in noms2025]

Corrigé
noms2023 = [l["prenom"] for l in prenoms if l["sexe"] == "F" and l["annee"] == "2023"]
noms2025 = [l["prenom"] for l in prenoms if l["sexe"] == "F" and l["annee"] == "2025"]

deux_annees = [p for p in noms2023 if p in noms2025]
sortis = [p for p in noms2023 if p not in noms2025]    # ['Anna']
entres = [p for p in noms2025 if p not in noms2023]    # ['Adele']
print("dans les deux :", deux_annees)
print("sortis :", sortis)
print("entres :", entres)

Autre méthode : sans compréhension, avec une boucle et append (même idée : on garde les prénoms de 2023 absents de 2025).

sortis = []
for p in noms2023:
    if p not in noms2025:
        sortis.append(p)

★★★ Exercice 26 — Écrire un CSV au-delà du programme #

▶ ≈ 20 min

Bonus, au-delà du programme. À l’aide de csv.DictWriter, écrire une fonction exporter(table, fichier) qui sauvegarde une table (par exemple le résultat d’un filtre) dans un nouveau fichier CSV, puis vérifier en le rechargeant avec charger.

Coup de pouce

Ouvrir le fichier en écriture ("w") ; un csv.DictWriter a besoin de la liste des noms de colonnes (paramètre fieldnames) : la fonction descripteurs la donne.

Coup de pouce 2 (début de solution)

with open(fichier, "w", encoding="utf-8", newline="") as f:
ecrivain = csv.DictWriter(f, fieldnames=descripteurs(table))
ecrivain.writeheader()

Corrigé
def exporter(table, fichier):
    with open(fichier, "w", encoding="utf-8", newline="") as f:
        ecrivain = csv.DictWriter(f, fieldnames=[cle for cle in table[0]])
        ecrivain.writeheader()
        for ligne in table:
            ecrivain.writerow(ligne)

exporter(filles23, "filles_2023.csv")
verif = charger("filles_2023.csv")
print(len(verif))   # 10

★★ Exercice 27 — Une moyenne selon un assistant d’IA #

▶

Un élève demande à un assistant d’IA : « J’ai chargé prenoms.csv avec csv.DictReader dans une liste de dictionnaires prenoms. Écris une fonction qui calcule le nombre moyen d’attributions (colonne nombre). » Voici la réponse obtenue.

def moyenne_nombre(table):
    total = 0
    for ligne in table:
        total = total + ligne["nombre"]
    return total / len(table)

« On parcourt la table en accumulant la colonne nombre dans total, puis on divise par le nombre de lignes. Comme DictReader a déjà lu les valeurs numériques, il n’y a rien à convertir : moyenne_nombre(prenoms) renvoie directement la moyenne. »

  1. La réponse est-elle correcte ? Exécuter moyenne_nombre(prenoms) et noter ce qui se passe.

    Coup de pouce

    Quel est le type de ligne["nombre"] ? Relire le réflexe no 1 du mode d’emploi.

  2. Localiser et corriger l’erreur.

  3. Quelle vérification simple, faite avant de faire confiance à la réponse, aurait suffi à détecter l’erreur ?

Corrigé

1. Non. L’appel s’interrompt dès la première ligne de la table :

>>> moyenne_nombre(prenoms)
TypeError: unsupported operand type(s) for +: 'int' and 'str'

total est un entier (0) et ligne["nombre"] une chaîne (’3177’) : Python refuse de les additionner. 2. L’erreur est l’affirmation « rien à convertir » : csv lit du texte, toutes les valeurs sont des str, même les nombres. On convertit avec int :

def moyenne_nombre(table):
    total = 0
    for ligne in table:
        total = total + int(ligne["nombre"])
    return total / len(table)

3. Regarder une fiche avant de calculer : prenoms[0] affiche ’nombre’: ’3177’, avec des guillemets, donc du texte (ou demander type(prenoms[0]["nombre"])). C’est le réflexe du cours : dès qu’on calcule sur une colonne, on convertit.

Entraînement#

Exercices courts pour s’entraîner aux gestes de base, chacun avec plusieurs données. Ils font tous partie du parcours essentiel.

Dans les deux exercices, on utilise la petite table suivante, lue dans un CSV (les valeurs sont donc des chaînes) :

eleves = [{"nom": "Ada", "classe": "1A", "note": "15"},
          {"nom": "Léo", "classe": "1B", "note": "9"},
          {"nom": "Zoé", "classe": "1A", "note": "12"}]

★ Exercice 28 — Lire et filtrer une table #

• essentiel

Sans machine, donner la valeur de chaque expression, puis ▶ vérifier.

  1. eleves[2]["nom"]

  2. eleves[0]["note"] + eleves[1]["note"]

  3. int(eleves[0]["note"]) + int(eleves[1]["note"])

  4. [e["nom"] for e in eleves if e["classe"] == "1A"]

  5. [e["nom"] for e in eleves if int(e["note"]) < 10]

Coup de pouce

D’abord la fiche (indice dans la liste), puis la valeur (nom du descripteur). Entre deux chaînes, + colle les textes.

Corrigé

a) ’Zoé’ b) ’159’ (deux chaînes collées) c) 24 d) [’Ada’, ’Zoé’] e) [’Léo’].

★ Exercice 29 — Trier une table : prédire l’ordre #

• essentiel

Sans machine, donner la liste des noms dans l’ordre obtenu par chaque tri, puis ▶ vérifier.

  1. sorted(eleves, key=lambda e: e["nom"])

  2. sorted(eleves, key=lambda e: int(e["note"]))

  3. sorted(eleves, key=lambda e: int(e["note"]), reverse=True)

  4. sorted(eleves, key=lambda e: e["note"])

Pourquoi les résultats de b) et d) diffèrent-ils ?

Coup de pouce

La clé dit sur quoi comparer les fiches. Sans int, on compare des chaînes caractère par caractère, comme des mots du dictionnaire : comparer d’abord les premiers caractères de "15", "9" et "12".

Corrigé

a) [’Ada’, ’Léo’, ’Zoé’] (ordre alphabétique des noms) ; b) [’Léo’, ’Zoé’, ’Ada’] (notes 9, 12, 15) ; c) [’Ada’, ’Zoé’, ’Léo’] (notes décroissantes) ; d) [’Zoé’, ’Ada’, ’Léo’]. En d), les notes sont comparées comme des chaînes : "12" et "15" commencent par ’1’, qui vient avant ’9’ : "9" passe en dernier, comme si c’était la meilleure note. C’est le piège no 1 : on convertit avec int dans la clé.

▷ Erreur fréquente. trier une colonne de nombres lue dans un CSV sans int : le tri ne signale aucune erreur, mais il range les valeurs dans l’ordre du dictionnaire, pas dans l’ordre numérique.