Premiers exemples

In [1]:
dressing = {"pantalons":3, "pulls":4, "tee-shirts":8}
In [2]:
dressing["pulls"]
Out[2]:
4
In [3]:
vocabulaire = {"navigateur":"browser", "précédent":"back", "suivant":"forward"}
In [4]:
vocabulaire["suivant"]
Out[4]:
'forward'
In [5]:
AlanTuring = {"naissance":(23,6,1912),"décès":(12,6,1954),"lieu naissance":"Londres", "lieu décès":"Wilmslow"}
In [6]:
AlanTuring["décès"]
Out[6]:
(12, 6, 1954)

Définition d'un dictionnaire

Un dictionnaire est une donnée composite qui n'est pas ordonnée.
Il fonctionne par un système de clé:valeur.
Les clés, comme les valeurs, peuvent être de types différents. Un dictionnaire est délimité par des accolades.
Rappel :

  • crochets [ ] -> listes
  • parenthèses ( ) -> tuples
  • accolades { } -> dictionnaires
In [7]:
vocabulaire
Out[7]:
{'navigateur': 'browser', 'précédent': 'back', 'suivant': 'forward'}
In [8]:
type(vocabulaire)
Out[8]:
dict

Il est possible d'obtenir la liste des clés et des valeurs avec la méthode keys() et la méthode values.

In [9]:
dressing.keys()
Out[9]:
dict_keys(['pantalons', 'pulls', 'tee-shirts'])
In [10]:
vocabulaire.values()
Out[10]:
dict_values(['browser', 'back', 'forward'])

Création d'un dictionnaire vide

On crée un dictionnaire vide par l'instruction :

In [11]:
monDico = dict()
In [12]:
type(monDico)
Out[12]:
dict

ou plus simplement de cette manière :

In [13]:
unAutreDico = {}
In [14]:
type(unAutreDico)
Out[14]:
dict

Ajout / Modification d'un élément dans un dictionnaire

Pas besoin d'une méthode append(), il suffit de rajouter une paire clé : valeur

In [15]:
dressing["chaussettes"] = 12
In [16]:
dressing
Out[16]:
{'pantalons': 3, 'pulls': 4, 'tee-shirts': 8, 'chaussettes': 12}

