Skip to content

Cours - Traitement des données en tables

L'informatique est souvent utilisée pour le traitement de quantités importantes de données dans de nombreux domaines tels que :

  • La gestion de données de sites web. (Exemple : Données utilisateurs sur un réseau social.)
  • La Data Science (science des données) : Extraire des connaissances à partir de données massives.
  • La finance, les assurances, l'épidémiologie, etc.

En Terminale est étudiée la notion de base de données.
Pour préparer les élèves, on étudie en Première le traitement de données organisées sous forme de tables (= listes de p-uplets nommés).

Présentation des tables de données

Les données représentées en tables se présentent ainsi (image issue de info.blaisepascal.fr):

Image_table

Le format CSV

Un fichier texte CSV:

  • Permet de représenter des données structurées.
  • Est couramment utilisé pour importer ou exporter des données d'une feuille de calcul.
  • Chaque ligne correspond à une ligne du tableau.
  • Chaque colonne est séparée par un séparateur, ou délimiteur (souvent une virgule ou un point-virgule)

La feuille de calcul suivante :

Screen_Excel

... donne un fichier CSV contenant :

Nom,Prenom,Age,Date naissance
Demerville,Erwan,26,03/05/95
Dupont,Michel,42,19/09/86
Dufoux,Alex,21,05/07/65

Chaque ligne correspond à un enregistrement (= structure de données éventuellement différentes).
La première ligne définit les descripteurs (également appelés attributs, clés, champs).

Les banques de données

Il existe des banques de données libres contenant des bases de données que vous pouvez librement télécharger dans plusieurs formats (CSV, JSON, Excel) et utiliser.

Par exemple :

Tables de données en Python

Implémentation en Python

En Python, il y a plusieurs façons de représenter une table de données :

  • en utilisant un tableau de n-uplets représenté en Python par une liste de tuples,
  • en utilisant un tableau de tableaux, représenté en Python par une liste de listes,
  • en utilisant un tableau de n-uplets nommés, représenté en Python par une liste de dictionnaires.

La feuille de calcul précédente :

Screen_Excel

... pourra donc être implémentée de la manière suivante :

table = \
[('Demerville', 'Erwan', '26', '03/05/95'),
('Dupont', 'Michel', '42', '19/09/86'),
('Dufoux', 'Alex', '21', '05/07/65')]

ou de la manière suivante :

table = \
[['Nom', 'Prenom', 'Age', 'Date naissance'],
['Demerville', 'Erwan', '26', '03/05/95'],
['Dupont', 'Michel', '42', '19/09/86'],
['Dufoux', 'Alex', '21', '05/07/65']]

ou encore :

table = \
[{'Nom': 'Demerville', 'Prenom': 'Erwan', 'Age': '26', 'Date naissance': '03/05/95'},
{'Nom': 'Dupont', 'Prenom': 'Michel', 'Age': '42', 'Date naissance': '19/09/86'},
{'Nom': 'Dufoux', 'Prenom': 'Alex', 'Age': '21', 'Date naissance': '05/07/65'}]

Dans notre cas, on privilégiera plutôt cette dernière implémentation, sous la forme d'une liste de dictionnaires, qui présente l'avantage d'être plus aisément manipulable.

Importer un fichier TXT / CSV :

On peut récupérer les données d'un fichier avec les fonctions de base de Python.

Pour cela, on va utiliser la fonction open pour ouvrir un fichier et les fonctions readlines et readline afin d'en récupérer le contenu.

Voici un fichier lire_ecrire.py contenant une fonction lire_fichier permettant de lire une table de données (en .csv ou .txt) et qui renvoie une liste de dictionnaires, et un fichier ecrire_fichier qui crée un fichier à partir d'une table de données.

Télécharger le fichier lire_ecrire.py

En reprenant le CSV précédent :

Nom,Prenom,Age,Date naissance
Demerville,Erwan,26,03/05/95
Dupont,Michel,42,19/09/86
Dufoux,Alex,21,05/07/65

Si on fait appel à notre fonction lire_fichier en spécifiant le nom du fichier et éventuellement un délimiteur, on obtient donc :

table = lire_fichier("exemple.csv")
table

