Retour à la page d'accueilNSI · Première (spé maths) · Changer de matière
Commencer gratuitement
Première (spé maths) · NSI
Types construits et algorithmique en première NSI · Traitement de données en tables

Traitement de données en tables : CSV, recherche, tri et fusion

Le fichier des inscrits à la médiathèque de Rennes contient 40 000 lignes. Comment retrouver en quelques instructions tous les lecteurs d'une même commune, détecter une adresse mail saisie deux fois, ou associer chaque emprunt au titre du livre correspondant ? Il faut d'abord savoir représenter ces données en Python, puis les interroger.

Qu'est-ce qu'une table ? Le format CSV

Une table est un ensemble de données organisé en lignes (les enregistrements) et en colonnes (les champs). Chaque colonne porte un nom, le descripteur, et ne contient que des valeurs d'un même domaine (entiers, textes, etc.).

Le format CSV (Comma-Separated Values) stocke une table dans un fichier texte : un enregistrement par ligne, les champs séparés par un séparateur (virgule, point-virgule ou tabulation). La première ligne contient souvent les descripteurs.

nom,ville,age

Durand,Lille,17

Petit,Nice,16

Roux,Lille,16

En Python, on représente une table par une liste de dictionnaires : chaque dictionnaire est un enregistrement, ses clés sont les descripteurs.

table = [

    {"nom": "Durand", "ville": "Lille", "age": 17},

    {"nom": "Petit", "ville": "Nice", "age": 16},

    {"nom": "Roux", "ville": "Lille", "age": 16},

]

print(table[1]["ville"])   # Nice

On peut aussi utiliser une liste de p-uplets, plus légère mais moins lisible : on accède alors aux champs par leur indice.

Importer un fichier CSV

Le module csv de la bibliothèque standard lit le fichier. DictReader utilise la première ligne comme descripteurs et produit un dictionnaire par ligne.

import csv

with open("eleves.csv", encoding="utf-8") as f:

    lecteur = csv.DictReader(f)          # séparateur « , » par défaut

    table = [ligne for ligne in lecteur]

L'instruction with ferme automatiquement le fichier à la sortie du bloc, même en cas d'erreur. Sans with, il faudrait appeler f.close(). Si le séparateur est un point-virgule (courant dans les fichiers français, car la virgule sert de virgule décimale), on écrit csv.DictReader(f, delimiter=";").

Entraîne-toi★☆☆Pas commencé
Voici le contenu d'un fichier CSV : a) Quel est le séparateur de champs ? Quels sont les descripteurs ? b) Combien d'enregistrements contient la table ? c) Représenter…

Voici le contenu d'un fichier CSV :

nom,ville,age

Durand,Lille,17

Petit,Nice,16

Roux,Lille,16
  1. a) Quel est le séparateur de champs ? Quels sont les descripteurs ?
  1. b) Combien d'enregistrements contient la table ?
  1. c) Représenter cette table sous forme d'une liste de dictionnaires en Python.
Résoudre
Entraîne-toi★☆☆Pas commencé
Écrire en Python les instructions qui ouvrent le fichier eleves.csv (encodage UTF-8), le lisent avec csv.DictReader et stockent les enregistrements dans une liste table.…

Écrire en Python les instructions qui ouvrent le fichier eleves.csv (encodage UTF-8), le lisent avec csv.DictReader et stockent les enregistrements dans une liste table. Préciser comment fermer correctement le fichier.

Résoudre

Rechercher dans une table

Rechercher, c'est sélectionner les enregistrements qui vérifient un critère exprimé avec and, or, not et des comparaisons. La compréhension est l'outil naturel.

Pour ajouter un enregistrement, on utilise append : table.append({"nom": "Blanc", "ville": "Brest", "age": 17}).

Doublons et cohérence

Deux contrôles classiques :

  • chercher les doublons : une même valeur (un mail, un identifiant) qui apparaît plusieurs fois ;

  • tester la cohérence : champs non vides, valeurs dans le bon domaine, présence d'un @ dans un mail.

Avec deux boucles imbriquées, la recherche de doublons compare toutes les paires : environ $n^2$ opérations. Avec un dictionnaire (ou un ensemble) de valeurs déjà rencontrées, un seul parcours suffit, car tester x in d est quasi immédiat :

def doublons(table, champ):

    vus = {}

    res = []

    for e in table:

        v = e[champ]

        if v in vus and v not in res:

            res.append(v)

        vus[v] = True

    return res