On peut évidemment modifier un dictionnaire existant (ce n'est pas un tuple !)

In [17]:
dressing["chaussettes"] = 11
In [18]:
dressing
Out[18]:
{'pantalons': 3, 'pulls': 4, 'tee-shirts': 8, 'chaussettes': 11}

Suppression d'une valeur

On utilise l'instruction del (déjà rencontrée pour les listes)

In [19]:
del dressing["chaussettes"]
In [20]:
dressing
Out[20]:
{'pantalons': 3, 'pulls': 4, 'tee-shirts': 8}

Exercice :

Créer une fonction achat(habit) qui augmente de 1 le nombre d'habits (pantalon, pull ou tee-shirt) de mon dressing.

In [21]:
def achat(habit):
    '''votre code'''

Utilisation :

In [22]:
print(dressing)
achat("pantalons")
print(dressing)
{'pantalons': 3, 'pulls': 4, 'tee-shirts': 8}
{'pantalons': 4, 'pulls': 4, 'tee-shirts': 8}

Test d'appartenance à un dictionnaire

Le mot in permet de tester l'appartenance d'une clé à un dictionnaire. Un booléen est renvoyé.

In [23]:
"cravates" in dressing
Out[23]:
False
In [24]:
"pulls" in dressing
Out[24]:
True

Utilisation de in pour d'autres types construits (listes, tuples, chaines de caractères...)

In [25]:
voyelles = ("a", "e", "i", "o", "u", "y")
In [26]:
"y" in voyelles
Out[26]:
True
In [27]:
"z" in voyelles
Out[27]:
False
In [28]:
mot = "vacances"
"k" in mot
Out[28]:
False

À vous de jouer: exercices sur les dictionnaires


Exercice 1 : création de dictionnaires

On reprend la liste d'élèves précédente:

In [43]:
élèves = [
    ("Sophie", "Jacquemin", 3, 6, 2003),
    ("Zoé", "Ledoux", 1, 2, 2004),
    ("Adrien", "Millet", 8, 6, 2003),
    ("Gilles", "Hebert", 11, 12, 2003),
    ("Andrée", "Millet", 14, 1, 2005),
    ("Françoise", "Auger", 30, 5, 2003),
    ("Margot", "Dias-Laurent", 30, 6, 2003),
    ("Thimothée", "Lebreton", 2, 10, 2004),
    ("Audrey", "Monnier", 30, 12, 2006),
    ("Élisabeth", "Lebrun", 27, 4, 2003)
]

Écrire une fonction élèves_tuples_nommés(tableau_élèves) prenant pour paramètre un tableau au format précédent, et renvoyant un tableau similaire où chaque tuple a été remplacé par un dictionnaire. Les clés seront "prénom", "nom", "jour", "mois", "année"

In [44]:
def élèves_tuples_nommés(tableau_élèves, liste_clés):
    """Écrire la documentation ici"""
    
     # Écrire votre code ici

N'oubliez pas de tester votre code, en incluant la liste des clés correspondant aux tuples définissant les élèves:

In [ ]:
résultats = élèves_tuples_nommés(élèves)
assert résultats[0] == { "prénom": "Sophie", "nom": "Jacquemin",
                                              "jour": 3, "mois": 6, "année": 2003}
résultats

Assurez-vous que la cellule précédente affiche un tableau contenant les dictionnaires définissant chaque élève.


Exercice 1 bis : création de dictionnaires plus générale

On reprend la liste d'élèves précédente:

In [46]:
élèves = [
    ("Sophie", "Jacquemin", 3, 6, 2003),
    ("Zoé", "Ledoux", 1, 2, 2004),
    ("Adrien", "Millet", 8, 6, 2003),
    ("Gilles", "Hebert", 11, 12, 2003),
    ("Andrée", "Millet", 14, 1, 2005),
    ("Françoise", "Auger", 30, 5, 2003),
    ("Margot", "Dias-Laurent", 30, 6, 2003),
    ("Thimothée", "Lebreton", 2, 10, 2004),
    ("Audrey", "Monnier", 30, 12, 2006),
    ("Élisabeth", "Lebrun", 27, 4, 2003)
]

Écrire une fonction élèves_tuples_nommés(tableau_élèves, liste_clés) prenant pour paramètre un tableau au format précédent et une liste de clés, et renvoyant un tableau similaire où chaque tuple a été remplacé par un tuple nommé, c'est-à-dire un dictionnaire. On utilisera la liste de clés pour créer chaque dictionnaire.

In [47]:
def élèves_tuples_nommés(tableau_élèves, liste_clés):
    """Écrire la documentation ici"""
    
     # Écrire votre code ici

N'oubliez pas de tester votre code, en incluant la liste des clés correspondant aux tuples définissant les élèves:

In [48]:
élèves_tuples_nommés(élèves, ["prénom", "nom", "jour", "mois", "année"])

Assurez-vous que la cellule précédente affiche un tableau contenant les dictionnaires définissant chaque élève.


Exercice 2: décompte du nombre d'occurences

Les dictionnaires facilitent énormément l'écriture de certains algorithmes.

Écrivez une fonction décompte_années(tableau_élèves) prenant pour paramètre le résultat de l'exercice précédente (un tableau contenant des dictionnaires pour chaque élève), et renvoyant un dictionnaire associant à chaque année de naissance le nombre d'élèves nés cette année là. On n'incluera uniquement des années ayant connu au moins une naissance.

Indication: Il va falloir différencier le cas où une année est déjà stockée dans le dictionnaire ou non:

  • Si elle y est déjà, on incrémente le compteur associé de 1;
  • Si elle n'y est pas déjà, on la rajoute dans le dictionnaire, avec une valeur égale à 1.
In [49]:
def décompte_années(tableau_élèves):
    """Écrire la documentation de la fonction ici"""
    
    pass # Écrire votre code ici

On s'assure que votre fonction donne les bons résultats:

In [ ]:
clés = ["prénom", "nom", "jour", "mois", "année"]
élèves_dict = élèves_tuples_nommés(élèves, clés)
résultat = décompte_années(élèves_dict)
assert résultat[2003] == 6
assert résultat[2004] == 2
assert résultat[2005] == 1
assert résultat[2006] == 1
assert len(résultat) == 4

Exercice 3: encore des occurences !

Écrire une fonction occurences(tableau) prenant en paramètre un tableau (de valeurs dont le type n'est pas spécifié) et renvoyant un dictionnaire où les clés sont les valeurs du tableau, et chaque clé est associée aux nombre d'occurences dans le tableau.

In [51]:
def occurences(tableau):
    """Écrire la documentation de la fonction ici"""
    
     # Écrire votre code ici

Quelques tests que votre fonction doit passer sans encombre pour être validée:

In [ ]:
assert occurences([1, 2, 3, 4]) == {1: 1, 2: 1, 3: 1, 4: 1}
assert occurences([1, 2, 2, 3, 3, 3, 4, 4, 4, 4]) == {1: 1, 2: 2, 3: 3, 4: 4}
assert occurences([1, 3, 4, 4, 2, 3, 4, 2, 4, 3]) == {1: 1, 2: 2, 3: 3, 4: 4}
assert occurences("pascal") == {"p": 1, "a": 2, "s": 1, "c": 1, "l": 1}

Exercice 4: comparaison de deux tableaux

On revient ici vers l'exercice consistant à déterminer si deux tableaux sont identiques, mais sans utiliser d'algorithme de tri.

On propose au contraire de créer pour chaque tableau un dictionnaire associant à une valeur donnée du tableau le nombre de fois où elle apparaît dans ce tableau: c'est ce que l'on a fait dans l'exercice précédent !

Il ne reste ensuite qu'à comparer les dictionnaires en s'assurant que toutes les clés présentes dans un des dictionnaires le sont aussi dans l'autre, et avec le même nombre d'occurences.

Attention: il est possible de comparer deux dictionnaires avec la syntaxe dict1 == dict2 qui fait exactement ce que l'on veut. Mais on demande ici de réaliser cette comparaison à la main, en utilisant un algorithme adéquat.

La fonction doit renvoyer True si les tableaux contiennent les mêmes éléments (avec les mêmes effectifs), False sinon. Les dictionnaires cités ci-dessus servent à déterminer l'égalité, mais ne doivent pas être visibles en dehors de la fonction: idéalement, l'utilisateur de la fonction ne devrait avoir aucune idée de l'algorithme utilisé, ce qui permet notamment de changer d'algorithme de manière totalement transparente sans qu'il y ait la moindre incidence sur le reste du code. D'où l'importance d'avoir un cahier des charges précis pour une fonction: valeurs attendues, valeurs renvoyées, contraintes diverses sur ces valeurs.

In [53]:
def tableaux_égaux(t1, t2):
    """Écrire la documentation de la fonction ici"""
    
     # Écrire votre code ici
In [ ]:
# N'oubliez pas de tester votre fonction
t1 = [1, 1, 2, 1, 0, 1, 0, 0]
t2 = [2, 0, 0, 1, 1, 1, 0, 1]
t3 = [0, 0, 1, 1, 1, 1, 2]

assert tableaux_égaux(t1, t2) == True
assert tableaux_égaux(t1, t3) == False

Exercice 5: Fréquences des lettres dans un texte

Le dernier test de l'exercice précédent montre que votre fonction occurence fonctionne en particulier aussi sur les chaînes de caractères: en effet, celles-ci ressemblent beaucoup à des tableaux de caractères (et, d'une certaine manière, peuvent souvent être considérées comme telles). En particulier, on peut accéder au $n$-ième caractère d'une chaîne S par la syntaxe S[n], comme s'il s'agissait d'un tableau. De ce fait, la fonction occurence ne fait aucune différence entre un tableau et une chaîne de caractères.

Remarque importante: À la différence des tableaux cependant, les chaînes de caractères sont immuables: il n'est pas possible d'en modifier le contenu une fois la chaîne créée. Cela présente des avantages pour l'implémentation du langage python: celle-ci peut optimiser le stockage des chaînes de caractères en mémoire, par exemple.

C'est notamment cet aspect immuable des chaînes de caractères qui fait que l'on peut les utiliser comme clés dans un dictionnaires, contrairement à des tableaux.

But de l'exercice: Dans cet exercice, votre tâche consiste à utiliser le résultat de la fonction occurences sur un texte (stocké dans une chaîne de caractères) et à afficher les fréquences des lettres. Quelques contraintes:

  • On ne calculera les fréquences que pour les lettres de l'alphabet: la somme de ces fréquences doit valoir 1 (cela signifie que l'on ignorera les caractères autre que les lettres de l'alphabet);
  • On ne distinguera pas les lettres majuscules des lettres minuscules: un "A" et un "a" seront comptabilisés comme une seule et même lettre;
  • Dans le même ordre d'idée, on considèrera qu'un "é" est la lettre "e" (on enlève les accents), de même pour tous les autres caractères accentués;
  • Le "ç" est considéré bien entendu comme un "c";
  • Enfin, les fréquences seront arrondies à trois chiffres après la virgule. On peut pour cela utiliser la fonction round:
>>> round(0.123456789, 3)
0.123

Comme il est difficile de saisir les lettres accentuées en majuscule sous windows (contrairement à linux qui fait cela très naturellement), on peut utiliser l'astuce suivante pour disposer de ces caractères:

In [55]:
accents = "éèêëàâäôöîïùûüç"
accents = accents + accents.upper()
accents
Out[55]:
'éèêëàâäôöîïùûüçÉÈÊËÀÂÄÔÖÎÏÙÛÜÇ'

Une astuce géniale pour se faciliter la vie: les dictionnaires permettent dans certains cas de s'épargner de nombreuses lignes de code. Ici, on imagine très bien qu'il y aurait un long enchaînement de if ... elif ... elif... ... ... ... else... afin de traîter ce problème d'accents.

On peut avantageusement remplacer ce code très compliqué à lire par l'usage d'un simple dictionnaire:

In [56]:
traduction_accents = [("éèêë", "e"), ("àâä", "a"), ("ôö", "o"), ("îï", "i"), ("üûù", "u"), ("ç", "c")]
dictionnaire_accents = {}
for lettres, traduction in traduction_accents:
    for l in lettres:
        dictionnaire_accents[l] = traduction
        dictionnaire_accents[l.upper()] = traduction

On obtient avec ces quelques lignes le dictionnaire suivant:

In [57]:
dictionnaire_accents
Out[57]:
{'é': 'e',
 'É': 'e',
 'è': 'e',
 'È': 'e',
 'ê': 'e',
 'Ê': 'e',
 'ë': 'e',
 'Ë': 'e',
 'à': 'a',
 'À': 'a',
 'â': 'a',
 'Â': 'a',
 'ä': 'a',
 'Ä': 'a',
 'ô': 'o',
 'Ô': 'o',
 'ö': 'o',
 'Ö': 'o',
 'î': 'i',
 'Î': 'i',
 'ï': 'i',
 'Ï': 'i',
 'ü': 'u',
 'Ü': 'u',
 'û': 'u',
 'Û': 'u',
 'ù': 'u',
 'Ù': 'u',
 'ç': 'c',
 'Ç': 'c'}

On est très heureux de s'être épargné la saisie de ce dictionnaire à la main. Si on veut connaître la traduction d'une lettre accentuée quelconque, il suffit d'interroger le contenu du dictionnaire:

In [58]:
dictionnaire_accents["é"]
Out[58]:
'e'

Indication: Il y a plein de façons de réaliser la tâche demandée. Une idée relativement simple consiste à récupérer le dictionnaire fourni par occurences, puis à créer un nouveau dictionnaire n'incluant que les lettres minuscules (attention, les lettres majuscules et les éventuels accents/cédilles doivent être rajoutées au décompte de la lettre minuscule correspondante).

On peut partir du principe que dans ce nouveau dictionnaire, si une lettre n'apparaît pas dans le texte alors son effectif sera égal à zéro: autrement dit, toutes les lettres minuscules seront présentes dans le dictionnaire, avec l'effectif correspondant dans le texte.

Le calcul des fréquences est ensuite relativement simple, puisqu'il n'y a plus de clé à ignorer.

In [59]:
def fréquences_lettres(texte):
    """Écrire la documentation de la fonction ici"""

     # Écrire votre code ici

Voici un exemple permettant de tester votre code:

In [60]:
texte = """
Maître Corbeau, sur un arbre perché,
           Tenait en son bec un fromage.
       Maître Renard, par l'odeur alléché,
           Lui tint à peu près ce langage :
       Et bonjour, Monsieur du Corbeau,
    Que vous êtes joli ! que vous me semblez beau !
           Sans mentir, si votre ramage
           Se rapporte à votre plumage,
     Vous êtes le Phénix des hôtes de ces bois.
À ces mots le Corbeau ne se sent pas de joie,
           Et pour montrer sa belle voix,
   Il ouvre un large bec, laisse tomber sa proie.
   Le Renard s'en saisit, et dit : Mon bon Monsieur,
              Apprenez que tout flatteur
     Vit aux dépens de celui qui l'écoute.
   Cette leçon vaut bien un fromage sans doute.
           Le Corbeau honteux et confus
   Jura, mais un peu tard, qu'on ne l'y prendrait plus."""
In [ ]:
assert fréquences_lettres(texte) == {
    'a': 0.076, 'b': 0.029, 'c': 0.031, 'd': 0.025, 'e': 0.172, 'f': 0.008, 'g': 0.013, 'h': 0.01,
     'i': 0.05, 'j': 0.008, 'k': 0.0, 'l': 0.044, 'm': 0.031, 'n': 0.065, 'o': 0.073, 'p': 0.032,
     'q': 0.01, 'r': 0.078, 's': 0.071, 't': 0.069, 'u': 0.078, 'v': 0.017, 'w': 0.0, 'x': 0.008,
     'y': 0.002, 'z': 0.004}

Comparez les résultats obtenus avec les fréquences usuelles des lettres de la langue française (bien connues des joueurs de scrabble par exemple). On peut les trouver sur wikipedia par exemple: https://fr.wikipedia.org/wiki/Fr%C3%A9quence_d%27apparition_des_lettres_en_fran%C3%A7ais


Exercice 6 : Les misérables

On souhaite reprendre l'exercice précédent, mais sur un texte un peu plus long afin que le calcul des fréquences soit plus pertinent (c'est la loi des grands nombres: plus l'effectif total est important, plus les fréquences tendront à converger vers les fréquences théoriques de la langue française).

On se propose ici de calculer la fréquence des lettres sur le texte intégral des Misérables de Victor Hugo. Celui-ci est stocké dans le fichier misérables.txt. Voici la syntaxe permettant de lire les lignes d'un fichier sur le disque dur:

In [62]:
with open("misérables.txt", "r") as fichier:
    misérables = []
    for ligne in fichier:
        misérables.append(ligne)
        
len(misérables)
Out[62]:
62243

La première ligne sert à ouvrir le fichier en lecture seule (paramètre "r"). La variable référençant le fichier ouvert est fichier.

La boucle for sert à parcourir l'ensemble des lignes du fichier. Celles-ci sont stockées dans le tableau misérables.

On constate que le fichier contient plus de 62000 lignes !

Attention: N'essayez surtout pas d'afficher le contenu du tableau dans jupyter lab. Votre environnement n'y survivrait probablement pas, ni votre navigateur web.

In [63]:
from sys import getsizeof
getsizeof(misérables)
Out[63]:
514576

Le tableau occupe environ un demi méga-octet de mémoire, ce qui n'est finalement pas si énorme pour un ordinateur moderne.

But de l'exercice: Adaptez la fonction fréquences_lettres de l'exercice précédent pour qu'elle ne fonctionne pas avec un unique tableau, mais plutôt avec un tableau de lignes (en réalité, c'est plutôt la fonction occurences qui doit être modifiée à cet effet. On pourra par exemple utiliser une nouvelle fonction occurences_lignes à cet effet).