[{'Nom': 'Demerville',
  'Prenom': 'Erwan',
  'Age': '28',
  'Date naissance': '03/05/95'},
 {'Nom': 'Dupont',
  'Prenom': 'Michel',
  'Age': '44',
  'Date naissance': '19/09/86'},
 {'Nom': 'Dufoux',
  'Prenom': 'Alex',
  'Age': '23',
  'Date naissance': '05/07/65'}]

Le paramètre delimiteur de la fonction lire_fichier est par défaut défini à une virgule. Il n'y a donc pas besoin d'indiquer de délimiteur lors de l'appel à lire_fichier si l'on souhaite utiliser la virgule.
Il faut toutefois l'indiquer pour tout autre délimiteur, par exemple si le délimiteur est un point-virgule, l'appel sera du type lire_fichier("nom_fichier.csv", ";").

Note : Les données sont toutes enregistrées en chaînes de caractères.
On peut utiliser des fonctions comme int(), float() ou encore eval() pour convertir les données.

À faire

Téléchargez le fichier exemple.csv contenant les enregistrements de l'exemple précédent :

Télécharger le fichier exemple.csv
  1. Lire le fichier CSV avec la fonction lire_fichier du fichier lire_ecrire.py et stocker le résultat dans une variable.
  2. Votre variable contient donc une liste de dictionnaires. Rajoutez un nouvel enregistrement contenant votre nom, prénom, âge et date de naissance.
  3. Ré-écrivez à présent un nouveau fichier CSV nommé nouveau.csv à l'aide de la fonction ecrire_fichier.
  4. Ouvrez votre nouveau CSV sur LibreOffice Calc ou Excel, et observez le résultat.

Interroger les bases de données

Pour cette partie, on travaillera avec la base de données des "Effectifs dans les enseignements de spécialités en Terminale générale par spécialités et selon les principales doublettes", proposée par data.education.gouv.fr.

Télécharger fr-en-effectifs-specialites-doublettes-terminale-generale.csv

Ce fichier CSV utilise le point-virgule comme délimiteur.
Il ne faudra pas se tromper de délimiteur lorsque l'on lira le fichier.

Ouvrons d'abord ce fichier avec LibreOffice Calc pour visuellement son contenu. Il ne faut pas oublier de cocher le point-virgule commme séparateur.

Contenu du fichier CSV

Le module csv

Pour cette partie, plutôt que d'utiliser les fonctions définies dans lire_ecrire.py, nous utiliserons le module csv qui permet de faciliter la lecture et l'écriture de fichiers CSV.

Ce module est natif, vous n'avez donc pas besoin de l'installer.

À faire avant les exercices qui suivent

Si vous ne l'avez pas fait, téléchargez le fichier fr-en-effectifs-specialites-doublettes-terminale-generale.csv.

Téléchargez également le script Python suivant, et mettez le dans le même dossier que le fichier csv précédent :

Télécharger manipulation_tables.py

Ce script contient :

  • une fonction lire_csv(fichier, delim) qui permet de lire un fichier CSV, et renvoie la table contenue sous la forme d'une liste de dictionnaires. Ses deux paramètres sont par défaut initialisés ainsi :
    • fichier, le chemin du fichier à lire, prend la valeur de la variable globale FICHIER_ENTREE,
    • delim, qui correspond au délimiteur à utiliser, est initialisé à ;. Ainsi, on a pas besoin, lors de l'appel à lire_csv, de fournir un chemin de fichier et un délimiteur si l'on souhaite utiliser les valeurs par défaut.
  • une fonction creer_csv(table, fichier, delim, ordre) qui permet de créer un fichier CSV à partir d'une table sous la forme d'une liste de dictionnaires. Ses trois paramètres sont :
    • table, une liste de dictionnaires représentant la table que l'on souhaite enregistrer,
    • fichier, le chemin du fichier à créer ou à remplacer, initialisé à FICHIER_SORTIE,
    • delim, qui correspond au délimiteur à utiliser, initialisé à ;,
    • ordre, initialisé à None, permettant si on le souhaite de définir l'ordre des attributs de la table.

Correction des exercices