Le coût est proportionnel à $n$ : sur 40 000 lignes, on évite plus d'un milliard de comparaisons.

def coherente(table):

    for e in table:

        for valeur in e.values():

            if valeur == "":

                return False

        if "@" not in e["mail"]:

            return False

    return True
Entraîne-toi★☆☆Pas commencé
On dispose de la table t, liste de dictionnaires, avec les champs nom, ville, age de l'exercice précédent. Chaque enregistrement est de la forme {"nom": "Martin",…

On dispose de la table t, liste de dictionnaires, avec les champs nom, ville, age de l'exercice précédent. Chaque enregistrement est de la forme {"nom": "Martin", "ville": "Lille", "age": 16} : le champ age est un entier.

  1. a) Écrire une compréhension qui renvoie la liste des enregistrements dont la ville est "Lille".
  1. b) Écrire une compréhension qui renvoie la liste des noms des élèves d'âge 16.
  1. c) Écrire une instruction qui ajoute l'enregistrement de Blanc, Brest, 17 ans.
Résoudre
Entraîne-toi★★☆Pas commencé
Une table inscrits est une liste de dictionnaires avec les champs id, nom, mail. a) Écrire une fonction doublons(table) qui renvoie la liste des adresses mail…

Une table inscrits est une liste de dictionnaires avec les champs id, nom, mail.

  1. a) Écrire une fonction doublons(table) qui renvoie la liste des adresses mail apparaissant plus d'une fois.
  1. b) Écrire une fonction coherente(table) qui renvoie True si tous les champs de chaque enregistrement sont non vides et si le mail contient le caractère @.
  1. c) Expliquer pourquoi l'utilisation d'un dictionnaire améliore l'efficacité de la recherche de doublons par rapport à deux boucles imbriquées.
Résoudre

Trier une table

La fonction sorted(table, key=f, reverse=False) renvoie une nouvelle liste triée ; table.sort(key=f) trie la liste en place. La fonction key reçoit un enregistrement et renvoie la valeur qui sert à comparer.

def population(v):

    return v["population"]

par_pop = sorted(villes, key=population, reverse=True)   # décroissant

# ou : sorted(villes, key=lambda v: v["population"], reverse=True)

Trier selon deux critères

On peut renvoyer un couple : Python compare d'abord le premier élément, puis le second en cas d'égalité.

sorted(t, key=lambda e: (e["ville"], e["age"]))   # ville ↑, puis âge ↑

Pour une ville croissante et un âge décroissant, on utilise -e["age"] (valable pour un nombre) : key=lambda e: (e["ville"], -e["age"]).

Autre méthode : le tri de Python est stable : deux éléments égaux pour la clé gardent leur ordre relatif d'origine. On trie donc d'abord selon le critère secondaire, puis selon le critère principal :

t1 = sorted(t, key=lambda e: e["age"], reverse=True)

t2 = sorted(t1, key=lambda e: e["ville"])
Entraîne-toi★★☆Pas commencé
Une table villes contient les champs nom, region, population (entier) et superficie (en km²). a) Écrire une compréhension qui renvoie les villes de la région "Bretagne"…

Une table villes contient les champs nom, region, population (entier) et superficie (en km²).

  1. a) Écrire une compréhension qui renvoie les villes de la région "Bretagne" de plus de 50 000 habitants.
  1. b) Écrire une fonction qui renvoie la moyenne des densités (population divisée par superficie) de chaque ville de la table.
  1. c) Trier la table par population décroissante avec sorted et une fonction key.
Résoudre
Entraîne-toi★★★Pas commencé
On veut trier une table t selon deux critères : d'abord par ville croissante, puis, à ville égale, par age décroissant. a) Écrire l'appel à sorted correspondant en…

On veut trier une table t selon deux critères : d'abord par ville croissante, puis, à ville égale, par age décroissant.

  1. a) Écrire l'appel à sorted correspondant en utilisant une fonction key renvoyant un couple.
  1. b) Justifier que le tri de Python est stable et montrer comment l'utiliser pour obtenir le même résultat avec deux tris successifs. Préciser l'ordre des deux tris.
  1. c) Appliquer la méthode aux enregistrements (Lille, 16), (Nice, 17), (Lille, 18), (Nice, 15) et donner le résultat.
Résoudre

Fusionner des tables

Souvent, l'information est répartie sur plusieurs tables reliées par un champ commun. Ici, eleves contient id_club et clubs décrit chaque club :

eleves id nom id_club
1 Léa 2
2 Hugo 1
clubs id_club club salle
1 Échecs B12
2 Théâtre A03

Fusionner, c'est construire une nouvelle table qui combine les enregistrements des deux tables dont le champ commun est égal.

def fusion(eleves, clubs):

    res = []

    for e in eleves:

        for c in clubs:

            if e["id_club"] == c["id_club"]:

                res.append({"nom": e["nom"],

                            "club": c["club"],

                            "salle": c["salle"]})

    return res

Avec $n$ élèves et $m$ clubs, les deux boucles imbriquées font $n \times m$ comparaisons. Pour faire mieux, on indexe d'abord la petite table par un dictionnaire dont la clé est le champ commun :

index = {c["id_club"]: c for c in clubs}

res = []

for e in eleves:

    c = index[e["id_club"]]

    res.append({"nom": e["nom"], "club": c["club"], "salle": c["salle"]})

Le coût devient proportionnel à $n + m$.

Les contrôles de cohérence entre tables vérifient par exemple que chaque id_club d'une table existe dans l'autre, ou qu'un identifiant censé être unique l'est bien.

Entraîne-toi★★☆Pas commencé
Deux tables : eleves avec les champs id, nom, id_club, et clubs avec les champs id_club, club, salle. a) Écrire une fonction fusion(eleves, clubs) qui renvoie une table…

Deux tables : eleves avec les champs id, nom, id_club, et clubs avec les champs id_club, club, salle.

  1. a) Écrire une fonction fusion(eleves, clubs) qui renvoie une table dont chaque enregistrement contient nom, club et salle.
  1. b) Que devient un élève dont id_club n'existe pas dans clubs ? Proposer un traitement.
  1. c) Donner la complexité de la fusion avec deux boucles imbriquées, puis expliquer comment l'améliorer.
Résoudre
Entraîne-toi★★★Pas commencé
Une table emprunts (champs id_livre, id_lecteur, date) et une table livres (champs id_livre, titre, auteur) décrivent une médiathèque à Rennes. a) Écrire une fonction…

Une table emprunts (champs id_livre, id_lecteur, date) et une table livres (champs id_livre, titre, auteur) décrivent une médiathèque à Rennes.

  1. a) Écrire une fonction qui renvoie le titre du livre le plus emprunté.
  1. b) Écrire une fonction qui renvoie, pour chaque lecteur, la liste des titres empruntés.
  1. c) Identifier deux contrôles de cohérence entre les tables et écrire une fonction qui les vérifie.
Résoudre

Calculer des agrégats

Un traitement fréquent : regrouper les enregistrements selon un champ et calculer une valeur par groupe (somme, moyenne, maximum). Le dictionnaire convient très bien : la clé est le groupe.

Entraîne-toi★★★Pas commencé
Une table releves contient les champs station, date et temp (température en °C, lue comme chaîne dans le CSV) pour des stations météo françaises. a) Écrire une fonction…

Une table releves contient les champs station, date et temp (température en °C, lue comme chaîne dans le CSV) pour des stations météo françaises.

  1. a) Écrire une fonction qui convertit les températures en flottants et signale les valeurs manquantes (chaîne vide) en les écartant.
  1. b) Écrire une fonction qui renvoie un dictionnaire donnant la température moyenne par station.
  1. c) En déduire la station la plus chaude en moyenne. Préciser ce qui se passe en cas d'égalité avec votre code.
Résoudre

Retour au début : pour les 40 000 inscrits de Rennes, on lit le CSV avec DictReader, on filtre par compréhension, on détecte les doublons avec un dictionnaire et on associe chaque emprunt à son livre avec un index sur id_livre.

L'essentiel

  • Une table est une liste de dictionnaires (un par enregistrement) ; le fichier CSV la stocke en texte avec un séparateur et une ligne de descripteurs.

  • csv.DictReader lit le fichier dans un bloc with ; toutes les valeurs sont des chaînes : convertir avec int ou float.

  • Rechercher : compréhension avec un critère en logique (and, or, not) ; doublons et cohérence se contrôlent, un dictionnaire évite le coût en $n^2$.

  • Trier : sorted(t, key=…, reverse=…) ; pour deux critères, une clé en couple ou deux tris successifs (secondaire d'abord) grâce à la stabilité.

  • Fusionner : combiner deux tables via un champ commun ; indexer avec un dictionnaire ramène le coût de $n \times m$ à $n + m$ ; traiter les clés absentes.

Voir tous les exercices de cette partie