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) : descripteursprenom,sexe(F/G),annee(2023 ou 2025),nombre. Et surcommunes.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
-
Quel est le séparateur ? Combien y a-t-il de descripteurs ? d’enregistrements ?
-
Citer les descripteurs. Donner la valeur du descripteur
dureepour l’enregistrementDixit. -
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
-
Écrire
total(table, sexe, annee)qui renvoie la somme desnombredes fiches correspondantes.Coup de pouce
Patron de l’accumulateur : un total initialisé à
0, augmenté pour chaque fiche retenue. On peut réutiliserselection. -
En déduire combien de garçons ont reçu l’un de ces 10 prénoms en 2023.
-
Écrire
nombre_moyen(table)(moyenne desnombre). Que vaut-elle surfilles23?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 #
▶
-
Écrire
plus_donne(table)qui renvoie le couple (prénom, nombre) de la fiche au plus grandnombre(invariant du champion).Coup de pouce
Prendre la première fiche comme championne provisoire, puis la remplacer dès qu’une fiche a un
nombreplus grand (comparer des entiers). Renvoyer à la fin un p-uplet construit à partir de la championne. -
Écrire
moins_donne(table)sur le même modèle. -
Tester sur
prenomsentier. Pourquoi faut-il convertir avecintpour 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.
-
Écrire
jointure(gauche, droite, cle)(patron du cours). -
Vérifier que la fiche de
Niceobtient bien saregion. -
Le fichier
communes.csvcontientAjaccio(code2A), absent dedepartements.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 :
-
le prénom de fille et le prénom de garçon les plus donnés ;
-
la moyenne des attributions chez les filles, puis chez les garçons ;
-
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.
-
Écrire
rang(table, sexe, annee)qui renvoie un dictionnaire{prenom : rang}(rang 1 \(=\) le plus donné), obtenu en triant puis en numérotant. -
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. »
-
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. -
Localiser et corriger l’erreur.
-
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.
-
eleves[2]["nom"] -
eleves[0]["note"] + eleves[1]["note"] -
int(eleves[0]["note"]) + int(eleves[1]["note"]) -
[e["nom"] for e in eleves if e["classe"] == "1A"] -
[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.
-
sorted(eleves, key=lambda e: e["nom"]) -
sorted(eleves, key=lambda e: int(e["note"])) -
sorted(eleves, key=lambda e: int(e["note"]), reverse=True) -
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.