In [64]:
def occurences_lignes(lignes):
    """Écrire la documentation de la fonction ici"""
    
    pass # Écrire votre code ici
In [65]:
def fréquences_lignes(lignes):
    """Écrire la documentation de la fonction ici"""
    
    pass # Écrire votre code ici

Et l'inévitable test de validation:

In [ ]:
assert fréquences_lignes(misérables) == {
    'a': 0.089, 'b': 0.011, 'c': 0.032, 'd': 0.035,
    'e': 0.172, 'f': 0.011, 'g': 0.009, 'h': 0.01,
    'i': 0.075, 'j': 0.005, 'k': 0.0, 'l': 0.06,
    'm': 0.028, 'n': 0.067, 'o': 0.051, 'p': 0.026,
    'q': 0.012, 'r': 0.064, 's': 0.075, 't': 0.077,
    'u': 0.064, 'v': 0.018, 'w': 0.0, 'x': 0.004,
    'y': 0.003, 'z': 0.002}

Exercice 10: Visualisation des résultats

On aimerait représenter graphiquement les résultats de l'exercice précédent. Un diagramme à barres semble parfaitement approprié.

On utilise pour cela la librarie matplotlib. Voici un exemple:

In [68]:
films = ["Annie Hall", "Ben-Hur", "Casablanca", "Gandhi", "West Side Story"]
nbr_oscars = [5, 11, 3, 8, 19]

from matplotlib import pyplot as plt
plt.bar(range(len(films)), nbr_oscars)
plt.title("Quelques films classiques")
plt.ylabel("Nombre de récompenses")
plt.xticks(range(len(films)), films)
plt.show()

# Évaluer DEUX fois cette cellule si le diagramme ne s'affiche pas dans jupyterlab

But de l'exercice: Représenter graphiquement le diagramme des fréquences de lettres dans les Misérables de Victor Hugo. Pensez à inclure une légende et un titre adaptés.

In [ ]: