Chapitre 12 : Recherche, tri et fusion de tables.

Introduction :

La recherche dans une table consiste à localiser des données spécifiques en fonction de critères, souvent via des clés ou des indices.

La fusion de tables combine deux tables sur une ou plusieurs colonnes communes, permettant de regrouper les informations provenant de plusieurs sources de données en une seule table pour une analyse plus complète.

Avant de commencer :

Pour les exercices qui suivent, on récupérera la liste des élèves du chapitre précédent.

Cette liste est sous la forme d'une table dont les premières lignes sont les suivantes :

Élèves — Première Générale
id_eleve nom prénom sexe lycée classe spécialité1 spécialité2 spécialité3
00001 Rochon Hugo M Lycée Edith Piaf 1g12 SVT HGGSP LLCE
00002 Grosset Octave M Lycée Alexandre Dumas 1g06 HGGSP SVT Math
00004 Monet Apolline F Lycée François Mitterrand 1g11 Arts_Plastique Physique-Chimie HGGSP
00005 Julien Chantal F Lycée Camille Claudel 1g13 LLCE SVT SES
00006 Delmas Ariane F Lycée Émile Zola 1g08 HGGSP Physique-Chimie SVT

On pourra au choix :

Dans les deux cas :

1. Exercice :

Ecrire les lignes permettant d'afficher les informations (la sous-liste complète) de l'élève ayant pour identifiant "11076".

Tests :

# Tests

Affichage :

Console:



    
>>>

2. Exercice :

Ecrire la fonction select_par_identifiant(liste, identifiant) qui retourne la sous-liste correspondant à l'élève ayant pour identifiant id.

Une fois fini, on pourra tester le code avec les identifiants suivants:

  • "00193"
  • "10446"
  • "10800"
def select_par_identifiant(liste, identifiant):

Tests :

# Tests

Affichage :

Console:



    
>>>

3. Exercice :

Ecrire la fonction select_par_nom(liste, nom) qui retourne une liste de listes de TOUS les élèves ayant le nom de famille passé en argument.

Chaque sous-liste correspondant à un élève ayant pour nom de famille nom.

On pourra tester le code avec les noms suivants:

  • "Moreau"
  • "Kerbrat"
  • "Perrot"

Une fois fini, répondre à la question suivante :

Combien d'élèves portent le nom de famille "Pagnol"?

def select_par_nom(liste, nom):

Tests :

# Tests

Affichage :

Console:



    
>>>

4. Exercice :

Ecrire la fonction select_par_nom_et_par_prenom(liste, nom, prenom) qui retourne une liste de listes de TOUS les élèves ayant le nom de famille ET le prénom passé en argument.

Chaque sous-liste correspondant à un élève ayant pour nom de famille nom et pour prénom prenom.

On pourra tester le code avec les noms et prénoms suivants:

  • "Fouquet" "Florence"
  • "Tanguy" "Élodie"
  • "Fernandez" "Olivier"
  • "Vannier" "Valérian"
def select_par_nom_et_par_prenom(liste, nom, prenom):

Tests :

# Tests

Affichage :

Console:



    
>>>

5. Exercice :

  1. Ecrire la fonction select_par_specialite(liste, specialite) qui retourne une liste de listes de TOUS les élèves faisant la spécialité cherchée.

    Afin de ne pas faire d'erreur, il peut être utile de regarder l'élève ayant pour identifiant "00013" 👀.

    On pourra tester le code avec les spécialités suivantes:

    • "Math"
    • "NSI"
  2. Ecrire la fonction select_par_specialite_count(liste, specialite) qui retourne le nombre d'élèves faisant une spécialité donnée.

  3. Écrire une fonction select_par_specialite_pourcentage(liste, specialite) qui renvoie un entier représentant le pourcentage d’élèves ayant choisi la spécialité indiquée. Le résultat devra être arrondi à l’entier le plus proche.

    Par exemple, si l'appel select_par_specialite_pourcentage(liste, "NSI") renvoie 32, cela signifie qu'à peu près 32% des élèves font la spécialité NSI.

def select_par_specialite(liste, specialite): pass # à compléter def select_par_specialite_count(liste, specialite): pass # à compléter def select_par_specialite_pourcentage(liste, specialite): pass # à compléter

Tests :

# Tests

Affichage :

Console:



    
>>>

6. Exercice :

Y a-t-il deux élèves ayant les mêmes noms et prénoms? Si oui lesquels?

Programmer une fonction cherche_homonymes(liste) qui renvoie une liste de listes de deux integers correspondant aux indices dans la liste où ils sont présents.

Par exemple, si la fonction renvoie [(1,40),(3,799)] cela signifie que liste[1] et liste[40] comportent les mêmes noms-prénoms et de même pour liste[3] et liste[799].

Une fois fini, programmer l'affichage de ces sous-listes.

def cherche_homonymes(liste): liste_retournee = [] for indice1 in range(1, ...): for indice2 in range(indice1, ...): if .............. : .............. return .... homonymes = cherche_homonymes(liste) for indice1, indice2 in homonymes: ...

Tests :

# Tests

Affichage :

Console:



    
>>>

7. Tri :

On peut effectuer plusieurs tris en fonction des différentes colonnes.

8. Exercice :

Nous continuons avec la même liste.

En modifiant l'algorithme de tri par selection afin qu'il ne prenne pas en compte la première sous-liste puisque ce sont les entêtes, faire les questions suivantes :

  1. Créer une fonction tri_par_prenom(liste) qui trie la liste des élèves de NSI en fonction de leurs prénoms.

    Elle renvoie une liste de listes ne contenant que les élèves qui font NSI.

    On pourra compléter le code suivant :

    def tri_par_prenom(liste):
        # On place les élèves de NSI dans une liste liste_NSI
        liste_NSI=[]
        for eleve in liste[1:]: # On ne prend pas les entêtes
            if "NSI" in eleve[6:]:
                liste_NSI.append(eleve)
    
        #On trie liste_NSI suivant les prénoms :
        n=len(liste_NSI)
        for k in range(n-1):
            imin = k
            for i in range(imin, n):
                if ............................... : #On compare les prénoms
                    imin=i
            liste_NSI[k], liste_NSI[imin] = liste_NSI[imin], liste_NSI[k]
        return liste_NSI
    
  2. Créer une fonction tri_par_nom(liste) qui trie la liste des élèves de NSI en fonction de leurs noms.
  3. Créer une fonction tri_par_classe_puis_prenom(liste) qui trie la liste des élèves de NSI en fonction de leurs classes puis de leurs prénoms.
def tri_par_prenom(liste): # On place les élèves de NSI dans une liste liste_NSI liste_NSI=[] for eleve in liste[1:]: # On ne prend pas les entêtes if "NSI" in eleve[6:]: liste_NSI.append(eleve) #On trie liste_NSI suivant les prénoms : n=len(liste_NSI) for k in range(n-1): imin = k for i in range(imin, n): if ............................... : #On compare les prénoms imin=i liste_NSI[k], liste_NSI[imin] = liste_NSI[imin], liste_NSI[k] return liste_NSI def tri_par_nom(liste): pass # à compléter def tri_par_classe_puis_prenom(liste): pass # à compléter

Tests :

# Tests

Affichage :

Console:



    
>>>

9. Fusion interne de deux tables :

La fusion interne consiste à combiner deux tables à partir d'une colonne commune, ici id, en ne gardant que les lignes dont la valeur d’identifiant est présente dans les deux tables.

Chaque ligne de la première table est comparée à chaque ligne de la seconde : lorsqu’un identifiant correspond, on crée une nouvelle ligne contenant les informations des deux.

ALGORITHME FusionInterne
Variables
    tableA, tableB, fusion : tableaux de tableaux
Début
    fusion ← tableau vide
    POUR chaque ligne de tableA FAIRE
        POUR chaque ligne de tableB FAIRE
            SI les identifiants des deux lignes sont égaux ALORS
                nouvelle_ligne ← [ fusion des deux lignes ]
                AJOUTER nouvelle_ligne à fusion
            FIN SI
        FIN POUR
    FIN POUR
    RETOURNER fusion
FIN

Ce code est bien entendu à adapter selon le contexte :

Le résultat contient uniquement les lignes dont l’identifiant id apparaît dans les deux tables. Cela illustre la notion de domaine de valeurs : les colonnes id des deux tables doivent représenter le même type d’identifiant pour que la fusion ait du sens.

10. Exercice : Fusion interne de deux tables

Écrire une fonction fusion_interne(tableA, tableB) qui fusionne deux tables (listes de listes) à partir de la colonne id (position 0) : on ne garde que les lignes dont l’id est présent dans les deux tables.

Le résultat est une nouvelle table commençant par une ligne d’en-tête :

["id", "nom", "prenom", "classe", "note", "coefficient"]

Exemple d’exécution (autre jeu de données) :

eleves_exemple = [
    ["id", "nom", "prenom", "classe"],
    [1, "Durand", "Alice", "1re A"],
    [2, "Moreau", "Benoît", "1re A"],
    [3, "Nguyen", "Lina", "1re B"]
]

notes_exemple = [
    ["id", "note", "coefficient"],
    [1, 14, 2],
    [3, 9, 1]
]

fusion_interne(eleves_exemple, notes_exemple)
→ [["id", "nom", "prenom", "classe", "note", "coefficient"],
   [1, "Durand", "Alice", "1re A", 14, 2],
   [3, "Nguyen", "Lina", "1re B", 9, 1]]
eleves = [ ["id", "nom", "prenom", "classe"], [1, "Durand", "Lucie", "2nde A"], [2, "Martin", "Léo", "2nde B"], [3, "Dubois", "Emma", "2nde A"], [4, "Bernard", "Julie", "2nde A"], [5, "Petit", "Tom", "2nde C"], [6, "Robert", "Hugo", "2nde B"], [7, "Richard", "Nina", "2nde A"], [8, "Simon", "Eva", "2nde C"], [9, "Lefevre", "Paul", "2nde A"], [10, "Morel", "Sofia", "2nde B"], [11, "Fournier", "Noah", "2nde A"], [12, "Girard", "Lina", "2nde C"], [13, "Andre", "Louis", "2nde B"], [14, "Mercier", "Elisa", "2nde C"], [15, "Garcia", "Maël", "2nde B"], [16, "Lambert", "Mila", "2nde A"], [17, "Bonnet", "Ethan", "2nde B"], [18, "Francois", "Chloé", "2nde A"], [19, "Collet", "Léa", "2nde C"], [20, "Perrin", "Axel", "2nde A"] ] notes = [ ["id", "note", "coefficient"], [1, 15, 2], [14, 9, 2], [16, 10, 1], [7, 11, 2], [9, 8, 1], [11, 17, 2], [18, 15, 2], [19, 14, 3], [20, 18, 2], [2, 12, 1], [4, 10, 3], [6, 14, 2], [12, 13, 1], [13, 16, 3] ] notes_bis = [ ["id", "note", "coefficient"], [2, 14, 2], [10, 16, 2], [11, 17, 1], [13, 13, 3], [3, 9, 1], [15, 18, 2], [16, 9, 2], [8, 10, 3], [9, 12, 2], [18, 15, 1], [19, 11, 3], [5, 11, 2], [6, 15, 1], [20, 20, 2] ] def fusion_interne(tableA, tableB): """ Retourne une nouvelle table contenant les seules lignes pour lesquelles l'id (colonne 0) apparaît dans les deux tables. Format du résultat : [ ["id", "nom", "prenom", "classe", "note", "coefficient"], ... ] """ ... # à compléter table = fusion_interne(eleves, notes) #affichage de la table sur une ligne print(table) #Affichage de la table ligne par ligne for ligne in table: for element in ligne: print(str(element).ljust(10), end="") print()

Tests :

# Vérifie l'entête, plusieurs correspondances spécifiques,
# la cohérence des données, le type, et la richesse du résultat

Affichage :

Console:


    
>>>

11. Exercice : Fusion clients ↔ factures (jointure interne)

On dispose de deux tables (listes de listes) :

  • clients : chaque ligne commence par id_client.
  • factures : chaque ligne contient id_facture, id_client, prix_a_payer, paye (booléen True/False).

Écrire une fonction fusion_clients_factures(clients, factures) qui effectue une jointure interne sur id_client et renvoie une nouvelle table (avec en-tête) dont chaque ligne est :

["id_facture", "id_client", "nom", "prenom", "ville", "prix_a_payer", "paye"]

Exemple (jeu court, différent des données réelles ci-dessous) :

clients_ex = [
    ["id_client","nom","prenom","ville"],
    [1,"Leroy","Anna","Lyon"],
    [2,"Roux","Marc","Lille"]
]
factures_ex = [
    ["id_facture","id_client","prix_a_payer","paye"],
    [101, 1, 49.9, True],
    [102, 2, 19.0, False]
]
fusion_clients_factures(clients_ex, factures_ex)
→ [["id_facture","id_client","nom","prenom","ville","prix_a_payer","paye"],
   [101,1,"Leroy","Anna","Lyon",49.9,True],
   [102,2,"Roux","Marc","Lille",19.0,False]]
clients = [ ["id_client","nom","prenom","ville"], [101,"Durand","Lucie","Lyon"], [102,"Martin","Léo","Paris"], [103,"Dubois","Emma","Marseille"], [104,"Bernard","Julie","Toulouse"], [105,"Petit","Tom","Nice"], [106,"Robert","Hugo","Nantes"], [107,"Richard","Nina","Strasbourg"], [108,"Simon","Eva","Bordeaux"], [109,"Lefevre","Paul","Lille"], [110,"Morel","Sofia","Rennes"], [111,"Fournier","Noah","Reims"], [112,"Girard","Lina","Le Havre"], [113,"Andre","Louis","Saint-Étienne"], [114,"Mercier","Elisa","Toulon"], [115,"Garcia","Maël","Grenoble"], [116,"Lambert","Mila","Dijon"], [117,"Bonnet","Ethan","Angers"], [118,"Francois","Chloé","Nîmes"], [119,"Collet","Léa","Clermont-Ferrand"], [120,"Perrin","Axel","Montpellier"], [121,"Blanc","Iris","Metz"], [122,"Guerin","Yanis","Besançon"] ] factures = [ ["id_facture","id_client","prix_a_payer","paye"], [1001,101,49.90,True], [1002,102,19.00,False], [1003,103,75.50,True], [1004,105,12.00,False], [1005,106,130.00,True], [1006,107,8.99,True], [1007,108,199.99,False], [1008,110,59.00,True], [1009,111,22.50,False], [1010,112,42.00,True], [1011,114,18.75,False], [1012,115,67.30,True], [1013,116,5.00,False], [1014,118,14.20,True], [1015,119,220.00,False], [1016,120,33.33,True], [1017,121,10.00,False], [1018,122,77.77,True], [1019,104,15.00,True], [1020,109,9.90,False], [1021,113,11.11,True] ] def fusion_clients_factures(clients, factures): """ Effectue une jointure interne sur id_client et renvoie : [ ["id_facture","id_client","nom","prenom","ville","prix_a_payer","paye"], [ ... ], ... ] Contraintes : uniquement des boucles/tests (pas de bibliothèques). """ resultat = [["id_facture","id_client","nom","prenom","ville","prix_a_payer","paye"]] ... # à compléter table = fusion_clients_factures(clients, factures) #affichage de la table sur une ligne print(table) #Affichage de la table ligne par ligne for ligne in table: for element in ligne: print(str(element).ljust(20), end="") print()

Tests :

Affichage :

Console:


    
>>>