Importation d'une table depuis un fichier CSV, recherche dans une table, tri d'une table, fusion de tables
Une des utilisations principales de l’informatique de nos jours est le traitement de quantités importantes de données dans des domaines très variés. Par exemple, un site de commerce en ligne peut avoir à gérer des bases de données pour des dizaines de milliers d’articles en vente, de clients, de commandes. Un hopital doit pouvoir accéder efficacement à tous les détails de traitements de ses patients, etc.
Mais si les logiciels de traitement de base de données sont des programmes hautement spécialisés pour effectuer ce genre de tâches le plus efficacement possible, il est facile de mettre en œuvre les opérations de base dans un langage de programmation comme Python. C'est l'objectif de ce chapitre.
Le format CSV (pour comma separated values, c'est-à-dire valeurs séparées par des virgules) est un format de fichier texte très pratique pour représenter des données structurées. Dans ce format, chaque ligne représente un enregistrement et chaque colonne représente un champ de l’enregistrement. Les champs sont séparés par une virgule, d’où le nom CSV.
En pratique, on peut choisir le caractère utilisé pour séparer les différents champs et on utilise parfois, plutôt qu'une virgule, un point-virgule, une tabulation, deux points ou une barre verticale. Nous pouvons enfin remarquer que la première ligne d’un tel fichier est généralement utilisée pour indiquer le nom des différents champs. Dans ce cas, le premier enregistrement apparaît sur la deuxième ligne du fichier et non sur la première.
Dans tout ce chapitre, nous utiliserons les données contenues dans le fichier prefectures.csv, dont voici les 20 premières lignes :
dep,ville,dep_nom,lat,long,CL_reg
1,Bourg-en-Bresse,Ain,46.202500000000001,5.221944444444444,0
2,Laon,Aisne,49.562222222222225,3.622500000000000,0
3,Moulins,Allier,46.568333333333335,3.334444444444444,0
4,Digne-les-Bains,Alpes-de-Haute-Provence,44.092222222222219,6.238055555555555,0
5,Gap,Hautes-Alpes,44.557499999999997,6.076111111111111,0
6,Nice,Alpes-Maritimes,43.696388888888890,7.275000000000000,0
7,Privas,Ardèche,44.735277777777775,4.599444444444444,0
8,Charleville-Mézières,Ardennes,49.761388888888888,4.720000000000000,0
9,Foix,Ariège,42.967222222222219,1.605833333333333,0
10,Troyes,Aube,48.297777777777775,4.078333333333333,0
11,Carcassonne,Aude,43.214722222222221,2.353333333333333,0
12,Rodez,Aveyron,44.349444444444444,2.574722222222222,0
13,Marseille,Bouches-du-Rhône,43.290277777777774,5.380000000000000,1
14,Caen,Calvados,49.180000000000000,-0.365277777777778,0
15,Aurillac,Cantal,44.929166666666667,2.446944444444445,0
16,Angoulême,Charente,45.645833333333336,0.156944444444444,0
17,La Rochelle,Charente-Maritime,46.157499999999999,-1.157222222222222,0
18,Bourges,Cher,47.081111111111113,2.395277777777778,0
19,Tulle,Corrèze,45.271111111111111,1.768888888888889,0
Question 1 : Ouvrir avec un éditeur de texte le fichier prefectures.csv et décrire quel est le contenu du fichier. En particulier, quel est le nombre d'enregistrements, à quoi correspondent-ils, et quels en sont les différents champs ?
prefectures.csv est un fichier CSV contenant 101 enregistrements, qui correspondent aux 101 départements français. Chaque enregistrement est composé des champs dep (numéro du département), ville (nom de la ville préfecture), dep_nom (nom du département), lat et long (coordonnées géographiques de la préfecture) et CL_reg (1 si la ville est chef-lieu de région et 0 sinon).Pour pouvoir manipuler avec Python les données enregistrées dans le fichier CSV, il faut commencer par importer ces données et les stocker dans un tableau. Pour réaliser cette opération, on peut définir la fonction importer_donnees :
import csv
def importer_donnees(nom_de_fichier):
"""
Importe les données contenues dans un fichier CSV et les retourne sous forme de tableau
- Entrée : nom_de_fichier (chaîne de caractères correspondant à un nom de fichier CSV)
- Sortie : tab (tableau de dictionnaires, correspondant aux enregistrements du fichier CSV)
Attention : les données importées sont de type chaîne de caractères
"""
tab = []
with open(nom_de_fichier, 'r', encoding = 'utf-8') as fichier:
donnees = csv.DictReader(fichier, delimiter = ',')
for enregistrement in donnees:
tab.append(dict(enregistrement))
return tab
pref = importer_donnees('prefectures.csv')
with open(nom_de_fichier, 'r', encoding = 'utf-8'), déjà rencontrée dans le chapitre sur les chaînes de caractères, permet d'ouvrir le fichier en lecture en précisant que le texte est encodé au format UTF-8.DictReader, importée du module csv, récupère les données depuis le fichier CSV et les stocke dans la variable donnees, qui est un itérable dont les éléments sont des dictionnaires ordonnés (type particulier de dictionnaire). Le paramètre optionnel delimiter permet d'indiquer par quel caractère sont séparés les champs dans le fichier CSV.donnees en un tableau tab, et les dictionnaires ordonnés en simples dictionnaires.Question 2 : Ecrire la spécification de la fonction importer_donnees.
Question 3 : Après avoir exécuté la cellule suivante et affiché les cinq premiers éléments de pref, dire quel est le type de ces éléments.
for k in range(5):
print(pref[k])
pref est un tableau dont les éléments sont des dictionnaires. Tous les dictionnaires contenus dans pref ont les mêmes clés, à savoir 'dep', 'ville', 'dep_nom', 'lat', 'long' et 'CL_reg'.On constate que, lors de l'importation, tous les champs sont des chaînes de caractères. Pour faire en sorte que la latitude et la longitude des différentes préfectures soit de type numérique, exécuter la cellule ci-dessous :
for k in range(len(pref)):
pref[k]['lat'] = float(pref[k]['lat'])
pref[k]['long'] = float(pref[k]['long'])
Question 4 : Ecrire des lignes de code permettant de faire en sorte que le champ 'CL_reg' soit de type booléen.
for k in range(len(pref)):
pref[k]['CL_reg'] = bool(int(pref[k]['CL_reg']))
Une fois que la table est stockée en mémoire sous la forme d'un tableau de dictionnaires, il est possible d'effectuer des recherches dans la table.
Par exemple, voici des lignes de code permettant d'afficher le nom des 101 départements français :
for k in range(len(pref)):
print(pref[k]['dep_nom'], end = ' ')
Question 5 : Ecrire des lignes de code permettant d'afficher le nom des villes préfectures.
for k in range(len(pref)):
print(pref[k]['ville'], end = ' ')
Question 6 : Ecrire des lignes de code permettant d'afficher le nom des départements dont la préfecture contient la lettre X.
for k in range(len(pref)):
if 'x' in pref[k]['ville'].lower():
print(pref[k]['dep_nom'], end = ' ')
Question 7 : Ecrire des lignes de code permettant d'afficher les préfectures dont la latitude est comprise entre 44° et 46° et dont la longitude est comprise entre 2° et 4°.
for k in range(len(pref)):
if 44 <= pref[k]['lat'] <= 46 and 2 <= pref[k]['long'] <= 4:
print(pref[k]['ville'], end = ' ')
Question 8 : Ecrire des lignes de code permettant d'afficher le nom et les coordonnées géographiques des 18 capitales de région (arrondies à deux chiffres après la virgule).
for k in range(len(pref)):
if pref[k]['CL_reg']:
print(pref[k]['ville'], ':', round(pref[k]['lat'], 2), ';', round(pref[k]['long'], 2))
Question 9 : Ecrire des lignes de code permettant compter le nombre de villes préfectures situées à l'ouest du méridien de Greenwich, c'est-à-dire dont la longitude est négative.
cpt = 0
for k in range(len(pref)):
if pref[k]['long'] <= 0:
cpt += 1
print('Il y a', cpt, 'préfectures à l\'ouest du méridien de Greenwich.')
Question 10 : Ecrire des lignes de code permettant compter le nombre de villes préfectures situées au nord de Paris.
k = 0
while pref[k]['ville'] != 'Paris':
k += 1
long_Paris = pref[k]['lat']
cpt = 0
for k in range(len(pref)):
if pref[k]['lat'] > long_Paris:
cpt += 1
print('Il y a', cpt, 'préfectures au nord de Paris.')
Pour exploiter les données, il peut être intéressant de les trier. Une utilisation possible est l’obtention du classement des entrées selon tel ou tel critère.
On ne peut pas directement trier le tableau pref. Il faut indiquer selon quels critères on veut effectuer ce tri. Pour cela, on commence par définir une fonction qui renvoie la valeur utilisée pour le tri.
Concrètement, si on souhaite trier la table pref en fonction de la latitude de la ville préfecture, on commence par définir la fonction suivante :
def cle_latitude(d):
"""
Retourne la valeur associée à la clé 'lat' dans le dictionnaire d
- Entrée : d (dictionnaire contenant au moins la clé 'lat')
- Sortie : (nombre)
"""
return d['lat']
Pour trier la table, il suffit alors d'utiliser la commande .sort() avec le paramètre optionnel key égal à la fonction de tri définie précédemment :
pref.sort(key = cle_latitude)
On peut alors afficher la liste des préfectures dans l'ordre croissant de leur latitude, c'est-à-dire du sud au nord :
for k in range(len(pref)):
print(pref[k]['ville'], end = ' ')
Si l'on préfère afficher les villes du nord au sud, et donc par ordre décroissant des latitudes, on rajoute à la commande .sort() le paramètre optionnel reverse :
pref.sort(key = cle_latitude, reverse = True)
for k in range(len(pref)):
print(pref[k]['ville'], end = ' ')
Question 11 : Ecrire des lignes de code permettant d'afficher les préfectures françaises d'ouest en est.
def cle_longitude(d):
"""
Retourne la valeur associée à la clé 'long' dans le dictionnaire d
- Entrée : d (dictionnaire contenant au moins la clé 'long')
- Sortie : (nombre)
"""
return d['long']
pref.sort(key = cle_longitude)
for k in range(len(pref)):
print(pref[k]['ville'], end = ' ')
Question 12 : Ecrire des lignes de code permettant d'afficher les capitales régionales françaises du nord au sud.
pref.sort(key = cle_latitude, reverse = True)
for k in range(len(pref)):
if pref[k]['CL_reg']:
print(pref[k]['ville'], end = ' ')
En utilisant la commande .sort(), nous avons modifié la table pref en mémoire : l'index de chaque dictionnaire a été modifié dans le tableau pref. Si on souhaite garder intact la table initiale, il faut utiliser la fonction sorted() à la place de la commande .sort() :
pref = importer_donnees('prefectures.csv')
for k in range(len(pref)):
pref[k]['lat'] = float(pref[k]['lat'])
pref[k]['long'] = float(pref[k]['long'])
pref[k]['CL_reg'] = bool(int(pref[k]['CL_reg']))
pref2 = sorted(pref, key = cle_latitude, reverse = True)
for k in range(len(pref)):
print(pref[k]['ville'], end = ' ')
for k in range(len(pref2)):
print(pref2[k]['ville'], end = ' ')
Question 13 : Ecrire des lignes de code permettant d'afficher les noms des départements français dans l'ordre alphabétique, sans modifier la table pref :
def cle_dep_nom(d):
"""
Retourne la valeur associée à la clé 'dep_nom' dans le dictionnaire d
- Entrée : d (dictionnaire contenant au moins la clé 'dep_nom')
- Sortie : (chaîne de caractères)
"""
return d['dep_nom'].lower()
pref2 = sorted(pref, key = cle_dep_nom)
for k in range(len(pref2)):
print(pref2[k]['dep_nom'], end = ' ')
Question 14 : Ecrire des lignes de code permettant d'afficher les préfectures dans l'ordre alphabétique, sans modifier la table pref :
def cle_ville(d):
"""
Retourne la valeur associée à la clé 'ville' dans le dictionnaire d
- Entrée : d (dictionnaire contenant au moins la clé 'ville')
- Sortie : (chaîne de caractères)
"""
return d['ville'].lower()
pref2 = sorted(pref, key = cle_ville)
for k in range(len(pref2)):
print(pref2[k]['ville'], end = ' ')
cle_ville.def ascii(phrase):
"""
Transforme une chaîne de caractères en enlevant majuscules et accents.
- Entrée : phrase (chaîne de caractères)
- Sortie : nouvelle_phrase (chaîne de caractères)
"""
if type(phrase) != str:
raise TypeError('l\'argument doit être une chaîne de caractères')
nouvelle_phrase = ''
for lettre in phrase:
lettre = lettre.lower()
if lettre in 'àâä':
nouvelle_phrase += 'a'
elif lettre in 'éèêë':
nouvelle_phrase += 'e'
elif lettre in 'îï':
nouvelle_phrase += 'i'
elif lettre in 'ôö':
nouvelle_phrase += 'o'
elif lettre in 'ùûü':
nouvelle_phrase += 'u'
elif lettre == 'ÿ':
nouvelle_phrase += 'y'
elif lettre == 'ç':
nouvelle_phrase += 'c'
elif lettre == 'æ':
nouvelle_phrase += 'ae'
elif lettre == 'œ':
nouvelle_phrase += 'oe'
else:
nouvelle_phrase += lettre
return nouvelle_phrase
def cle_ville(d):
"""
Retourne la valeur associée à la clé 'ville' dans le dictionnaire d
- Entrée : d (dictionnaire contenant au moins la clé 'ville')
- Sortie : (chaîne de caractères)
"""
return ascii(d['ville'])
pref2 = sorted(pref, key = cle_ville)
for k in range(len(pref2)):
print(pref2[k]['ville'], end = ' ')
Le fichier population.csv (créé à partir de données récoltées sur le site de l'INSEE) contient la population estimée de chaque département français au 1er janvier 2020, ainsi que la répartition de cette population par tranche d'âge :
Question 15 : Ecrire des lignes de code permettant d'importer les données du fichier population.csv dans un tableau pop composé de dictionnaires. Les populations seront stockées sous forme d'entiers.
pop = importer_donnees('population.csv')
for k in range(len(pop)):
pop[k]['popA'] = int(pop[k]['popA'])
pop[k]['popB'] = int(pop[k]['popB'])
pop[k]['popC'] = int(pop[k]['popC'])
pop[k]['popD'] = int(pop[k]['popD'])
pop[k]['popE'] = int(pop[k]['popE'])
pop[k]['pop'] = int(pop[k]['pop'])
Puisque les deux tables pref et pop possèdent en commun le champ dep, il semble intéressant de pouvoir créer une table unique regroupant les données présentes dans chacune des deux tables. On parle de jointure des deux tables.
On commence par créer une fonction fusion dont le rôle est de fusionner deux dictionnaires en un seul :
def fusion(d1, d2):
"""
Fusionne deux dictionnaires
- Entrées : d1 (dictionnaire contenant les clés 'dep', 'dep_nom', 'ville', 'lat' et 'long')
d2 (dictionnaire contenant les clés 'dep', 'popA', 'popB', 'popC', 'popD', 'popE' et 'pop')
- Sortie : (dictionnaire unique contenant toutes les clés citées précédemment)
"""
return {'dep' : d1['dep'], 'dep_nom' : d1['dep_nom'], 'ville' : d1['ville'], 'lat' : d1['lat'], 'long' : d1['long'],
'popA' : d2['popA'], 'popB' : d2['popB'], 'popC' : d2['popC'], 'popD' : d2['popD'], 'popE' : d2['popE'],
'pop' : d2['pop']}
La jointure des deux tables d'effectue alors à l'aide d'une double boucle :
table = []
for d1 in pref:
for d2 in pop:
if d1['dep'] == d2['dep']:
table.append(fusion(d1, d2))
Voici les cinq premiers éléments de la table ainsi obtenue :
for k in range(5):
print(table[k])
table est un dictionnaire possédant à la fois les clés issues des enregistrements du tableau pref et du tableau pop.Il est enfin possible d'enregistrer la nouvelle table dans un ficher CSV, grâce à la fonction suivante :
import csv
def exporter_donnees(nom_de_fichier, tab):
"""
Exporte les données de tab vers un fichier CSV
- Entrée : nom_de_fichier (chaîne de caractères correspondant à un nom de fichier CSV), tab (tableau de dictionnaires)
- Effet de bord : écriture dans un fichier texte
Attention : le fichier texte est écrasé, son contenu précédent est effacé
"""
with open(nom_de_fichier, 'w', encoding = 'utf-8') as fichier:
donnees = csv.DictWriter(fichier, ['dep', 'dep_nom', 'ville', 'lat', 'long', 'popA', 'popB', 'popC',
'popD', 'popE', 'pop'], delimiter = ',')
donnees.writeheader() # Ecriture des clés sur la première ligne du fichier CSV
donnees.writerows(tab) # Ecriture des enregistrements à partir de la deuxième ligne du fichier CSV
exporter_donnees('nouveau.csv', table)
.sort() ou de la fonction sorted.reverse pour effectuer un tri dans l'ordre décroissant..sort() et la fonction sorted en fonction du contexte.Ecrire des lignes de code permettant de calculer la population française totale estimée au 1er janvier 2020.
population_totale = 0
for k in range(len(table)):
population_totale += table[k]['pop']
print('L\'INSEE estime que la population française totale au 1er janvier 2020 est de', population_totale, 'hab.')
Ecrire des lignes de code permettant d'afficher la liste des dix départements les plus peuplés et leur population, par ordre décroissant de population.
def cle_pop(d):
"""
Retourne la valeur associée à la clé 'pop' dans le dictionnaire d
- Entrée : d (dictionnaire contenant au moins la clé 'pop')
- Sortie : (entier)
"""
return d['pop']
table2 = sorted(table, key = cle_pop, reverse = True)
for k in range(10):
print(table2[k]['dep_nom'], ':', table2[k]['pop'])
Ecrire des lignes de code permettant d'afficher la liste des dix départements dont la proportion de moins de 20 ans est la plus forte, puis la liste des dix départements pour lesquels cette proportion est la plus faible.
def cle_taux_jeunes(d):
"""
Retourne le taux de moins de 20 ans calculé avec les données du dictionnaire d
- Entrée : d (dictionnaire contenant au moins les clés 'popA' et 'pop')
- Sortie : (nombre flottant)
"""
return d['popA'] / d['pop']
table2 = sorted(table, key = cle_taux_jeunes, reverse = True)
for k in range(10):
print(table2[k]['dep_nom'], ':', round(table2[k]['popA'] / table2[k]['pop'] * 100, 2), '%')
for k in range(len(table)-1, len(table)-11, -1):
print(table2[k]['dep_nom'], ':', round(table2[k]['popA'] / table2[k]['pop'] * 100, 2), '%')
Ecrire des lignes de code permettant d'afficher la liste des dix départements dont la proportion de plus de 75 ans est la plus forte, puis la liste des dix départements pour lesquels cette proportion est la plus faible.
def cle_taux_seniors(d):
"""
Retourne le taux de plus de 75 ans calculé avec les données du dictionnaire d
- Entrée : d (dictionnaire contenant au moins les clés 'popE' et 'pop')
- Sortie : (nombre flottant)
"""
return d['popE'] / d['pop']
table2 = sorted(table, key = cle_taux_seniors, reverse = True)
for k in range(10):
print(table2[k]['dep_nom'], ':', round(table2[k]['popE'] / table2[k]['pop'] * 100, 2), '%')
for k in range(len(table)-1, len(table)-11, -1):
print(table2[k]['dep_nom'], ':', round(table2[k]['popE'] / table2[k]['pop'] * 100, 2), '%')
Le fichier emissions_CO2.csv (créé à partir de données récoltées sur le site de la Banque mondiale) contient les quantités de CO2, en tonnes par habitant, émises par chaque pays de l'Union Européenne, pour les années 2000 et 2010.
Ecrire des lignes de code permettant d'importer les données du fichier. Les quantités de CO2 émises seront stockées sous forme de nombre flottant.
co2 = importer_donnees('emissions_CO2.csv')
for k in range(len(co2)):
co2[k]['em_2000'] = float(co2[k]['em_2000'])
co2[k]['em_2010'] = float(co2[k]['em_2010'])
Ecrire des lignes de code permettant d'afficher la liste des pays de l'UE qui ont diminué leurs émissions de CO2 par habitant entre 2000 et 2010.
for k in range(len(co2)):
if co2[k]['em_2010'] < co2[k]['em_2000']:
print(co2[k]['nom_pays'], end = ' ')
Ecrire des lignes de code permettant d'afficher le nom des cinq pays qui émettent le plus de CO2 par habitant en 2010, puis le nom des cinq pays qui en émettent le moins.
def cle_emission_2010(d):
"""
Retourne la valeur associée à la clé 'em_2010' dans le dictionnaire d
- Entrée : d (dictionnaire contenant au moins la clé 'em_2010')
- Sortie : (nombre flottant)
"""
return d['em_2010']
table = sorted(co2, key = cle_emission_2010, reverse = True)
for k in range(5):
print(table[k]['nom_pays'], ':', round(table[k]['em_2010'], 2), 't/hab.')
for k in range(len(table)-1, len(table)-6, -1):
print(table[k]['nom_pays'], ':', round(table[k]['em_2010'], 2), 't/hab.')
Ecrire des lignes de code permettant d'afficher, pour chaque pays, le taux d'évolution de ses émissions de CO2 par habitant entre 2000 et 2010. Les pays seront classés de celui qui a le taux d'évolution le plus négatif à celui qui a le taux d'évolution le plus positif.
On rappelle qu'un taux d'évolution se calcule avec la formule suivante : taux_evolution = (valeur_finale - valeur_initiale) / valeur_initiale.
for k in range(len(table)):
taux_evolution = (table[k]['em_2010'] - table[k]['em_2000']) / table[k]['em_2000']
table[k]['taux_evol'] = taux_evolution
def cle_taux_evolution(d):
"""
Retourne le taux d'évolution des émissions de CO2 entre 2000 et 2010 calculé avec les données du dictionnaire d
- Entrée : d (dictionnaire contenant au moins la clé 'taux_evol')
- Sortie : (nombre flottant)
"""
return d['taux_evol']
table = sorted(co2, key = cle_taux_evolution)
for k in range(len(table)):
print(table[k]['nom_pays'], ':', round(table[k]['taux_evol'] * 100, 1), '%')
Ecrire des lignes de code permettant de stocker dans un nouveau fichier CSV la liste des pays de l'UE et leur taux d'évolution des émissions de CO2 par habitant.
import csv
def exporter_donnees(nom_de_fichier, tab):
"""
Exporte les données de tab vers un fichier CSV
- Entrée : nom_de_fichier (chaîne de caractères correspondant à un nom de fichier CSV), tab (tableau de dictionnaires)
- Effet de bord : écriture dans un fichier texte
Attention : le fichier texte est écrasé, son contenu précédent est effacé
"""
with open(nom_de_fichier, 'w', encoding = 'utf-8') as fichier:
donnees = csv.DictWriter(fichier, ['nom_pays', 'em_2000', 'em_2010', 'taux_evol'], delimiter = ',')
donnees.writeheader() # Ecriture des clés sur la première ligne du fichier CSV
donnees.writerows(tab) # Ecriture des enregistrements à partir de la deuxième ligne du fichier CSV
exporter_donnees('nouveau_CO2.csv', table)