Une version corrigée du fichier manipulation_tables.py, contenant des explications détaillées, est disponible :

Télécharger manipulation_tables_corr.py

Exercice 1 - Créer un fichier CSV

Compléter la fonction creer_table_classe qui consiste à créer une table dans un fichier CSV contenant le nom, prénom, l'âge et la classe de chaque élève de première NSI.

Vous devrez donc, dans la fonction, appeler creer_csv en lui donnant en entrée la liste de dictionnaires que vous aurez créé. Le fichier CSV de sortie sera par défaut celui contenu dans FICHIER_SORTIE (initialement 'mon_fichier.csv'), que vous pouvez changer si vous le souhaitez.

Exercice 2 - Lister les colonnes

Complétez la fonction lister_colonnes qui prend une table (liste de dictionnaires) en entrée et renvoie une liste des attributs de la table.

Dans le bloc if __name__ == '__main__', créez une variable table dans laquelle vous stockerez une table de données récupérée à partir d'un fichier CSV de votre choix. Ajoutez les instructions permettant d'afficher sur une ligne différente chaque attribut de la table, en appelant votre fonction lister_colonnes.

Pour les exercices suivants, on travaillera avec notre fichier fr-en-effectifs-specialites-doublettes-terminale-generale.csv.

Vous pouvez donc déjà, dans un premier temps, vous assurer que FICHIER_ENTREE = 'fr-en-effectifs-specialites-doublettes-terminale-generale.csv' et ajouter l'instruction suivante au début du bloc if __name__ == '__main__' en bas du script :

table = lire_csv()

Cela permettra de lire le fichier fr-en-effectifs-specialites-doublettes-terminale-generale.csv et de récupérer la table sous la forme d'une liste de dictionnaires dans la variable table.

Exercice 3

Complétez toutes les autres fonctions de manipulation_tables.py. Lisez bien les docstrings pour comprendre ce que doit faire chaque fonction.

Trier une table de données

Une autre opération courante sur les tables de données consiste à trier une table en fonction d'une clé donnée.

Si l'on essaie de trier une liste de dictionnaires avec la fonction native de Python sorted, voici ce qu'il se passe :

>>> table = lire_csv()
>>> sorted(table)
Traceback (most recent call last):
  File "/home/erwan/Documents/Gits/erwandemervillefr/git_nsi/docs/premiere/traitement_donnees/src/manipulation_tables.py", line 111, in <module>
    sorted(table)
TypeError: '<' not supported between instances of 'dict' and 'dict'

En effet, on ne peut pas comparer directement deux dictionnaires.

Pour utiliser la fonction sorted sur notre tableau de dictionnaires, il faut indiquer comment se ramener à des valeurs que Python sait comparer (nombres, chaînes de caractères, n-uplets). Pour cela, on commence par définir une fonction qui prend en argument un enresgistrement de la table (donc un dictionnaire) et renvoie la valeur que l’on souhaite comparer.
Par exemple, si l'on travaille avec notre table des effectifs d'élèves en enseignements de spécialité, et qu'on souhaite trier les enregistrements par rapport à la colonne "PATRONYME", on créera d'abord une fonction :

def patronyme(e):
    ''' Prend une entrée sur un lycée de la table et renvoie le patronyme du lycée.
    :param e: (dict) un dictionnaire contenant les infos sur un lycée 
    :return: (str) le patronyme du lycée '''

    return e["PATRONYME"]

Ensuite, pour trier notre table par rapport au patronyme, et stocker le résultat dans une variable nouvelle_table, il ne restera plus qu'à faire :

nouvelle_table = sorted(table, key=patronyme)

Le paramètre key prend la fonction patronyme que l'on a créé ci-dessus.
On peut également ajouter un paramètre reverse si l'on souhaite trier dans l'ordre inverse :

nouvelle_table = sorted(table, key=patronyme, reverse=True)

À noter qu'il est également possible, plutôt que de créer une nouvelle table triée, de modifier directement la table de données initiale. Pour cela, on aurait utiliser la méthode sort, comme ceci :

table.sort(table, key=patronyme)

On peut également trier une liste de dictionnaires par rapport à deux clés.

