# C'est à vous !
Traitement de fichiers CSV
Dans ce TD, nous allons apprendre à manipuler un type de fichier texte très utilisé pour stocker et diffuser des données : le CSV.
CSV est l’abréviation de comma-separated values, soit “valeurs séparées par des virgules” – même si, bien souvent, le délimiteur standard (la virgule) est remplacée par un autre délimiteur.
Un fichier CSV reproduit la structure d’un tableau :
- Chaque ligne du fichier représente une ligne du tableau.
- Dans chaque ligne figurent les valeurs stockées dans les colonnes, séparées par le délimiteur.
Un module de la bibliothèque standard de Python est spécialement conçu pour faciliter l’interaction avec ce type de fichiers : le module csv
.
En pratique, on utilise souvent des modules de plus haut niveau (comme pandas
) pour travailler avec des tableaux (DataFrames).
Nous allons néanmoins nous entraîner à l’utiliser en explorant deux fichiers de l’INSEE :
- insee_data.csv, généré avec l’outil Statistiques locales
- v_departement_2025.csv, l’un des fichiers du COG 2025
Préambule
Ce notebook est conçu pour être interactif. Dans les cellules prévues à cet effet, vous pouvez écrire du code en Python et l’exécuter en pressant SHIFT+ENTER.
Les variables restent en mémoire d’une cellule à l’autre. Essayez ci-dessous !
Première exploration des données
Avant tout, téléchargez les fichiers source sur votre ordinateur et ouvrez-les avec un éditeur de texte brut (comme Notepad++) pour en examiner la structure.
Exploration de insee_data.csv
On note que :
- Le fichier est encodé en UTF-8.
- les valeurs sont séparées par des points-virgules (
;
). - La 1ère ligne du fichier contient le nom des colonnes : c’est l’entête, ou
header
. - Les 34 785 autres lignes contiennent les données, une ligne par commune.
On relève qu’il y a quatre colonnes :
Code
: le numéro INSEE de la commune.Libellé
: le nom de la commune.Population municipale 2022
: la population de référence de la commune, millésime 2022, entrée en vigueur le 1er janvier 2025, donnée issue du recensement de la population.Boulangerie-pâtisserie (en nombre) 2024
: le nombre de boulangeries-pâtisseries que comptait la commune en 2024, donnée issue de la Base Permanente des Équipements (BPE).
Exploration de v_departement_2025.csv
On note que :
- Le fichier est encodé en UTF-8.
- les valeurs sont séparées par des virgules (
,
). - Le fichier comporte un
header
. - Les 101 autres lignes contiennent les données, une ligne par département.
On relève qu’il y a sept colonnes :
REGION
: code région.DEP
: code département.CHEFLIEU
: code de la commune chef-lieu.TNCC
: Type de nom en clair (donne une indication grammaticale).NCC
: Nom en clair, en majuscules.NCCENR
: Nom en clair, en minuscules accentuées.LIBELLE
: Nom en clair, en minuscules accentuées avec article.
Chargement des données
Principe
On peut modéliser les données avec Python de plusieurs manières.
Liste de listes
On peut construire une liste de listes : la grande liste représente le tableau, elle contient plusieurs listes (autant que de lignes) dont chacun des élements représentent les valeurs des colonnes.
# On importe le module CSV
import csv
# On crée la première liste
= []
rows
# On ouvre le fichier en mode lecture ('r') en spécifiant l'encodage ('utf-8'),
with open('data/insee_data.csv', 'r', encoding='utf-8') as mf:
# On crée un objet reader() qui lit une ligne du fichier qui lui est passé,
# détecte les délimiteurs et renvoie la liste des champs qu'ils séparent
= csv.reader(mf, delimiter=';')
csv_reader
# Cet objet est conçu comme un itérateur : à chaque fois qu'on l'appelle,
# il passe à la ligne suivante, jusqu'à la fin du fichier.
# On stocke ainsi toutes les listes renvoyées dans la première liste.
for row in csv_reader:
rows.append(row)
# On affiche la valeur stockée 13e ligne, 2eme colonne
# (pour mémoire, Python compte à partir de 0) :
print('La valeur à la 13e ligne, 2eme colonne est la suivante : ', end=None)
print(rows[12][1])
Liste de dictionnaires
Mais puisque notre fichier comporte un entête, il est intéressant de modéliser les données sous la forme d’une liste de dictionnaires : chaque ligne sera représentée par un dictionnaire qui fait correspondre aux noms des colonnes (ex: Code
) leurs valeurs (ex: '01001'
).
Pour montrer l’intérêt, on ne conservera ici que les communes dont le champ Code
commence par '77'
(c’est-à-dire les communes de Seine-et-Marne).
# On crée une autre liste
= []
seinemarne
with open('data/insee_data.csv', 'r', encoding='utf-8') as mf:
# On utilise ici un objet DictReader()
= csv.DictReader(mf, delimiter=';')
csv_reader
# Les objets renvoyés à chaque appel sont donc des dictionnaires
for dico in csv_reader:
# On utilise la méthode `startswith` dont disposent les objets de type
# natif `str` pour filtrer les éléments qui nous intéressent
if dico['Code'].startswith('77'):
seinemarne.append(dico)
# On affiche le libellé du dernier élément de la liste ainsi constituée
= seinemarne[-1]['Libellé']
libelle print("La dernière ville de la liste est : {}".format(libelle))
À vous de jouer !
Chargement de insee_data.csv
Chargez les données du fichier insee_data.csv
dans une liste de dictionnaires que vous nommerez communes
.
Affichez le nom et la population de la 10 000e ville de la liste.
# Entrez ici votre code
Solution
= []
communes
with open('data/insee_data.csv', 'r', encoding='utf-8') as mf:
= csv.DictReader(mf, delimiter=';')
csv_reader for row in csv_reader:
communes.append(row)
print("La 10000e ville de la liste est : {}, {} habitants.".format(
9999]['Libellé'],
communes[9999]['Population municipale 2022']
communes[
) )
Chargement de v_departement_2025.csv
Chargez les données du fichier v_departement_2025.csv
dans une liste de dictionnaires que vous nommerez depts
.
Affichez le nom en majuscule du 17e département de la liste.
# Entrez ici votre code
Solution
= []
depts
with open('data/v_departement_2025.csv', 'r', encoding='utf-8') as mf:
= csv.DictReader(mf, delimiter=',')
csv_reader for row in csv_reader:
depts.append(row)
print("Le 17eme département de la liste est : {}.".format(depts[16]['NCC']))
Vérification et nettoyage des données
Il est utile de vérifier que les données ne présentent pas de “bizarreries” (éléments aberrants, valeurs manquantes, etc.).
Nous allons conduire quelques tests de cohérence.
Nombre de champs pour chaque enregistrement
On va s’assurer que chaque enregistrement des listes communes
et depts
comporte le bon nombre de champs : 4 pour les communes, 7 pour les départements.
Écrivez un programme qui vérifie pour les deux listes que chaque dictionnaire contient le nombre approprié de clés. On stockera dans des listes à part les problèmes éventuels (si tout va bien, ces listes seront vides).
# Entrez ici votre code
Solution
# Pour les communes tout d'abord
# On le fait de façon naïve, avec une boucle traditionnelle
= []
communes_avec_pb for com in communes:
if len(com) != 4:
communes_avec_pb.append(com)
print("{} enregistrements problématiques dans la liste 'communes'".format(
len(communes_avec_pb)
)
)
# Pour les départements ensuite
# On le fait e façon pythonique, avec une compréhension de liste
= [dep for dep in depts if len(dep) != 7]
dept_avec_pb
print("{} enregistrements problématiques dans la liste 'dept'".format(
len(dept_avec_pb)
) )
Cohérence géographique
Dans la liste depts
, on va vérifier que la commune désignée comme chef-lieu d’un département appartient bien à ce département.
On se souviendra que le code INSEE d’une commune commence par le numéro du département auquel elle appartient, codé sur 2 caractères (3 pour l’outre-mer).
# Entrez ici votre code
Solution
# Voici la façon naïve, avec une boucle traditionnelle
= []
pb_chef_lieu for dep in depts:
= dep['CHEFLIEU']
codeinsee if codeinsee[:2] != dep['DEP'] and codeinsee[:3] != dep['DEP']:
pb_chef_lieu.append(dep)
pb_chef_lieu
# Et la même chose de façon pythonique, avec une compréhension de liste
= [
pb_chef_lieu for depts in depts
dep if dep['CHEFLIEU'][:2] != dep['DEP']
and dep['CHEFLIEU'][:3] != dep['DEP']
]
print("{} communes situées hors du département.".format(len(pb_chef_lieu)))
Valeurs manquantes
Bien souvent, les données manquantes dans une série de données sont signalées par une chaîne de caractère spécifique (comme N/A
) et non par l’absence de valeurs. Cela peut-être gênant.
Dans la table communes
, on va ainsi filtrer les enregistrements qui contiennent des valeurs non numériques dans les champs de données.
On utilisera pour cela la méthode is_decimal
dont disposent les objets de type natif str
(cf documentation). Cette méthode renvoie True
si tous les caractères d’une chaîne sont des caractères décimaux et qu’elle contient au moins un caractère, False
sinon.
# Entrez ici votre code
Solution
# Voici la façon naïve, avec une boucle traditionnelle
= []
pb_nombre for com in communes:
if not com['Population municipale 2022'].isdecimal():
pb_nombre.append(com)next
elif not com['Boulangerie-pâtisserie (en nombre) 2024'].isdecimal():
pb_nombre.append(com)
# Et la même chose de façon pythonique, avec une compréhension de liste
= [
pb_nombre for com in communes
com if not com['Population municipale 2022'].isdecimal()
or not com['Boulangerie-pâtisserie (en nombre) 2024'].isdecimal()
]
print("{} communes avec données manquantes.".format(len(pb_nombre)))
Ainsi donc, des données sont manquantes pour une vingtaine de communes… On les éliminera de la liste communes
.
# Entrez ici votre code
Solution
print("Nombre d'enregistrements avant nettoyage : {}".format(len(communes)))
for ville in pb_nombre:
communes.remove(ville)
print("Nombre d'enregistrements après nettoyage : {}".format(len(communes)))
Gestion des types
Dans la liste communes
, les valeurs stockées dans les champs de données sont pour l’instant représentées par des chaînes de caractères.
print(type(communes[0]['Population municipale 2022']))
Pour les utiliser dans des calculs, il faut d’abord les convertir en nombres entiers. Allez-y !
# Entrez ici votre code
Solution
for c in communes:
for champ in ['Population municipale 2022', 'Boulangerie-pâtisserie (en nombre) 2024']:
= int(c[champ])
c[champ]
print(type(communes[0]['Population municipale 2022']))
Requêtes sur les données
Utilisez les deux tables communes
et depts
construites précédemment pour répondre aux questions suivantes.
Boulangeries de(s) Champs
Combien y a-t-il de boulangeries à Champs-sur-Marne (77083) ?
# Entrez ici votre code
Solution
for c in communes:
if c['Code'] == '77083':
print("{} : {} boulangeries".format(
'Libellé'],
c['Boulangerie-pâtisserie (en nombre) 2024']
c[
) )
Villes-fantômes
Quelles sont les villes ne comportant aucun habitant ? Pourquoi ?
# Entrez ici votre code
Solution
= [
villes_mortes for v in communes
v if v['Population municipale 2022'] == 0
]
print("Villes sans habitants :")
# Ce sont toutes des communes de la Meuse, non loin de Verdun :
# souvenir de la Grande Guerre...
for v in villes_mortes:
print("- {} ({})".format(v['Libellé'], v['Code']))
Habitants de caractères
Quelles communes ont autant d’habitants que de caractères dans leur nom ?
# Entrez ici votre code
Solution
= [
villes_lettres for v in communes
v if len(v['Libellé']) == v['Population municipale 2022']
]
print("Villes qui ont autant d'habitants que de lettres dans leur nom :")
for v in villes_lettres:
print("- {} ({}) : {} habitants, {} caractères".format(
'Libellé'],
v['Code'],
v['Population municipale 2022'],
v[len(v['Libellé'])
) )
Du pain, du pain
Combien y a-t-il de boulangeries en France ? Quelle est la commune qui a la plus forte densité de boulangeries parmi les villes de plus de 5000 habitants ?
# Entrez ici votre code
Solution
= sum([c['Boulangerie-pâtisserie (en nombre) 2024'] for c in communes])
nb_boul
print("Boulangeries-pâtisseries en France en 2024 : {}".format(nb_boul))
= 0
densite_max = ''
laureat
for com in communes:
= com['Boulangerie-pâtisserie (en nombre) 2024']
boulang = com['Population municipale 2022']
population
if population > 5000 and boulang/population*1000 > densite_max:
= boulang/population*1000
densite_max = com['Libellé']
laureat
print("Ville à la plus forte densité : {} ({:.1f} boulangeries/1000 hab)".format(
laureat,
densite_max
) )
Essartage
Quelles communes contiennent le toponyme ‘essart’ dans leur nom ?
# Entrez ici votre code
Solution
= []
essarts
for com in communes:
if 'essart' in com['Libellé'].lower():
essarts.append(com)
print("Villes avec le toponyme Essart :")
for v in essarts:
print("- {name} ({code})".format(code=v['Code'], name=v['Libellé']))
Chef-lieu chétif
Dans quels départements le chef-lieu n’est-il pas la commune la plus peuplée ? Quelles sont les communes plus peuplées ?
Indice : on commencera par créer un dictionnaire associant à chaque chef-lieu sa population municipale.
## Entrez ici votre code
Solution
= {}
dico_cheflieu = {}
resultats
# Création du dictionnaire qui associe le code d'un chef-lieu à sa population
for d in depts:
= d['CHEFLIEU']
code for c in communes:
if code == c['Code']:
= c['Population municipale 2022']
dico_cheflieu[code]
# Examen de chacune des communes
for c in communes:
= c['Code']
code_insee = c['Population municipale 2022']
population = 'XX'
departement
# On récupère le département :
# les trois premiers chiffres du code INSEE en outre-mer,
# les deux premiers chiffres sinon
if code_insee.startswith('97'):
= code_insee[:3]
departement else:
= code_insee[:2]
departement
# Quel est le chef-lieu de ce département ?
= '0000'
chl for d in depts:
if d['DEP'] == departement:
= d['CHEFLIEU']
chl
# On compare la population de la commune à celle du chef-lieu
if population > dico_cheflieu[chl]:
# Si le département n'est pas déjà dans le dictionnaire, on l'ajoute
if departement not in resultats:
= []
resultats[departement] # Et on ajoute la ville à la liste
'Libellé'])
resultats[departement].append(c[
print("Départements comptant au moins une ville plus peuplée que le chef-lieu :")
for k, v in sorted(resultats.items()):
print("- {} : ".format(k), end='')
print(', '.join(v))
Nom commun
Quels sont les dix noms de ville le plus répandu ? (ie. ceux que le plus grand nombre de communes portent) ?
# Entrez ici votre code
Solution
= {}
dico_noms
for c in communes:
= c['Libellé'].upper()
nom if nom in dico_noms:
+=1
dico_noms[nom]else:
=1
dico_noms[nom]
= sorted(dico_noms.items(), key=lambda x: x[1], reverse=True)[:10]
top10
print('Top 10 des noms les plus répandus : ')
for ville, occurence in top10:
print('- {} ({})'.format(ville, occurence))