Aller au contenu

Import d'un fichier csv dans une liste de dictionnaires⚓︎

Avec le module csv⚓︎

On considère à nouveau le fichier temperatures_2020.csv. Ce fichier regroupe les températures minimales, maximales et moyennes dans différentes régions françaises pour certains jours de l'année 2020. Il est dans le dossier de travail et est encodé en utf-8.

Les premières lignes du fichier sont données ci-dessous :

📑 Données CSV
mois,jour,région,tmin,tmax,tmoy
août,13,Pays de la Loire,19.25,25.35,22.3
août,13,Occitanie,17.51,26.55,22.03
Repérer les bonnes informations

Observez l'extrait proposé et répondez aux questions suivantes :

  1. Quel est le séparateur utilisé ?
  2. Combien y-a-t-il de descripteurs ?
  3. Quels sont les types des descripteurs ? (entier, nombre décimal, chaîne de caractères...)
Solution
  1. Le séparateur est la virgule
  2. Il y a six descripteurs
  3. mois et région sont des chaînes de caractères, jour est un entier, les trois autres sont des flottants.

Le module csv de Python permet d'importer facilement des fichiers csv vers des listes de dictionnaires. La démarche est la suivante :

  • ouvrir le fichier csv,

  • créer un objet de type csv.DictReader (voir plus bas),

  • l'utiliser pour lire les données du fichier et les convertir en dictionnaire.

Le code alors très concis :

Important

Prenez le temps de lire les commentaires !

Cliquez sur les +

🐍 Script Python
import csv
with open("temperatures_2020.csv", "r", encoding="utf-8") as fichier:
    lecteur = csv.DictReader(fichier, delimiter=",")  # (1)
    temperatures = [entree for entree in lecteur]  # (2)
  1. Un objet csv.DictReader prend deux arguments : le fichier à lire et le séparateur
  2. Les entree obtenues lors du parcours du lecteur sont des dictionnaires. On crée une liste en compréhension.
Comment Python connaît-il les descripteurs ?

Le comportement par défaut de csv.DictReader est de chercher les noms des descripteurs sur la première ligne du fichier. On utilise ce comportement ici.