Pour cela, la fonction que l'on donnera comme key de la fonction sorted renverra un tuple de deux éléments. Par exemple, si l'on souhaite trier les enregistrements de notre table d'effectifs d'élèves en terminale générale selon le "DEPARTEMENT" ET la "COMMUNE", on écrira une fonction :

def departement_puis_commune(e):
    ''' Prend une entrée sur un lycée de la table et renvoie un tuple contenant le département et la commune.
    :param e: (dict) un dictionnaire contenant les infos sur un lycée 
    :return: (tuple of str) un tuple contenant le département et la commune '''

    return e["DEPARTEMENT"], e["COMMUNE"]

Ainsi, pour récupérer les enregistrements triés dans une variable nouvelle_table :

nouvelle_table = sorted(table, key=departement_puis_commune)

Pour être plus précis, le tri va ici se faire d'abord par rapport au département, puis, si plusieurs lycées sont dans le même département, le tri se fera alors par rapport à la commune.

Exercice 4

Complétez les fonctions trier_selon_patronyme, trier_selon_effectif et trier_selon_academie_et_effectif de manipulation_tables.py.
Vous pourrez utiliser la fonction native sorted de Python.

À noter qu'il est possible de définir une fonction à l'intérieur d'une fonction. Si vous avez besoin par exemple de créer une fonction permettant de comparer l'élément souhaité d'un dictionnaire, vous pourrez procéder ainsi.

Fusion de tables

Exercice 5

Complétez la fonction fusionner_deux_tables de manipulation_tables.py.

La fonction précédente fonctionne si l'on fusionne deux tables avec exactement les mêmes colonnes (les mêmes attributs, dans le même ordre).

Si deux tables ont des colonnes en commun et d'autres colonnes qui diffèrent, et que l'on souhaite fusionner leur contenu, il faut alors définir leur « domaine de valeur », c’est-à-dire quels sont les « champs » (ou colonnes) communs aux deux tables.

Voici un exemple avec deux tables que l'on souhaite fusionner :

table1 = [{"nom":"Duchmol","prenom":"Jean","moyenne":12},{"nom":"Martin","prenom":"Lise","moyenne":14}]
table2 = [{"nom":"Duchmol","prenom":"Jean","classe":"3A"},{"nom":"Martin","prenom":"Lise","classe":"5B"}]

On dispose de ces deux tables qui contiennent toutes les deux une colonne nom et une colonne prenom. La différence est que la première table contient une colonne moyenne tandis que la deuxième table contient une colonne classe. On aimerait créer une nouvelle table contenant le nom, le prénom, la moyenne et la classe de chaque élève, c'est-à-dire :

[{'nom': 'Duchmol', 'prenom': 'Jean', 'moyenne': 12, 'classe': '3A'}, {'nom': 'Martin', 'prenom': 'Lise', 'moyenne': 14, 'classe': '5B'}]

Exercice 6

Complétez les ... de la fonction à trous suivante :

