NSI Première

Les données structurées - Activité 2

Le but de cette activité est de manipuler les fichiers CSV.

De nos jours, une des utilisations de l'informatique est de pouvoir stocker et traiter un grand nombre de données, par exemple les produits disponibles, les commandes et comptes clients dans un site marchand, les données médicales des patients dans un hôpital, ... Pour cela il faut organiser le stockage des données afin de pouvoir accéder facilement à des informations précises.

Même s'il existe de logiciels dédiés pour le traitement de données, il est assez facile en Python de mettre en oeuvre des opérations de base : chargement de données structurées, recherche d'une donnée particulière, suppression des doublons, fusion de données.

Par exemple, un tableau contenant le nom des élèves de la classe avec les notes obtenues à chacun des devoirs est un exemple de données structurées : on peut retrouver facilement la note d'un élève donné à un devoir donné en sélectionnant la ligne et la colonne correspondante.

Activité 1 : indexation de tables

On parle d'indexation de tables quand on crée une structure de données à partir de données en table.

En base de données, un index est une structure de données auxiliaire permettant un accès rapide aux données.

Dans cet exercice, nous allons voir comment indexer les données issues d'un fichier CSV de façon pratique et efficace.

  • Lire les données d'un fichier CSV avec Python

Les fichiers CSV sont des fichiers texte. Pour charger les données d'un fichier CSV nous pouvons utiliser les méthodes classiques de lecture de fichier que nous avons vu au chapitre 8 en utilisant la méthode split notamment. Cependant, il nous faudra alors charger les données, puis découper chaque ligne pour récupérer les attributs de chaque entrée, c'est à dire créer des fonctions dédiées à la forme de la table chargée.

Heureusement le langage Python propose un module csv qui va largement nous faciliter la tâche : il contient notamment des fonctions utilitaires pour lire et écrire des fichiers CSV.

Les instructions suivantes permettent de charger le module csv, d'ouvrir le fichier CSV dans une variable fichier et d'en récupérer les données sous la forme d'une liste de listes dans la variable table :

In [1]:
import csv
fichier = open('Inventeurs.csv', 'r', encoding = 'utf-8')
table = list(csv.reader(fichier))

La variable table contient alors une liste de listes de chaînes de caractères comme on peut le voir en exécutant la cellule suivante :

In [2]:
print(table)
[['Nom', 'Prénom', 'Pays', 'Naissance', 'Mort', 'Invention'], ['Bell', 'Alexander Graham', 'Ecosse', '1847', '1922', 'téléphone'], ['Berners-Lee', 'Timothee', 'Angleterre', '1955', '', 'HTML'], ['Boole', 'George', 'Angleterre', '1815', '1864', 'algèbre de Boole'], ['Dijkstra', 'Edsger Wybe', 'Pays-Bas', '1930', '2002', 'algorithme du plus court chemin'], ['Gates', 'Bill', 'Etats-Unis', '1955', '', 'Microsoft'], ['Hopper', 'Grace', 'Etats-Unis', '1906', '1992', 'premier compilateur et langage COBOL'], ['Jobs', 'Steve', 'Etats-Unis', '1955', '2011', 'Apple'], ['Krivine', 'Jean-Louis', 'France', '1939', '', 'Lambda-calcul'], ['Von Neumann', 'John', 'Hongrie', '1903', '1957', 'architecture des ordinateurs'], ['Shannon', 'Claude', 'Etats-Unis', '1916', '2001', "théorie de l'information"], ['Torvalds', 'Linus', 'Finlande', '1969', '', 'Linux'], ['Turing', 'Alan', 'Angleterre', '1912', '1954', 'intelligence artificielle']]
  1. Que contient table[0] ?
table[0] contient une liste de chaîne de caractères contenant les descripteurs.
  1. Compléter le code suivant pour obtenir la liste des noms de tous les inventeurs américains :
In [3]:
américain = []
for i in range(1, 11):
    if table[i][2] == 'Etats-Unis':
        américain.append(table[i][1])

américain # permet d'afficher le contenu de la liste
Out[3]:
['Bill', 'Grace', 'Steve', 'Claude']

Est-ce pratique ?

Non ce n'est pas pratique.
  1. De quel type est table[2][3] ? Est approprié ? Comment remédier à cela ?

Ecrire la réponse ici

table[2][3] est de type string. Ce n'est pas approprié si l'on doit utiliser des nombres ou des dates

On constate que la structure de données de listes de listes n'est pas idéale puisque la première liste de table contient le nom des champs et qu'il faudra donc l'ignorer dans le traitement des données mais qu'il faudra en récupérer les informations pour connaître la correspondance entre le nom des attributs et leur position dans les sous-listes.

Une autre solution consiste alors en le choix d'un autre type de structure de données : une liste de dictionnaires partageant les mêmes clés. Pour cela, le module csv dispose d'une fonction nommée DictReader que l'on l'utilise de la façon suivante :

In [4]:
import csv
fichier = open('Inventeurs.csv', 'r', encoding = 'utf-8')
table = list(csv.DictReader(fichier))

Remarque : lorsque le séparateur des données du fichier CSV n'est plus la virgule mais, par exemple, le point-virgule, on précise un paramètre supplémentaire nommé delimiter dans la fonction DictReader. Par exemple, dans le cas du point-virgule, on écrira

table = list(csv.DictReader(fichier, delimiter = ';'))

Avec cette méthode, la variable table contient cette fois une liste de dictionnaires comme on peut le voir en exécutant la cellule suivante :

In [5]:
print(table)
[{'Nom': 'Bell', 'Prénom': 'Alexander Graham', 'Pays': 'Ecosse', 'Naissance': '1847', 'Mort': '1922', 'Invention': 'téléphone'}, {'Nom': 'Berners-Lee', 'Prénom': 'Timothee', 'Pays': 'Angleterre', 'Naissance': '1955', 'Mort': '', 'Invention': 'HTML'}, {'Nom': 'Boole', 'Prénom': 'George', 'Pays': 'Angleterre', 'Naissance': '1815', 'Mort': '1864', 'Invention': 'algèbre de Boole'}, {'Nom': 'Dijkstra', 'Prénom': 'Edsger Wybe', 'Pays': 'Pays-Bas', 'Naissance': '1930', 'Mort': '2002', 'Invention': 'algorithme du plus court chemin'}, {'Nom': 'Gates', 'Prénom': 'Bill', 'Pays': 'Etats-Unis', 'Naissance': '1955', 'Mort': '', 'Invention': 'Microsoft'}, {'Nom': 'Hopper', 'Prénom': 'Grace', 'Pays': 'Etats-Unis', 'Naissance': '1906', 'Mort': '1992', 'Invention': 'premier compilateur et langage COBOL'}, {'Nom': 'Jobs', 'Prénom': 'Steve', 'Pays': 'Etats-Unis', 'Naissance': '1955', 'Mort': '2011', 'Invention': 'Apple'}, {'Nom': 'Krivine', 'Prénom': 'Jean-Louis', 'Pays': 'France', 'Naissance': '1939', 'Mort': '', 'Invention': 'Lambda-calcul'}, {'Nom': 'Von Neumann', 'Prénom': 'John', 'Pays': 'Hongrie', 'Naissance': '1903', 'Mort': '1957', 'Invention': 'architecture des ordinateurs'}, {'Nom': 'Shannon', 'Prénom': 'Claude', 'Pays': 'Etats-Unis', 'Naissance': '1916', 'Mort': '2001', 'Invention': "théorie de l'information"}, {'Nom': 'Torvalds', 'Prénom': 'Linus', 'Pays': 'Finlande', 'Naissance': '1969', 'Mort': '', 'Invention': 'Linux'}, {'Nom': 'Turing', 'Prénom': 'Alan', 'Pays': 'Angleterre', 'Naissance': '1912', 'Mort': '1954', 'Invention': 'intelligence artificielle'}]
  1. Comment ont été créés les clés communes à tous les dictionnaires ?
Elles ont été creer à partir des deescriteurs du tableau.
  1. Comment accède-t-on alors à l'année de naissance du premier inventeur ?
In [6]:
# Ecrire l'instruction ici
table[0]['Naissance']
Out[6]:
'1847'
  1. De quel type sont les attributs des champs des dictionnaires obtenus ? Cela est-il toujours approprié ?
Les attributs des champs des dictionnaires obtenus sont de type string. Toutefois, ça n'est pas toujours approprié notamment pour les dates.
  1. Peut-on alors considérer que les données obtenues sous cette forme sont tout à fait conformes aux données d'origine et à l'utilisation que l'on veut en faire ?
Non pas vraiment lorsqu'il s'agit de données numériques.
  • Validation des données

