Deux tables valent mieux qu'une : rechercher, trier et fusionner des CSV en Python
Ton club d'e-sport organise un tournoi. Le secrétaire tient la liste des inscrits dans un fichier, l'arbitre envoie les scores dans un autre. Qui gagne ? Les Lyonnais ont-ils brillé ? Pour répondre, il faut rechercher, filtrer, trier et surtout fusionner deux tables. En Seconde, tu as appris à lire un CSV ; on passe maintenant au travail de pro, avec des fonctions réutilisables.
1. Une table, c'est une liste de dictionnaires
Voici les deux fichiers, joueurs.csv puis scores.csv, à enregistrer en UTF-8 dans le dossier de ton programme :
pseudo;prenom;ville;age
Zelda42;Inès;Lyon;16
Kraken;Hugo;Lille;17
Nova;Lina;Lyon;15
Pixel;Tom;Nantes;16
Orion;Sami;Lyon;18
pseudo;score
Nova;8400
Zelda42;9100
Kraken;9100
Ghost;7700
Pixel;900
import csv
def charger(nom_fichier):
with open(nom_fichier, encoding="utf-8", newline="") as f:
return list(csv.DictReader(f, delimiter=";"))
joueurs = charger("joueurs.csv")
scores = charger("scores.csv")
print(joueurs[1])
Affichage : {'pseudo': 'Kraken', 'prenom': 'Hugo', 'ville': 'Lille', 'age': '17'}. Chaque ligne devient un dictionnaire (un p-uplet nommé) dont les clés sont les descripteurs de l'en-tête. Attention : toutes les valeurs sont des chaînes, même '17'.
2. Rechercher une ligne
def cherche(table, descripteur, valeur):
for ligne in table:
if ligne[descripteur] == valeur:
return ligne
return None
print(cherche(joueurs, "pseudo", "Nova")["prenom"]) # Lina
print(cherche(joueurs, "pseudo", "Ghost")) # None
La fonction parcourt la table et renvoie la première ligne qui convient, ou None. Dans le pire cas (valeur absente), elle compare les n lignes : son coût est linéaire.
3. Filtrer : garder certaines lignes
Une liste en compréhension sélectionne les lignes qui vérifient un critère, combiné avec and, or, not :
lyon = [j["pseudo"] for j in joueurs
if j["ville"] == "Lyon" and int(j["age"]) >= 16]
print(lyon) # ['Zelda42', 'Orion']
Lina, Lyonnaise de 15 ans, est écartée. Remarque le int(...) : comparer la chaîne '15' à l'entier 16 provoquerait une erreur TypeError. Et j["pseudo"] ne garde qu'une colonne : c'est une projection.
4. Trier selon une ou plusieurs colonnes
sorted(table, key=...) renvoie une nouvelle table triée selon la valeur que calcule key pour chaque ligne :
par_score = sorted(scores, key=lambda s: int(s["score"]), reverse=True)
print([s["pseudo"] for s in par_score])
# ['Zelda42', 'Kraken', 'Nova', 'Ghost', 'Pixel']
Sans int, Python comparerait des chaînes caractère par caractère, et '900' passerait devant '8400' car le caractère 9 suit le 8 !
Zelda42 et Kraken sont ex aequo : le tri de Python est stable, il garde leur ordre du fichier. Pour départager, on donne une clé tuple, comparée élément par élément : key=lambda s: (-int(s["score"]), s["pseudo"]) range par score décroissant, puis par pseudo : Kraken passe alors devant Zelda42.
5. Fusionner deux tables
Deux tables aux mêmes descripteurs (les inscrits de deux clubs) se fusionnent bout à bout : tous = club_a + club_b. Vérifie ensuite qu'aucune ligne n'est en double.
Pour combiner des tables différentes, on fait une jointure sur un descripteur commun, ici pseudo. On construit d'abord un index, un dictionnaire qui associe chaque pseudo à sa ligne : plus besoin de parcourir toute la table pour chaque score.
index = {j["pseudo"]: j for j in joueurs}
fusion = []
for s in scores:
if s["pseudo"] in index:
j = index[s["pseudo"]]
fusion.append({"pseudo": j["pseudo"], "ville": j["ville"],
"score": int(s["score"])})
print(len(fusion)) # 4
Ghost n'a aucune fiche d'inscription : sa ligne disparaît de la jointure. C'est une incohérence qu'il faut signaler à l'arbitre. Reste à enregistrer le résultat :
with open("fusion.csv", "w", encoding="utf-8", newline="") as f:
sortie = csv.DictWriter(f, fieldnames=["pseudo", "ville", "score"],
delimiter=";")
sortie.writeheader()
sortie.writerows(fusion)
À retenir
csv.DictReadertransforme chaque ligne en dictionnaire ; toutes les valeurs sont des chaînes, à convertir avecintoufloat.- Rechercher, c'est parcourir la table jusqu'à la première ligne qui convient : coût linéaire.
- Filtrer : liste en compréhension avec
and,or,not; trier :sorted(..., key=...), avec un tuple pour plusieurs critères. - Fusionner : concaténer deux tables aux mêmes descripteurs, ou faire une jointure sur un descripteur commun, avec un dictionnaire pour index.
★ Pour aller plus loin
En Terminale, tu écriras ces opérations en SQL, le langage des bases de données : SELECT ... FROM joueurs JOIN scores ON ... réalise notre jointure en une ligne, sur des millions d'enregistrements.
Notre jointure ne garde que les pseudos présents dans les deux tables (jointure interne). Les professionnels utilisent aussi des jointures externes, qui conservent Ghost avec une ville vide. La bibliothèque pandas les propose avec merge.
Enfin, une clé doit être unique : si deux joueurs partageaient le pseudo Nova, l'index n'en garderait qu'un, en silence. D'où les identifiants des bases de données.
Sources pour creuser
- Spécialité NSI (Numérique et sciences informatiques) : 30 leçons avec exercices corrigés – Première, Thibaut Balabonski, Sylvain Conchon, Jean-Christophe Filliâtre et Kim Nguyen (Ellipses).
- Apprendre à programmer avec Python 3, Gérard Swinnen (Eyrolles).
- docs.python.org/fr : la page du module csv.
- YouTube : les chaînes Docstring et Graven - Développement (Python en français).
- data.gouv.fr pour de vrais fichiers CSV ; le programme de NSI sur eduscol.education.fr.