###
def fusionner(table1, table2):bksl-nl ''' Effectue la fusion de deux tables de données qui possèdent desbksl-nl colonnes en commun et des colonnes qui diffèrent.bksl-nl :param table1: (list of dicts) une table de donnéesbksl-nl :param table2: (list of dicts) une autre table de donnéesbksl-nl :return: (list of dicts) une nouvelle table de données qui résulte de la fusion '''bksl-nlbksl-nl # on copie la table1 avec la méthode par compréhensionbksl-nl nouvellepy-undtable = [{cle: valeur for cle, valeur in e.items()} for e in table1]bksl-nlbksl-nl for i in range(...): # on parcourt la table2 par indicebksl-nl for cle, val in table2[i].items(): # pour chaque enregistrement (chaque dictionnaire) de la table2bksl-nl if ... in table1[i]: # si la clé était déjà présente dans la table1bksl-nl if val != table1[...][...]: # si la valeur est différente de celle de la table 1bksl-nl ... = val # on remplace l'ancienne valeur par la nouvellebksl-nl else: # si la clé n'était pas présente dans la table1bksl-nl ... # on ajoute la nouvelle clé en l'associant à la valeur valbksl-nl return nouvellepy-undtable # on renvoie la nouvelle tablebksl-nlbksl-nl# Test de la fonction fusionnerbksl-nltable1 = [{"nom":"Duchmol","prenom":"Jean","moyenne":12}, {"nom":"Martin","prenom":"Lise","moyenne":14}]bksl-nltable2 = [{"nom":"Duchmol","prenom":"Jean","classe":"3A"}, {"nom":"Martin","prenom":"Lise","classe":"5B"}]bksl-nlbksl-nlprint(fusionner(table1, table2))bksl-nldef fusionner(table1, table2):bksl-nl ''' Effectue la fusion de deux tables de données qui possèdent desbksl-nl colonnes en commun et des colonnes qui diffèrent.bksl-nl :param table1: (list of dicts) une table de donnéesbksl-nl :param table2: (list of dicts) une autre table de donnéesbksl-nl :return: (list of dicts) une nouvelle table de données qui résulte de la fusion '''bksl-nlbksl-nl # on copie la table1 avec la méthode par compréhensionbksl-nl nouvellepy-undtable = [{cle: valeur for cle, valeur in e.items()} for e in table1]bksl-nlbksl-nl for i in range(len(table2)): # on parcourt la table2bksl-nl for cle, val in table2[i].items(): # pour chaque enregistrement (chaque dictionnaire) de la table2bksl-nl if cle in table1[i]: # si la clé était déjà présente dans la table1bksl-nl if val != table1[i][cle]: # si la valeur est différente de celle de la table 1bksl-nl nouvellepy-undtable[i][cle] = val # on remplace l'ancienne valeur par la nouvellebksl-nl else: # si la clé n'était pas présente dans la table1bksl-nl nouvellepy-undtable[i][cle] = val # on ajoute la nouvelle clé en l'associant à la valeur valbksl-nl return nouvellepy-undtable # on renvoie la nouvelle tablebksl-nlbksl-nl# Test de la fonction fusionnerbksl-nltable1 = [{"nom":"Duchmol","prenom":"Jean","moyenne":12}, {"nom":"Martin","prenom":"Lise","moyenne":14}]bksl-nltable2 = [{"nom":"Duchmol","prenom":"Jean","classe":"3A"}, {"nom":"Martin","prenom":"Lise","classe":"5B"}]bksl-nlbksl-nlprint(fusionner(table1, table2))bksl-nl
Correction exercice 6

###
def fusionner(table1, table2):bksl-nl ''' Effectue la fusion de deux tables de données qui possèdent desbksl-nl colonnes en commun et des colonnes qui diffèrent.bksl-nl :param table1: (list of dicts) une table de donnéesbksl-nl :param table2: (list of dicts) une autre table de donnéesbksl-nl :return: (list of dicts) une nouvelle table de données qui résulte de la fusion '''bksl-nlbksl-nl # on copie la table1 avec la méthode par compréhensionbksl-nl nouvellepy-undtable = [{cle: valeur for cle, valeur in e.items()} for e in table1]bksl-nlbksl-nl for i in range(len(table2)): # on parcourt la table2bksl-nl for cle, val in table2[i].items(): # pour chaque enregistrement (chaque dictionnaire) de la table2bksl-nl if cle in table1[i]: # si la clé était déjà présente dans la table1bksl-nl if val != table1[i][cle]: # si la valeur est différente de celle de la table 1bksl-nl nouvellepy-undtable[i][cle] = val # on remplace l'ancienne valeur par la nouvellebksl-nl else: # si la clé n'était pas présente dans la table1bksl-nl nouvellepy-undtable[i][cle] = val # on ajoute la nouvelle clé en l'associant à la valeur valbksl-nl return nouvellepy-undtable # on renvoie la nouvelle tablebksl-nlbksl-nl# Test de la fonction fusionnerbksl-nltable1 = [{"nom":"Duchmol","prenom":"Jean","moyenne":12}, {"nom":"Martin","prenom":"Lise","moyenne":14}]bksl-nltable2 = [{"nom":"Duchmol","prenom":"Jean","classe":"3A"}, {"nom":"Martin","prenom":"Lise","classe":"5B"}]bksl-nlbksl-nlprint(fusionner(table1, table2))bksl-nl