Il peut être utile que les dates soient stockées sous forme d'entier et donc que le type de ces données soit int. Il faut donc, dans la plupart des cas, écrire une fonction de validation (ou une procédure si la fonction ne renvoie rien) qui transtype les données obtenues sous forme de chaînes de caractères dans le type adéquat.

  1. Compléter la fonction suivante de façon à ce que les dates de naissance et de mort (si cette dernière existe) soient des nombres entiers. Si la date de mort n'existe pas, c'est-à-dire que l'inventeur est toujours vivant, on attribuera à cet attribut la valeur None.
In [7]:
def validation_table(table_données):
    '''
    table_données est une liste de dictionnaires obtenue à partir d'un
    fichier CSV formaté comme le fichier Inventeurs.csv
    '''
    for i in range(len(table_données)):
        table_données[i]['Naissance'] = int(table_données[i]['Naissance'])
        if table_données[i]['Mort'] != "":
            table_données[i]['Mort'] = int(table_données[i]['Mort'])
        else:
            table_données[i]['Mort'] = None
    return table_données

La cellule suivante affiche la table des inventeurs après validation des données afin de vérifier que le résultat obtenu est cohérent avec ce que l'on désire.

In [8]:
table = validation_table(table)
table
Out[8]:
[{'Nom': 'Bell', 'Prénom': 'Alexander Graham', 'Pays': 'Ecosse', 'Naissance': 1847, 'Mort': 1922, 'Invention': 'téléphone'}, {'Nom': 'Berners-Lee', 'Prénom': 'Timothee', 'Pays': 'Angleterre', 'Naissance': 1955, 'Mort': None, 'Invention': 'HTML'}, {'Nom': 'Boole', 'Prénom': 'George', 'Pays': 'Angleterre', 'Naissance': 1815, 'Mort': 1864, 'Invention': 'algèbre de Boole'}, {'Nom': 'Dijkstra', 'Prénom': 'Edsger Wybe', 'Pays': 'Pays-Bas', 'Naissance': 1930, 'Mort': 2002, 'Invention': 'algorithme du plus court chemin'}, {'Nom': 'Gates', 'Prénom': 'Bill', 'Pays': 'Etats-Unis', 'Naissance': 1955, 'Mort': None, 'Invention': 'Microsoft'}, {'Nom': 'Hopper', 'Prénom': 'Grace', 'Pays': 'Etats-Unis', 'Naissance': 1906, 'Mort': 1992, 'Invention': 'premier compilateur et langage COBOL'}, {'Nom': 'Jobs', 'Prénom': 'Steve', 'Pays': 'Etats-Unis', 'Naissance': 1955, 'Mort': 2011, 'Invention': 'Apple'}, {'Nom': 'Krivine', 'Prénom': 'Jean-Louis', 'Pays': 'France', 'Naissance': 1939, 'Mort': None, 'Invention': 'Lambda-calcul'}, {'Nom': 'Von Neumann', 'Prénom': 'John', 'Pays': 'Hongrie', 'Naissance': 1903, 'Mort': 1957, 'Invention': 'architecture des ordinateurs'}, {'Nom': 'Shannon', 'Prénom': 'Claude', 'Pays': 'Etats-Unis', 'Naissance': 1916, 'Mort': 2001, 'Invention': "théorie de l'information"}, {'Nom': 'Torvalds', 'Prénom': 'Linus', 'Pays': 'Finlande', 'Naissance': 1969, 'Mort': None, 'Invention': 'Linux'}, {'Nom': 'Turing', 'Prénom': 'Alan', 'Pays': 'Angleterre', 'Naissance': 1912, 'Mort': 1954, 'Invention': 'intelligence artificielle'}]

Il peut également être utile de vérifier la cohérence des données au moment de cette étape de validation. Par exemple, on pourrait également vouloir vérifier que l'année de mort n'est pas inférieure à l'année de naissance pour éviter des erreurs futures, ou bien que l'on ne stocke pas deux fois la même information, autrement dit qu'il n'y a pas deux dictionnaires identiques dans la liste table (on parle alors de suppression de doublons). Les modifications à réaliser à l'étape de validation vont donc dépendre du type de données que l'on manipule et de comment on veut les traiter. Il faudra pour cela avoir connaissance du type unique de chacun des champs et des traitements que l'on voudra réaliser.

  • Ecrire dans un fichier CSV

Le module csv permet de sauvegarder dans un fichier des données issues d'une liste de dictionnaires préalablement créée. Pour cela on utilise les instructions suivantes où la variable nom_fichier est une chaîne de caractères donnant le nom du fichier à créer et la variable liste_champs contient la liste des champs de la table table (liste de dictionnaires) considérée:

with open(nom_fichier,'w', encoding = 'utf-8') as fichiercsv:
    liste_champs = table[0].keys()
    writer = csv.DictWriter(fichiercsv, liste_champs)
    writer.writeheader()
    writer.writerows(table)

Noter que l'on récupère ici la liste des champs avec la méthode keys de façon à éviter des éventuelles erreurs de saisie.

  1. Créer une liste de dictionnaires contenant les noms, prénoms, et le nom des deux spécialités de terminale de trois de vos camarades puis écrire ces données dans un fichier nommé Spécialités.csv à l'aide de la méthode décrite ci-dessus. L'ouvrir ensuite pour vérifier qu'il a été correctement créé.
In [11]:
# Ecrire les intructions de création ici
liste = [{'Nom': 'HOARAU', 'Prénom': 'Maëlys', 'Spé1': 'Maths', 'Spé2': 'Physique-chimie'}, {'Nom': 'LECONTE', 'Prénom': 'Jérémy', 'Spé1': 'SVT', 'Spé2': 'NSI'}, {'Nom': 'ALDEBARAN', 'Prénom': 'Vincent', 'Spé1': 'HGGSP', 'Spé2': 'NSI'}]
with open('Spécialités.csv','w', encoding = 'utf-8') as fichiercsv:
    liste_champs = liste[0].keys()
    writer = csv.DictWriter(fichiercsv, liste_champs)
    writer.writeheader()
    writer.writerows(liste)
In [12]:
# Ecrire les intructions de vérification ici
fichier = open('Spécialités.csv', 'r', encoding = 'utf-8')
liste2 = list(csv.reader(fichier))
print(liste2)
[['Nom', 'Prénom', 'Spé1', 'Spé2'], ['HOARAU', 'Maëlys', 'Maths', 'Physique-chimie'], ['LECONTE', 'Jérémy', 'SVT', 'NSI'], ['ALDEBARAN', 'Vincent', 'HGGSP', 'NSI']]

Activité 2 : rechercher dans une table

Maintenant que nous savons comment récupérer des données en table à partir d'un fichier CSV, nous allons voir comment exploiter ces données. On va pouvoir extraire des données particulières, tester la présence de certaines données, faire des statistiques, etc. Ces opérations de manipulation de données sont appelées des requêtes.

  • Recherche simple

En général on ne recherchera pas une ligne particulière mais plutôt une donnée associée à la valeur d'un attribut. Par exemple, on pourrait vouloir rechercher si un inventeur du nom de Pascal est présent dans la table Inventeurs.csv et ensuite obtenir éventuellement des informations liées à cette entrée.

On reprend ici la fichier Inventeurs.csv. Exécuter la cellule suivante pour récupérer ses données dans table (donnée ici sous la forme d'une liste de dictionnaires)

In [13]:
import csv
with open('Inventeurs.csv','r', encoding = 'utf-8') as fichier:
    table = list(csv.DictReader(fichier))
    table = validation_table(table)
    
table
Out[13]:
[{'Nom': 'Bell', 'Prénom': 'Alexander Graham', 'Pays': 'Ecosse', 'Naissance': 1847, 'Mort': 1922, 'Invention': 'téléphone'}, {'Nom': 'Berners-Lee', 'Prénom': 'Timothee', 'Pays': 'Angleterre', 'Naissance': 1955, 'Mort': None, 'Invention': 'HTML'}, {'Nom': 'Boole', 'Prénom': 'George', 'Pays': 'Angleterre', 'Naissance': 1815, 'Mort': 1864, 'Invention': 'algèbre de Boole'}, {'Nom': 'Dijkstra', 'Prénom': 'Edsger Wybe', 'Pays': 'Pays-Bas', 'Naissance': 1930, 'Mort': 2002, 'Invention': 'algorithme du plus court chemin'}, {'Nom': 'Gates', 'Prénom': 'Bill', 'Pays': 'Etats-Unis', 'Naissance': 1955, 'Mort': None, 'Invention': 'Microsoft'}, {'Nom': 'Hopper', 'Prénom': 'Grace', 'Pays': 'Etats-Unis', 'Naissance': 1906, 'Mort': 1992, 'Invention': 'premier compilateur et langage COBOL'}, {'Nom': 'Jobs', 'Prénom': 'Steve', 'Pays': 'Etats-Unis', 'Naissance': 1955, 'Mort': 2011, 'Invention': 'Apple'}, {'Nom': 'Krivine', 'Prénom': 'Jean-Louis', 'Pays': 'France', 'Naissance': 1939, 'Mort': None, 'Invention': 'Lambda-calcul'}, {'Nom': 'Von Neumann', 'Prénom': 'John', 'Pays': 'Hongrie', 'Naissance': 1903, 'Mort': 1957, 'Invention': 'architecture des ordinateurs'}, {'Nom': 'Shannon', 'Prénom': 'Claude', 'Pays': 'Etats-Unis', 'Naissance': 1916, 'Mort': 2001, 'Invention': "théorie de l'information"}, {'Nom': 'Torvalds', 'Prénom': 'Linus', 'Pays': 'Finlande', 'Naissance': 1969, 'Mort': None, 'Invention': 'Linux'}, {'Nom': 'Turing', 'Prénom': 'Alan', 'Pays': 'Angleterre', 'Naissance': 1912, 'Mort': 1954, 'Invention': 'intelligence artificielle'}]
  1. Compléter la fonction ci-dessous afin qu'elle vérifie sa documentation.
In [14]:
def appartient(nom, table_données):
    '''
    nom est une chaîne de caractères
    table_données est une liste de dictionnaires obtenue à partir d'un
    fichier CSV formaté comme le fichier Inventeurs.csv
    Renvoie True si l'inventeur nom est dans table_données
    '''
    for inventeur in table_données:
        if inventeur['Nom'] == nom:
            return True
    return False

Les deux cellules ci-dessous permettent de tester votre code. Elles doivent toutes renvoyer True lors de leur exécution.

In [15]:
appartient('Turing', table) == True
Out[15]:
True
In [16]:
appartient('Mozart', table) == False
Out[16]:
True
  1. Sur le même principe, écrire une fonction pays renvoyant le pays d'origine d'un inventeur dont le nom nom est passé en argument. Par exemple, l'instruction pays('Bell', table) renvoie Ecosse.
In [17]:
def pays(nom, table_données):
    # code à compléter
    for inventeur in table_données:
        if inventeur['Nom'] == nom:
            return inventeur['Pays']

Les trois cellules ci-dessous permettent de tester votre code. Elles doivent toutes renvoyer True lors de leur exécution.

In [18]:
pays('Bell', table) == 'Ecosse'
Out[18]:
True
In [19]:
pays('Turing', table) == 'Angleterre'
Out[19]:
True
In [20]:
pays('Mozart', table) == None
Out[20]:
True

Remarque : il se peut que plusieurs entrées aient le même attribut (par exemple le même nom). Dans ce cas on peut affiner sa recherche en donnant d'autres attributs pour identifier une entrée particulière. Il suffira alors d'ajouter des paramètres à la fonction.

  • Agrégation

Quelquefois on désire récupérer des données issues de plusieurs entrées. On parle alors d'agrégation de données. On peut par exemple compter le nombre de lignes réalisant une certaine condition et on pourra alors produire des statistiques sur ces données.

Par exemple, si on veut compter tous les inventeurs nés en 1955, on procèdera de l'une des façons suivantes :

def inventeurs_nes_en(annee, table_données):
    nb = 0
    for inventeur in table_données:
        if inventeur['Naissance'] == annee:
            nb += 1
    return nb

ou

def inventeurs_nes_en(annee, table_données):
    return len([inventeur for inventeur in table_données if inventeur['Naissance'] == annee])

L'appel inventeurs_nes_en(1955, table) renvoie alors 3.

  1. Ecrire dans la cellule ci-dessous une fonction inventeurs_pays renvoyant le nombre d'inventeurs d'un même pays pays dans la table table. Par exemple, l'instruction inventeurs_pays('Angleterre', table) renvoie 3.
In [21]:
def inventeurs_pays(pays, table_donnees):
    # code à compléter
    return len([inventeur for inventeur in table_donnees if inventeur['Pays'] == pays])

Les trois cellules ci-dessous permettent de tester votre code. Elles doivent toutes renvoyer True lors de leur exécution.

In [22]:
inventeurs_pays('Angleterre', table) == 3
Out[22]:
True
In [23]:
inventeurs_pays('Hongrie', table) == 1
Out[23]:
True
In [24]:
inventeurs_pays('Allemagne', table) == 0
Out[24]:
True
  1. Compléter la fonction ci-dessous afin qu'elle renvoie l'âge moyen des inventeurs encore vivants issus de la table des inventeurs passée en paramètre.
In [33]:
def age_moyen_vivant(table_données):
    s_age = 0 # somme des âges des inventeurs
    nb = 0 # accumulateur pour compter les inventeurs
    for inventeur in table_données:
        if inventeur['Mort'] == None: # l'inventeur est vivant
            age = 2025 - inventeur['Naissance']
            s_age += age
            nb += 1
    return s_age / nb # renvoie la moyenne

La cellule ci-dessous doit renvoyer True lors de son exécution.

In [34]:
age_moyen_vivant(table) == 70.5
Out[34]:
True
  • Sélection de lignes

Jusqu'à présent nous avons extrait une seule information à partir d'une table de données : le résultat d'un test , la valeur d'un attribut, ou encore une valeur agrégée. L'opération appelée sélection consiste cette fois à extraire plusieurs lignes (ou entrées) vérifiant une condition.

Par exemple, pour extraire tous les inventeurs anglais de la table inventeurs.csv, on peut procéder par compréhension de la façon suivante :

anglais = [inventeur for inventeur in table if inventeur['Pays] == 'Angleterre']

Lorsque le critère est plus complexe, on définit en général au préalable une fonction de test.

Remarque : pour procéder à la sélection, on peut également choisir de créer une liste vide et la remplir au fur et à mesure avec la méthode append.

  1. Ecrire ci-dessous une instruction permettant d'extraire de la table des inventeurs tous les inventeurs encore vivants.
In [35]:
vivants = [inventeur for inventeur in table if inventeur['Mort'] == None]

La cellule ci-dessous doit renvoyer True lors de son exécution.

In [36]:
vivants == [{'Nom': 'Berners-Lee', 'Prénom': 'Timothee', 'Pays': 'Angleterre', 'Naissance': 1955, 'Mort': None, 'Invention': 'HTML'},
            {'Nom': 'Gates', 'Prénom': 'Bill', 'Pays': 'Etats-Unis', 'Naissance': 1955, 'Mort': None, 'Invention': 'Microsoft'},
            {'Nom': 'Krivine', 'Prénom': 'Jean-Louis', 'Pays': 'France', 'Naissance': 1939, 'Mort': None, 'Invention': 'Lambda-calcul'}, {'Nom': 'Torvalds', 'Prénom': 'Linus', 'Pays': 'Finlande', 'Naissance': 1969, 'Mort': None, 'Invention': 'Linux'}]
Out[36]:
True
  • Sélection de lignes et de colonnes

Il se peut que l'on veuille construire une nouvelle table de données ne contenant que certaines colonnes. Par exemple, extraire uniquement les noms des inventeurs de la table d'inventeurs, ou encore, seulement leur nom et leur invention. On dit alors que l'on procède à une projection.

Par exemple, pour extraire les noms des inventeurs, on peut procéder comme suit :

noms = [inventeur['Nom'] for inventeur in table]

De même, pour extraire les noms des inventeurs et leur invention, on peut procéder comme suit :

noms_et_inventions = [{'Nom':inv['Nom'], 'Invention':inv['Invention']} for inv in table]
  1. Ecrire ci-dessous une instruction permettant de construire une nouvelle table d'inventeurs contenant uniquement les noms, prénoms et âges des inventeurs encore vivant.
In [37]:
nouvelle_table = [{'Nom': inv['Nom'], 'Prénom': inv['Prénom'], 'Age': 2023 - inv['Naissance']} for inv in table if inv['Mort'] == None]

La cellule ci-dessous doit renvoyer True lors de son exécution.

In [38]:
nouvelle_table == [{'Nom': 'Berners-Lee', 'Prénom': 'Timothee', 'Age': 68}, {'Nom': 'Gates', 'Prénom': 'Bill', 'Age': 68},
                   {'Nom': 'Krivine', 'Prénom': 'Jean-Louis', 'Age': 84},
                   {'Nom': 'Torvalds', 'Prénom': 'Linus', 'Age': 54}]
Out[38]:
True

Pour aller plus loin

Exercice 1 : tri d'une table

On peut vouloir trier une table suivant une colonne. Par exemple, on peut vouloir trier les inventeurs selon leur année de naissance. Cette manipulation est assez complexe puisqu'il s'agit de classer les entrées dans l'ordre croissant des valeurs d'un champ donné. Heureusement, Python propose la fonction sorted qui peut être appliquée sur un dictionnaire en définissant au préalable une fonction renvoyant les valeurs que l'on peut comparer.

Par exemple, pour trier notre table d'inventeurs selon l'année de naissance, on commence par définir la fonction suivante renvoyant l'année de naissance d'un inventeur donné :

def naissance(inventeur):
    return inventeur['Naissance']

On peut alors définir une nouvelle table dans laquelle les inventeurs de la table table ont été trié par ordre croissant sur leur année de naissance.

tri_naissance = sorted(table, key = naissance)

Noter que l'on peut également trier les données par ordre décroissante en ajoutant le paramètre reverse = True comme suit :

tri_naissance = sorted(table, key = naissance, reverse = True)
  1. Compléter la fonction ci-dessous afin qu'elle renvoie l'invention d'un inventeur.
In [39]:
def invention(inventeur):
    return inventeur["Invention"]
  1. Ecrire alors ci-dessous une instruction permettant de construire la table des inventeurs classés par ordre alphabétique de leur invention.
In [40]:
tri_invention = sorted(table, key = invention)

La cellule ci-dessous doit renvoyer True lors de son exécution.

In [41]:
tri_invention == [{'Nom': 'Jobs', 'Prénom': 'Steve', 'Pays': 'Etats-Unis', 'Naissance': 1955, 'Mort': 2011, 'Invention': 'Apple'},
                  {'Nom': 'Berners-Lee', 'Prénom': 'Timothee', 'Pays': 'Angleterre', 'Naissance': 1955, 'Mort': None, 'Invention': 'HTML'},
                  {'Nom': 'Krivine', 'Prénom': 'Jean-Louis', 'Pays': 'France', 'Naissance': 1939, 'Mort': None, 'Invention': 'Lambda-calcul'},
                  {'Nom': 'Torvalds', 'Prénom': 'Linus', 'Pays': 'Finlande', 'Naissance': 1969, 'Mort': None, 'Invention': 'Linux'},
                  {'Nom': 'Gates', 'Prénom': 'Bill', 'Pays': 'Etats-Unis', 'Naissance': 1955, 'Mort': None, 'Invention': 'Microsoft'},
                  {'Nom': 'Dijkstra', 'Prénom': 'Edsger Wybe', 'Pays': 'Pays-Bas', 'Naissance': 1930, 'Mort': 2002, 'Invention': 'algorithme du plus court chemin'},
                  {'Nom': 'Boole', 'Prénom': 'George', 'Pays': 'Angleterre', 'Naissance': 1815, 'Mort': 1864, 'Invention': 'algèbre de Boole'},
                  {'Nom': 'Von Neumann', 'Prénom': 'John', 'Pays': 'Hongrie', 'Naissance': 1903, 'Mort': 1957, 'Invention': 'architecture des ordinateurs'},
                  {'Nom': 'Turing', 'Prénom': 'Alan', 'Pays': 'Angleterre', 'Naissance': 1912, 'Mort': 1954, 'Invention': 'intelligence artificielle'},
                  {'Nom': 'Hopper', 'Prénom': 'Grace', 'Pays': 'Etats-Unis', 'Naissance': 1906, 'Mort': 1992, 'Invention': 'premier compilateur et langage COBOL'},
                  {'Nom': 'Shannon', 'Prénom': 'Claude', 'Pays': 'Etats-Unis', 'Naissance': 1916, 'Mort': 2001, 'Invention': "théorie de l'information"},
                  {'Nom': 'Bell', 'Prénom': 'Alexander Graham', 'Pays': 'Ecosse', 'Naissance': 1847, 'Mort': 1922, 'Invention': 'téléphone'}]
Out[41]:
True

Exercice 2 : fusion de tables

Quand on travaille avec des données en table, il est fréquent d'avoir plusieurs jeux de données. Il alors souvent utile de fusionner toutes ces données en une seule table. Plusieurs cas sont possibles : les tables contiennent le même type d'information (par exemple les prénoms des enfants nés en 2007 et ceux des enfants nés en 2008 dans une même ville) ou bien elles contiennent des informations complémentaires (les notes obtenues à une épreuve associées à numéro de candidat, et l'identité des candidats associée à chaque numéro). Dans le premier cas on voudra réaliser une réunion de tables et dans le second à une opération de jointure.

  • Réunion de tables

Le langage Python permet de procéder très simplement grâce à l'opérateur de concaténation + qui est compatible avec les dictionnaires. Il s'agit tout simplement de charger les jeux de données dans deux dictionnaires et de les concaténer.

Par exemple, on dispose des deux tables d'inventeurs (fichiers Inventeurs1.csv et Inventeurs2.csv) que l'on désire réunir en un seul fichier Inventeurs_complet.csv.

  1. Exécuter la cellules suivante pour réunir ces deux tables :
In [42]:
import csv

fInv1 = open('Inventeurs1.csv', 'r', encoding = 'utf-8')
fInv2 = open('Inventeurs2.csv', 'r', encoding = 'utf-8')

tInv1 = list(csv.DictReader(fInv1, delimiter = ';'))
tInv2 = list(csv.DictReader(fInv2, delimiter = ';'))

print('tInv1',':',tInv1)
print('\n')
print('tInv2',':',tInv2)
             
tInvFus = tInv1 + tInv2

print('\n')
print('tInvFus',':',tInvFus)
tInv1 : [{'Nom': 'Bell', 'Prénom': 'Alexander Graham', 'Pays': 'Ecosse', 'Naissance': '1847', 'Mort': '1922', 'Invention': 'téléphone'}, {'Nom': 'Berners-Lee', 'Prénom': 'Timothee', 'Pays': 'Angleterre', 'Naissance': '1955', 'Mort': '', 'Invention': 'HTML'}, {'Nom': 'Hopper', 'Prénom': 'Grace', 'Pays': 'Etats-Unis', 'Naissance': '1906', 'Mort': '1992', 'Invention': 'premier compilateur et langage COBOL'}, {'Nom': 'Jobs', 'Prénom': 'Steve', 'Pays': 'Etats-Unis', 'Naissance': '1955', 'Mort': '2011', 'Invention': 'Apple'}]


tInv2 : [{'Nom': 'Boole', 'Prénom': 'George', 'Pays': 'Angleterre', 'Naissance': '1815', 'Mort': '1864', 'Invention': 'algèbre de Boole'}, {'Nom': 'Von Neumann', 'Prénom': 'John', 'Pays': 'Hongrie', 'Naissance': '1903', 'Mort': '1957', 'Invention': 'architecture des ordinateurs'}, {'Nom': 'Shannon', 'Prénom': 'Claude', 'Pays': 'Etats-Unis', 'Naissance': '1916', 'Mort': '2001', 'Invention': "théorie de l'information"}]


tInvFus : [{'Nom': 'Bell', 'Prénom': 'Alexander Graham', 'Pays': 'Ecosse', 'Naissance': '1847', 'Mort': '1922', 'Invention': 'téléphone'}, {'Nom': 'Berners-Lee', 'Prénom': 'Timothee', 'Pays': 'Angleterre', 'Naissance': '1955', 'Mort': '', 'Invention': 'HTML'}, {'Nom': 'Hopper', 'Prénom': 'Grace', 'Pays': 'Etats-Unis', 'Naissance': '1906', 'Mort': '1992', 'Invention': 'premier compilateur et langage COBOL'}, {'Nom': 'Jobs', 'Prénom': 'Steve', 'Pays': 'Etats-Unis', 'Naissance': '1955', 'Mort': '2011', 'Invention': 'Apple'}, {'Nom': 'Boole', 'Prénom': 'George', 'Pays': 'Angleterre', 'Naissance': '1815', 'Mort': '1864', 'Invention': 'algèbre de Boole'}, {'Nom': 'Von Neumann', 'Prénom': 'John', 'Pays': 'Hongrie', 'Naissance': '1903', 'Mort': '1957', 'Invention': 'architecture des ordinateurs'}, {'Nom': 'Shannon', 'Prénom': 'Claude', 'Pays': 'Etats-Unis', 'Naissance': '1916', 'Mort': '2001', 'Invention': "théorie de l'information"}]
  1. Ecrire ces données fusionnées dans un fichier nommé Inventeurs_complet.csv, puis l'ouvrir pour vérifier qu'il a été correctement créé.
In [44]:
# Ecrire les intructions ici
with open("Inventeurs_complet.csv", 'w', encoding = 'utf8') as fichier:
    writer = csv.DictWriter(fichier, tInvFus[1].keys(), delimiter = ',')
    writer.writeheader()
    writer.writerows(tInvFus)
    

Remarque : pour que des opérations sur cette réunion de tables soient possibles, il faut s'assurer que les deux tables sont construites de la même façon : les champs et les types de données doivent être les mêmes.

  • Opération de jointure

Quelquefois, on veut produire une nouvelle table à partir de deux tables distinctes en fusionnant certaines informations. Cela n'est possible que si les deux tables contiennent une information commune non ambigüe, c'est-à-dire qu'il n'est pas possible de fusionner deux informations qui ne vont pas ensemble. Par exemple, si l'on veut fusionner les notes obtenues à l'épreuve de NSI associées à un numéro de candidat, avec la correspondance entre les numéros d'anonymat des élèves et leur identité, il ne faut pas qu'il existe deux numéros de candidat identiques. Il faut ainsi avoir un identifiant unique commun aux deux tables pour réaliser une opération de jointure.