14 - CSV
Introduction
Beaucoup de données se présentent sous forme de table : une ligne par objet décrit, une colonne par caractéristique. Un relevé météo, la liste des élèves d’une classe ou les horaires d’une ligne de bus en sont des exemples.
Pour échanger ces tables entre logiciels, on utilise très souvent le format CSV (Comma Separated Values, « valeurs séparées par des virgules »). Un fichier CSV est un simple fichier texte :
- chaque ligne du fichier est une ligne de la table, appelée enregistrement ;
- sur une ligne, les valeurs sont séparées par un caractère choisi, le délimiteur : une virgule, ou un point-virgule.
Un tableur (LibreOffice Calc, Excel) ouvre un fichier CSV et sait en enregistrer un ; en Python, le module csv permet de le lire et de l’écrire.
Structure d’un fichier CSV
Voici le fichier meteo.csv, qui donne pour quelques villes la température maximale de la journée, en degrés, et la hauteur de pluie tombée, en millimètres :
ville,région,température,pluie
Lyon,Auvergne-Rhône-Alpes,24,0
Brest,Bretagne,17,6
Nice,Provence-Alpes-Côte d'Azur,26,0
Lille,Hauts-de-France,15,12
La première ligne est l’en-tête : elle donne le nom de chaque colonne. Chacune des lignes suivantes décrit une ville. Le même fichier, présenté en tableau :
| ville | région | température | pluie |
|---|---|---|---|
| Lyon | Auvergne-Rhône-Alpes | 24 | 0 |
| Brest | Bretagne | 17 | 6 |
| Nice | Provence-Alpes-Côte d’Azur | 26 | 0 |
| Lille | Hauts-de-France | 15 | 12 |
Deux précautions :
- en France, les tableurs utilisent souvent le point-virgule comme délimiteur, parce que la virgule sert à écrire les nombres décimaux (
12,5) ; - une valeur qui contient le délimiteur s’écrit entre guillemets, par exemple
"Paris, 5e".
Lecture avec csv.reader()
La fonction csv.reader() lit le fichier ligne par ligne et donne chaque ligne sous forme de liste de valeurs :
import csv
with open('meteo.csv', encoding='utf-8') as fichier:
lecteur = csv.reader(fichier, delimiter=',')
for ligne in lecteur:
print(ligne)
['ville', 'région', 'température', 'pluie']
['Lyon', 'Auvergne-Rhône-Alpes', '24', '0']
['Brest', 'Bretagne', '17', '6']
['Nice', "Provence-Alpes-Côte d'Azur", '26', '0']
['Lille', 'Hauts-de-France', '15', '12']
La première liste est l’en-tête. Pour la passer, on appelle une fois next(lecteur) avant la boucle : la boucle commence alors à la deuxième ligne. Dans chaque liste, ligne[0] est la ville, ligne[2] la température :
import csv
with open('meteo.csv', encoding='utf-8') as fichier:
lecteur = csv.reader(fichier, delimiter=',')
next(lecteur) # passe la ligne d'en-tête
for ligne in lecteur:
print(f'À {ligne[0]}, il a fait {ligne[2]} degrés')
À Lyon, il a fait 24 degrés
À Brest, il a fait 17 degrés
À Nice, il a fait 26 degrés
À Lille, il a fait 15 degrés
Toutes les valeurs sont des chaînes
Dans les listes ci-dessus, la température est écrite '24', entre apostrophes : le module csv donne toujours des chaînes de caractères, même quand la valeur ressemble à un nombre. Pour calculer, il faut d’abord convertir avec int() ou float().
import csv
with open('meteo.csv', encoding='utf-8') as fichier:
lecteur = csv.reader(fichier, delimiter=',')
next(lecteur)
total = 0
nombre = 0
for ligne in lecteur:
total = total + int(ligne[2])
nombre = nombre + 1
print(f'Température moyenne : {total / nombre} degrés')
Température moyenne : 20.5 degrés
Sans int(), l’instruction total + ligne[2] provoquerait une erreur, car on ne peut pas ajouter une chaîne à un entier.
Lecture avec csv.DictReader()
La fonction csv.DictReader() utilise l’en-tête : elle donne chaque ligne sous forme de dictionnaire, dont les clés sont les noms des colonnes. L’en-tête n’apparaît donc plus comme une ligne de données.
import csv
with open('meteo.csv', encoding='utf-8') as fichier:
lecteur = csv.DictReader(fichier, delimiter=',')
for ligne in lecteur:
print(f'{ligne["ville"]} ({ligne["région"]}) : {ligne["pluie"]} mm de pluie')
Lyon (Auvergne-Rhône-Alpes) : 0 mm de pluie
Brest (Bretagne) : 6 mm de pluie
Nice (Provence-Alpes-Côte d'Azur) : 0 mm de pluie
Lille (Hauts-de-France) : 12 mm de pluie
Écrire ligne["pluie"] est plus clair que ligne[3], et le programme reste juste si quelqu’un change l’ordre des colonnes dans le fichier.
Écriture avec csv.writer()
Pour créer un fichier CSV, on ouvre un fichier en écriture et on lui associe un csv.writer(). La méthode writerow() écrit une ligne à partir d’une liste :
import csv
releves = [
['ville', 'région', 'température', 'pluie'],
['Rennes', 'Bretagne', 18, 4],
['Toulouse', 'Occitanie', 27, 0],
]
with open('releves.csv', 'w', encoding='utf-8', newline='') as fichier:
ecrivain = csv.writer(fichier, delimiter=',')
for ligne in releves:
ecrivain.writerow(ligne)
Le fichier releves.csv contient alors :
ville,région,température,pluie
Rennes,Bretagne,18,4
Toulouse,Occitanie,27,0
Les nombres sont convertis en texte à l’écriture. Le paramètre newline='' évite, sous Windows, une ligne vide après chaque enregistrement.
Le mode 'w' remplace le contenu du fichier. Pour ajouter une ligne à la fin d’un fichier existant, on l’ouvre en mode 'a' (append, « ajouter ») :
with open('releves.csv', 'a', encoding='utf-8', newline='') as fichier:
ecrivain = csv.writer(fichier, delimiter=',')
ecrivain.writerow(['Strasbourg', 'Grand Est', 21, 2])
releves.csv compte alors trois villes : Rennes, Toulouse et Strasbourg.
Écriture avec csv.DictWriter()
csv.DictWriter() écrit des dictionnaires. Le paramètre fieldnames lui donne la liste des colonnes ; la méthode writeheader() écrit la ligne d’en-tête, puis writerow() écrit un dictionnaire par ligne :
import csv
releves = [
{'ville': 'Rennes', 'température': 18},
{'ville': 'Toulouse', 'température': 27},
]
with open('temperatures.csv', 'w', encoding='utf-8', newline='') as fichier:
ecrivain = csv.DictWriter(fichier, fieldnames=['ville', 'température'], delimiter=',')
ecrivain.writeheader() # ligne d'en-tête
for ligne in releves:
ecrivain.writerow(ligne) # un dictionnaire par ligne
ville,température
Rennes,18
Toulouse,27
Comparaison : reader() vs DictReader()
Pour la ligne de Brest, les deux fonctions donnent :
# csv.reader() : une liste, lue par indice
['Brest', 'Bretagne', '17', '6']
# ligne[2] vaut '17'
# csv.DictReader() : un dictionnaire, lu par nom de colonne
{'ville': 'Brest', 'région': 'Bretagne', 'température': '17', 'pluie': '6'}
# ligne['température'] vaut '17'
csv.reader() suffit pour un petit fichier dont on connaît l’ordre des colonnes. csv.DictReader() est préférable dès qu’il y a plusieurs colonnes : le code se lit mieux et ne dépend pas de leur ordre. Dans les deux cas, les valeurs restent des chaînes.
Charger un CSV dans une liste de dictionnaires
On charge souvent tout un fichier en mémoire, sous forme d’une liste de dictionnaires : chaque dictionnaire est une ligne de la table. On peut ensuite la parcourir autant de fois qu’on veut, la filtrer ou la trier.
import csv
def charger_csv(nom_fichier):
"""Renvoie le contenu du fichier CSV sous forme de liste de dictionnaires."""
with open(nom_fichier, encoding='utf-8') as fichier:
lecteur = csv.DictReader(fichier, delimiter=',')
return list(lecteur)
table = charger_csv('meteo.csv')
print(len(table))
print(table[1])
4
{'ville': 'Brest', 'région': 'Bretagne', 'température': '17', 'pluie': '6'}
Pour garder, par exemple, les villes où il n’a pas plu :
seches = [ligne['ville'] for ligne in table if int(ligne['pluie']) == 0]
print(seches)
['Lyon', 'Nice']
Trier une table suivant une colonne
La fonction sorted() trie une liste. Pour trier une table, c’est-à-dire une liste de dictionnaires, suivant une colonne, on lui donne avec le paramètre key une fonction qui, appliquée à une ligne, renvoie la valeur à comparer :
eleves = [
{"nom": "Alice", "note": 15},
{"nom": "Bob", "note": 12},
{"nom": "Chloé", "note": 17},
]
def note(ligne):
return ligne["note"]
par_note = sorted(eleves, key=note)
print([e["nom"] for e in par_note]) # ['Bob', 'Alice', 'Chloé']
decroissant = sorted(eleves, key=note, reverse=True)
print([e["nom"] for e in decroissant]) # ['Chloé', 'Alice', 'Bob']
On écrit le nom de la fonction sans parenthèses : c’est sorted() qui l’appelle sur chaque ligne. Le paramètre reverse=True inverse l’ordre. Le même paramètre key existe pour max() et min() : max(eleves, key=note) renvoie la ligne de la meilleure note.
Résumé des fonctions du module csv
| Fonction | Rôle | Type renvoyé par ligne |
|---|---|---|
csv.reader() | Lecture ligne par ligne | liste de chaînes |
csv.DictReader() | Lecture avec en-tête | dictionnaire |
csv.writer() | Écriture ligne par ligne | — |
csv.DictWriter() | Écriture avec en-tête | — |