Si la première ligne comprend des données et que les descripteurs ont été récupérés ailleurs, on peut les passer en argument (sous forme d'une liste par exemple) en faisant lecteur = csv.DictReader(fichier, delimiter=",", fieldnames=descripteurs)

Python < 3.8

Pour les versions précédentes de Python (avant la 3.8), il faut faire une conversion : temperatures.append(dict(entree))

Typage des données⚓︎

Le module csv nous simplifie grandement la tâche mais il ne fait pas tout : les données ne sont pas typées :

🐍 Script Python
temperatures = [
    {"mois": "août", "jour": "13", "région": "Pays de la Loire", "tmin": "19.25", "tmax": "25.35", "tmoy": "22.3"},
    {"mois": "août", "jour": "13", "région": "Occitanie", "tmin": "17.51", "tmax": "26.55", "tmoy": "22.03"},
    {"mois": "août", "jour": "14", "région": "Pays de la Loire", "tmin": "17.7", "tmax": "25.7", "tmoy": "21.7"},
    ...
]

Le typage s'apparente à celui effectué dans le cas d'une liste de listes. Il est néanmoins plus simple sur un point : on peut directement utiliser le nom des descripteurs plutôt que leurs indices.

Import et typage complets

Compléter le script ci-dessous permettant d'importer et de typer convenablement les données du fichier temperatures_2020.csv.

Ordre de lecture

Le test de validation compare votre liste avec la liste obtenue en lisant le fichier dans l'ordre des lignes (comme proposé partout dans ce cours).

Le test échouera donc si vous ne parcourez pas les lignes du fichier dans l'ordre...

###
import csvbksl-nlattendu = []bksl-nlwith open("temperaturespy-und2020.csv", "r", encoding="utf-8") as fichier:bksl-nl lecteur = csv.DictReader(fichier, delimiter=",")bksl-nl for ligne in lecteur:bksl-nl attendu.append(ligne)bksl-nlbksl-nl# Typagebksl-nlfor entree in attendu:bksl-nl entree["jour"] = int(entree["jour"])bksl-nl entree["tmin"] = float(entree["tmin"])bksl-nl entree["tmax"] = float(entree["tmax"])bksl-nl entree["tmoy"] = float(entree["tmoy"])bksl-nlbksl-nlassert temperatures == attendu, "Erreur d'import ou de typage"bksl-nl 10/10
#--- HDR ---#bksl-nlurlpy-undfichier = "temperaturespy-und2020.csv"bksl-nlencodage = "utf-8"bksl-nlbksl-nlfrom js import fetchbksl-nlbksl-nlreponse = await fetch(f"../{urlpy-undfichier}")bksl-nlcontenu = await reponse.text()bksl-nlbksl-nlwith open(file=urlpy-undfichier, mode="w", encoding=encodage) as fichier:bksl-nl fichier.write(contenu)bksl-nl#--- HDR ---#bksl-nl# Importbksl-nlimport csvbksl-nlwith open("temperaturespy-und2020.csv", "r", encoding="utf-8") as fichier:bksl-nl lecteur = csv.DictReader(fichier, delimiter=",")bksl-nl temperatures = [entree for entree in lecteur]bksl-nlbksl-nl# Typagebksl-nlfor entree in temperatures:bksl-nl entree["jour"] = int(entree["jour"])bksl-nl entree["tmin"] = float(entree[...])bksl-nl entree[...] = ...(...[...])bksl-nl ...bksl-nlimport csvbksl-nlbksl-nlwith open("temperaturespy-und2020.csv", "r", encoding="utf-8") as fichier:bksl-nl lecteur = csv.DictReader(fichier, delimiter=",")bksl-nl temperatures = [entree for entree in lecteur]bksl-nlbksl-nlbksl-nl# Typagebksl-nlfor entree in temperatures:bksl-nl entree["jour"] = int(entree["jour"])bksl-nl entree["tmin"] = float(entree["tmin"])bksl-nl entree["tmax"] = float(entree["tmax"])bksl-nl entree["tmoy"] = float(entree["tmoy"])bksl-nlbksl-nl

A

Z

Remarque

La méthode proposée ici lit deux fois l'ensemble des informations :

  • une fois lors de l'import,
  • une fois lors du typage.

Il est possible de faire ces deux actions en une seule passe :

🐍 Script Python
import csv

temperatures = []
with open("temperatures_2020.csv", "r", encoding="utf-8") as fichier:
    lecteur = csv.DictReader(fichier, delimiter=",")
    for entree in lecteur:
        entree["jour"] = int(entree["jour"])
        entree["tmin"] = float(entree["tmin"])
        entree["tmax"] = float(entree["tmax"])
        entree["tmoy"] = float(entree["tmoy"])
        temperatures.append(entree)

À la main !⚓︎

Il est possible d'effectuer les mêmes actions sans utiliser le module csv. La démarche est un peu plus longue mais permet de bien comprendre le fonctionnement.

L'idée est la suivante :

  • ouvrir le fichier,
  • récupérer les noms des descripteurs à l'aide de la première ligne du fichier,
  • pour chaque ligne restant à lire :
    • la nettoyer et la découper,
    • créer un dictionnaire associant les noms des descripteurs aux valeurs,
    • ajouter ce dictionnaire à la liste générale.

Conseil

On pourra (re)lire avec intérêt la page sur les fichiers

Récupérer les noms des descripteurs

On considère un fichier csv dont le séparateur est la virgule.

Quelle(s) instruction(s) permet(tent) de récupérer les descripteurs ?

  • 🐍 Script Python
    with open("fichier.csv", "r", encoding="utf-8") as fichier:
        for ligne in fichier:
            descripteurs = ligne.split(",")
    
  • 🐍 Script Python
    with open("fichier.csv", "r", encoding="utf-8") as fichier:
        ligne = fichier.readline()
        ligne_propre = ligne.strip()
        descripteurs = ligne_propre.split(",")
    
  • 🐍 Script Python
    with open("fichier.csv", "r", encoding="utf-8") as fichier:
        ligne = fichier.readline()
        descripteurs = ligne.strip().split(",")
    
  • 🐍 Script Python
    with open("fichier.csv", "r", encoding="utf-8") as fichier:
        descripteurs = fichier.readline().strip().split(",")
    
  • ❌ On récupère ainsi les données de chaque ligne...
    🐍 Script Python
    with open("fichier.csv", "r", encoding="utf-8") as fichier:
        for ligne in fichier:
            descripteurs = ligne.split(",")
    
  • ✅ En trois temps :
    🐍 Script Python
    with open("fichier.csv", "r", encoding="utf-8") as fichier:
        ligne = fichier.readline()  # on lit la 1ère ligne
        ligne_propre = ligne.strip()  # on efface le \n
        descripteurs = ligne_propre.split(",")  # on découpe à chaque ","
    
  • ✅ En trois temps aussi mais sur deux lignes :
    🐍 Script Python
    with open("fichier.csv", "r", encoding="utf-8") as fichier:
        ligne = fichier.readline()  # lecture de la 1ère ligne
        descripteurs = ligne.strip().split(",")  # nettoyage et découpage
    
  • ✅ Tout en une seule ligne, ce n'est pas très clair !
    🐍 Script Python
    with open("fichier.csv", "r", encoding="utf-8") as fichier:
        descripteurs = fichier.readline().strip().split(",")
    
Import dans une liste de dictionnaires à la main

Compléter le script ci-dessous permettant d'importer les données du fichier temperatures_2020.csv dans une liste de dictionnaires. On n'utilisera pas le module csv.

Ordre de lecture

Le test de validation compare votre liste avec la liste obtenue en lisant le fichier dans l'ordre des lignes (comme proposé partout dans ce cours).

Le test échouera donc si vous ne parcourez pas les lignes du fichier dans l'ordre...

###
import csvbksl-nlattendu = []bksl-nlwith open("temperaturespy-und2020.csv", "r", encoding="utf-8") as fichier:bksl-nl lecteur = csv.DictReader(fichier, delimiter=",")bksl-nl for ligne in lecteur:bksl-nl attendu.append(ligne)bksl-nlbksl-nlassert temperatures == attendu, "Erreur d'import ou de typage"bksl-nl 10/10
#--- HDR ---#bksl-nlurlpy-undfichier = "temperaturespy-und2020.csv"bksl-nlencodage = "utf-8"bksl-nlbksl-nlfrom js import fetchbksl-nlbksl-nlreponse = await fetch(f"../{urlpy-undfichier}")bksl-nlcontenu = await reponse.text()bksl-nlbksl-nlwith open(file=urlpy-undfichier, mode="w", encoding=encodage) as fichier:bksl-nl fichier.write(contenu)bksl-nl#--- HDR ---#bksl-nl# Importbksl-nltemperatures = []bksl-nlwith open(file="temperaturespy-und2020.csv", mode="r", encoding="utf-8") as fichier:bksl-nl ... # récupération des descripteursbksl-nl for ligne in fichier:bksl-nl lignepy-undpropre = ligne.strip()bksl-nl valeurs = lignepy-undpropre.split(",")bksl-nl dico = {}bksl-nl for i in range(len(descripteurs)):bksl-nl dico[descripteurs[...]] = ...[i]bksl-nl temperatures.append(...)bksl-nlbksl-nltemperatures = []bksl-nlwith open(file="temperaturespy-und2020.csv", mode="r", encoding="utf-8") as fichier:bksl-nl ligne = fichier.readline()bksl-nl lignepy-undpropre = ligne.strip()bksl-nl descripteurs = lignepy-undpropre.split(",")bksl-nl for ligne in fichier:bksl-nl lignepy-undpropre = ligne.strip()bksl-nl valeurs = lignepy-undpropre.split(",")bksl-nl dico = {}bksl-nl for i in range(len(descripteurs)):bksl-nl dico[descripteurs[i]] = valeurs[i]bksl-nl temperatures.append(dico)bksl-nlbksl-nl

A

On peut aussi créer un dictionnaire en compréhension :

🐍 Script Python
temperatures = []
with open(file="temperatures_2020.csv", mode="r", encoding="utf-8") as fichier:
    ligne = fichier.readline()
    ligne_propre = ligne.strip()
    descripteurs = ligne_propre.split(",")
    for ligne in fichier:
        ligne_propre = ligne.strip()
        valeurs = ligne_propre.split(",")
        dico = {descripteurs[i]: valeurs[i] for i in range(len(descripteurs))}
        temperatures.append(dico)

Z