11. TP - Traitements de données



11.1. Introduction

Manipuler des données et en extraire de l'information est une compétence primordiale dans les domaines tels que l'Apprentissage, la Fouille de Données (ce que l'on qualifie en anglais de Machine Learning, Data Mining ou de Big Data).

Python offre certaines facilités de traitement des données. Nous allons en étudier quelques unes.

11.2. La fonction zip

La fonction zip prend en entrée plusieurs itérables (listes , dictionnaires) ayant généralement la même longueur et les regroupe en tuples. On peut alors itérer (parcourir) sur ces tuples.

Si les itérables ont des tailles différentes, zip s'arrête à la fin du plus petit itérable.

Voici un exemple avec deux listes pour lequel on veut associer chaque élément de la liste_1 avec un élément de la liste_2 en suivant l'ordre de parcours des listes :

  1. # Données initiales sous forme de deux tableaux
  2. liste_1 = [4, 7, 9, 1]
  3. liste_2 = ["rouge", "vert", "bleu", "jaune"]
  4.  
  5. # zip() donne un itérateur et n'est donc pas
  6. # affichable, il faut le transformer en liste
  7. # afin de l'afficher
  8. print(list(zip(liste_1, liste_2)))
  9.  
  10. # parcours des deux listes
  11. for x,y in zip(liste_1,liste_2):
  12.     print(f"{x} -> {y}")
  13.    

On obtient le résultat suivant :

[(4, 'rouge'), (7, 'vert'), (9, 'bleu'), (1, 'jaune')]

4 -> rouge
7 -> vert
9 -> bleu
1 -> jaune

Exercice 11.1

Créez le fichier zip1.py et définir trois listes de même taille (au minimum de taille 4) :

  • une liste d'entiers identifiants
  • une liste de chaînes de caractères noms qui correspond à des noms de personnes
  • une liste de flottants salaires
  • Itérer en utilisant zip et calculer la somme des salaires tout en affichant pour chaque personne son nom, son identifiant et combien elle gagne.

    Par exemple :

    Bob d'identifiant 7 gagne 4000 €
    Alice d'identifiant 4 gagne 6000 €
    ...
    

11.3. La fonction sorted

La fonction sorted en Python est utilisée pour trier un itérable (comme une liste, un tuple ou un objet itérateur) et retourne une nouvelle liste triée, sans modifier l'itérable original. Elle offre une grande flexibilité grâce à ses options pour personnaliser l'ordre de tri.

Cette fonction accepte trois paramètres :

Voici un exemple avec trois listes sur lesquelles on applique la fonction zip, puis la fonction sorted.

  1. identifiants = [4, 2, 4, 2]
  2. noms = ["richer", "richer", "vazquez", "vazquez"]
  3. prenoms = ["sarah", "jean-michel", "karla", "emily"]
  4.  
  5. print(sorted(zip(identifiants, noms, prenoms)))
  6.  
  7. print(sorted(zip(noms,prenoms,identifiants)))
  8.  
  9. print(sorted(zip(prenoms, noms, identifiants)))
  10.  
  11. print(sorted(zip(noms, identifiants, prenoms)))

On obtient le résultat suivant :

[(2, 'richer', 'jean-michel'), (2, 'vazquez', 'emily'), (4, 'richer', 'sarah'), (4, 'vazquez', 'karla')]
[('richer', 'jean-michel', 2), ('richer', 'sarah', 4), ('vazquez', 'emily', 2), ('vazquez', 'karla', 4)]
[('emily', 'vazquez', 2), ('jean-michel', 'richer', 2), ('karla', 'vazquez', 4), ('sarah', 'richer', 4)]
[('richer', 2, 'jean-michel'), ('richer', 4, 'sarah'), ('vazquez', 2, 'emily'), ('vazquez', 4, 'karla')]

On peut également utiliser la clé (key) afin de redéfinir l'ordre de tri en utilisant une fonction lambda :

  1. noms = ["richer", "dupond", "vazquez", "martin"]
  2. prenoms = ["sarah", "jean-michel", "karla", "emily"]
  3.  
  4. # on trie uniquement sur le champ 1 = prenom
  5. # le champ x[0] est le nom
  6. print(sorted(zip(noms, prenoms), key=lambda x: x[1]))
  7.  
  8. # on récupère les noms et prénoms triés grâce à zip(*...)
  9. n_t, p_t = zip(*sorted(zip(noms, prenoms), key=lambda x: x[1]))
  10.  
  11. # affiche la liste des noms correspondant au tri précédent
  12. # sur les prénoms
  13. print(n_t)
  14.  
  15. # affiche la liste des prénoms correspondant au tri précédent
  16. # sur les prénoms
  17. print(p_t)
[('martin', 'emily'), ('dupond', 'jean-michel'), ('vazquez', 'karla'), ('richer', 'sarah')]

('martin', 'dupond', 'vazquez', 'richer')
('emily', 'jean-michel', 'karla', 'sarah')

Dans le code précédent, la fonction zip(*...) permet de récupérer des listes correspondant aux éléments triés.

Voici le même code sans fonction lambda, on doit alors créer une fonction trier_par_prenom :

  1. noms = ["richer", "dupond", "vazquez", "martin"]
  2. prenoms = ["sarah", "jean-michel", "karla", "emily"]
  3.  
  4. # fonction qui retourne le champ sur lequel trier
  5. def trier_par_prenom(element):
  6.     # Retourne le deuxième élément du tuple (le prénom)
  7.     return element[1]  
  8.  
  9. # on trie uniquement sur le champ prenom
  10. print(sorted(zip(noms, prenoms), key=trier_par_prenom))
  11.  
  12. # on récupère les noms et prénoms triés grâce à zip(*...)
  13. n_t, p_t = zip(*sorted(zip(noms, prenoms), key=lambda x: x[1]))
  14.  
  15. # affiche la liste des noms correspondant au tri précédent
  16. # sur les prénoms
  17. print(n_t)
  18.  
  19. # affiche la liste des prénoms correspondant au tri précédent
  20. # sur les prénoms
  21. print(p_t)

11.4. La fonction map

La fonction map consiste à appliquer une fonction à tous les éléments d'une liste.

  1. liste_initiale = [ 2, 4, "to", 7, 3.14]
  2.  
  3. def mul2(x):
  4.     return x*2
  5.  
  6. # utilisation d'une fonction
  7. liste_modifiee = list(map(mul2, liste_initiale))
  8. print(liste_modifiee)
  9.  
  10. # utilisation d'une fonction lambda
  11. liste_modifiee = list(map(lambda x: x*3, liste_initiale))
  12. print(liste_modifiee)

Le résultat est le suivant :

[4, 8, 'toto', 14, 6.28]
[6, 12, 'tototo', 21, 9.42]

11.5. La fonction filter

La fonction filter permet de sélectionner des éléments d'un itérable.

  1. dico = { 0: "noir", 2: "rouge", 3:"bleu", 15:"blanc" }
  2.  
  3. couleurs = dict(filter(lambda x: x[1].startswith("bl"), dico.items()))
  4.  
  5. print(couleurs)

Le code précédent permet de sélectionner les couleurs dont le nom commence par "bl". Le résultat est le suivant :

{3: 'bleu', 15: 'blanc'}

11.6. Exercices

Exercice 11.2

Ecrire un programme exercice_map_filter.py qui analyse une phrase donnée et retourne les informations suivantes :

  1. une liste des longueurs de tous les mots dans la phrase
  2. une liste des mots dont la longueur est supérieure à une valeur spécifiée (par exemple, 4)
  3. une phrase reconstruite où chaque mot est remplacé par sa longueur

Vous devez utiliser map et filter pour accomplir cette tâche.

  1. # Étape 1 : Extraire les longueurs des mots
  2. def extraire_longueurs(phrase):
  3.     mots = phrase.split()
  4.     return list(map(_____))
  5.  
  6. # Étape 2 : Filtrer les mots de longueur minimale
  7. def filtrer_mots(phrase, longueur_min):
  8.     mots = phrase.split()
  9.     return list(filter(_____, mots))
  10.  
  11. # Étape 3 : Remplacer les mots par leur longueur
  12. def remplacer_par_longueurs(phrase):
  13.     mots = phrase.split()
  14.     return " ".join(map(_____))
  15.  
  16. # Exemple de phrase de test
  17. phrase = "Python est un langage de programmation étonnant"
  18. longueur_minimum = 4
  19.  
  20. # [6, 3, 2, 7, 2, 13, 8]
  21. print(extraire_longueurs(phrase))  
  22.  
  23. # ['Python', 'langage', 'programmation', 'étonnant']
  24. print(filtrer_mots(phrase, longueur_minimum))
  25.  
  26. # 6 3 2 7 2 13 8
  27. print(remplacer_par_longueurs(phrase))
  28.  

Exercice 11.3

On désire chercher des mots comme dans le jeu "le mot le plus long" des Chiffre et des Lettres.

Utilisez le dictionnaire du français (french.txt)

Ne garder que les mots de 3 à 9 lettres (utilisez filter).

Affichez combien il existe de mots de 3, 4, ..., 9 lettres, utilisez un dictionnaire pour stocker le nombre d'occurrences .

Attention, le dictionnaire contient des accents et des séparateurs comme dans "abat-vent" ou "ac.". Il est nécessaire de supprimer les accents et éliminer les séparateurs.

Pour supprimer les accents et les séparateurs, on peut utiliser le code suivant :

  1. import unicodedata
  2.  
  3. def sans_accent(mot):
  4.     return ''.join(
  5.         char for char in unicodedata.normalize('NFD', mot)
  6.         if unicodedata.category(char) != 'Mn'
  7.     )
  8.  
  9. mots_avec_accent = [ "économie", "élève", "chaîne" ];
  10.  
  11. mots_sans_accent = [ sans_accent(mot.strip('\n').replace("-","").replace('.','')) for mot in mots_avec_accent ]
  12.  
  13. print(mots_sans_accent)
  14.  

On obtient alors

['economie', 'eleve', 'chaine']

Vous devriez obtenir les occurrences suivantes en fonction de la longueur des mots :

3 => 553
4 => 2221
5 => 7104
6 => 16343
7 => 28999
8 => 42257
9 => 51046

Exercice 11.4

Reprendre l'exercice précédent et permettre à l'utilisateur de saisir un mot (entre 3 et 9 lettres) et de rechercher tous les mots de qui contiennent le même nombre de lettres.

Créez pour cela le dictionnaire dico_mots qui contient pour chaque longueur de mot (entre 3 et 9 lettres) la liste des mots correspondants :

3: ['abu', 'ace' ... ] 
4: ['abat', 'abbe', ... ]
5: ['abaca', 'abats', ... ] 
6: ['abaque', 'abasie', ... ] 
...
9: ['abaissais', 'abaissait', ... ] 

Pour le mot donné par l'utilisateur on génèrera toutes les permutations possibles et on cherchera celles qui sont présentent dans le dictionnaire.

Pour obtenir l'ensemble des permutations des lettres à partir d'un mot, on peut utiliser le code suivant :

  1. from itertools import permutations
  2.  
  3. def calculer_permutations(mot):
  4.     # Génère toutes les permutations sous forme de tuples
  5.     toutes_les_permutations = permutations(mot)
  6.    
  7.     # Transforme les tuples en chaînes
  8.     permutations_en_chaines = [''.join(p) for p in toutes_les_permutations]
  9.    
  10.     return permutations_en_chaines
  11.  
  12.  

Par exemple à partir du mot suivant "aient" vous devriez obtenir :

saisir un mot entre 3 et 9 lettres :aient

permutations = ['aient', 'aietn', 'ainet', 'ainte', 'aiten', 'aitne', 'aeint', 'aeitn', 'aenit', 'aenti', 'aetin', 'aetni', 'aniet', 'anite', 'aneit', 'aneti', 'antie', 'antei', 'atien', 'atine', 'atein', 'ateni', 'atnie', 'atnei', 'iaent', 'iaetn', 'ianet', 'iante', 'iaten', 'iatne', 'ieant', 'ieatn', 'ienat', 'ienta', 'ietan', 'ietna', 'inaet', 'inate', 'ineat', 'ineta', 'intae', 'intea', 'itaen', 'itane', 'itean', 'itena', 'itnae', 'itnea', 'eaint', 'eaitn', 'eanit', 'eanti', 'eatin', 'eatni', 'eiant', 'eiatn', 'einat', 'einta', 'eitan', 'eitna', 'enait', 'enati', 'eniat', 'enita', 'entai', 'entia', 'etain', 'etani', 'etian', 'etina', 'etnai', 'etnia', 'naiet', 'naite', 'naeit', 'naeti', 'natie', 'natei', 'niaet', 'niate', 'nieat', 'nieta', 'nitae', 'nitea', 'neait', 'neati', 'neiat', 'neita', 'netai', 'netia', 'ntaie', 'ntaei', 'ntiae', 'ntiea', 'nteai', 'nteia', 'taien', 'taine', 'taein', 'taeni', 'tanie', 'tanei', 'tiaen', 'tiane', 'tiean', 'tiena', 'tinae', 'tinea', 'teain', 'teani', 'teian', 'teina', 'tenai', 'tenia', 'tnaie', 'tnaei', 'tniae', 'tniea', 'tneai', 'tneia']
=========================
mots dans le dictionnaire
=========================
aient
entai
etain
tenia

Avec "aeinst", on obtient :

=========================
mots dans le dictionnaire
=========================
entais
etains
nastie
niates
tanise
tisane
tenais
tenias
sainte
satine