Nettoyer un texte: des exercices pour découvrir l’approche bag-of-words
NLP
Exercice
Ce chapitre continue de présenter l’approche de nettoyage de données
du NLP en s’appuyant sur le corpus de trois auteurs
anglo-saxons : Mary Shelley, Edgar Allan Poe, H.P. Lovecraft.
Dans cette série d’exercice nous mettons en oeuvre de manière
plus approfondie les différentes méthodes présentées
précédemment.
Cette page approfondit certains aspects présentés dans la
partie introductive. Après avoir travaillé sur le
Comte de Monte Cristo, on va continuer notre exploration de la littérature
avec cette fois des auteurs anglophones :
Le but va être dans un premier temps de regarder dans le détail les termes les plus fréquemment utilisés par les auteurs, de les représenter graphiquement. Il s’agit donc d’une approche basée sur l’analyse de fréquences.
On prendra appui sur l’approche bag of words présentée dans le chapitre précédent1.
Il n’y aura pas de modélisation particulière, ceci est réservé aux chapitres suivants.
Ce chapitre s’inspire de plusieurs ressources disponibles en ligne:
Les chapitres suivants permettront d’introduire aux enjeux de modélisation
de corpus textuels. Dans un premier temps, le modèle LDA permettra d’explorer
le principe des modèles bayésiens à couche cachées pour modéliser les sujets (topics)
présents dans un corpus et segmenter ces topics selon les mots qui les composent.
Le dernier chapitre de la partie visera à
prédire quel texte correspond à quel auteur à partir d’un modèle Word2Vec.
Cela sera un pas supplémentaire dans la formalisation puisqu’il s’agira de
représenter chaque mot d’un texte sous forme d’un vecteur de grande dimension, ce
qui nous permettra de rapprocher les mots entre eux dans un espace complexe.
Cette technique, dite des plongements de mots (Word Embedding),
permet ainsi de transformer une information complexe difficilement quantifiable
comme un mot
en un objet numérique qui peut ainsi être rapproché d’autres par des méthodes
algébriques. Pour découvrir ce concept, ce post de blog
est particulièrement utile. En pratique, la technique des
plongements de mots permet d’obtenir des tableaux comme celui-ci:
La liste des modules à importer est assez longue, la voici :
import numpy as npimport pandas as pdimport seaborn as snsimport matplotlib.pyplot as pltfrom wordcloud import WordCloudimport base64import stringimport reimport nltkfrom collections import Counterfrom time import time# from sklearn.feature_extraction.stop_words import ENGLISH_STOP_WORDS as stopwordsfrom sklearn.metrics import log_lossimport matplotlib.pyplot as plt#!pip install pywafflefrom pywaffle import Wafflefrom nltk.stem import WordNetLemmatizerfrom sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizerfrom sklearn.decomposition import NMF, LatentDirichletAllocationnltk.download('stopwords')nltk.download('punkt')nltk.download('genesis')nltk.download('wordnet')nltk.download('omw-1.4')
[nltk_data] Downloading package stopwords to /github/home/nltk_data...
[nltk_data] Package stopwords is already up-to-date!
[nltk_data] Downloading package punkt to /github/home/nltk_data...
[nltk_data] Package punkt is already up-to-date!
[nltk_data] Downloading package genesis to /github/home/nltk_data...
[nltk_data] Unzipping corpora/genesis.zip.
[nltk_data] Downloading package wordnet to /github/home/nltk_data...
[nltk_data] Downloading package omw-1.4 to /github/home/nltk_data...
True
Données utilisées
Exercice 1 (optionnel): Importer les données spooky
Pour ceux qui ont envie de tester leurs connaissances en Pandas
Ce code introduit une base nommée train dans l’environnement.
Le jeu de données met ainsi en regard un auteur avec une phrase qu’il a écrite :
train.head()
Text
Author
ID
Id
id26305
This process, however, afforded me no means of...
EAP
26305
id17569
It never once occurred to me that the fumbling...
HPL
17569
id11008
In his left hand was a gold snuff box, from wh...
EAP
11008
id27763
How lovely is spring As we looked from Windsor...
MWS
27763
id12958
Finding nothing else, not even gold, the Super...
HPL
12958
On peut se rendre compte que les extraits des 3 auteurs ne sont
pas forcément équilibrés dans le jeu de données.
Il faudra en tenir compte dans la prédiction.
fig = plt.figure()g = sns.barplot(x=['Edgar Allen Poe', 'Mary W. Shelley', 'H.P. Lovecraft'], y=train['Author'].value_counts())
/opt/mamba/lib/python3.9/site-packages/seaborn/_oldcore.py:1498: FutureWarning:
is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead
/opt/mamba/lib/python3.9/site-packages/seaborn/_oldcore.py:1498: FutureWarning:
is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead
/opt/mamba/lib/python3.9/site-packages/seaborn/_oldcore.py:1765: FutureWarning:
unique with argument that is not not a Series, Index, ExtensionArray, or np.ndarray is deprecated and will raise in a future version.
/opt/mamba/lib/python3.9/site-packages/seaborn/_oldcore.py:1498: FutureWarning:
is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead
Note
L’approche bag of words est présentée de
manière plus extensive dans le chapitre précédent.
L’idée est d’étudier la fréquence des mots d’un document et la
surreprésentation des mots par rapport à un document de
référence (appelé corpus).
Cette approche un peu simpliste mais très
efficace : on peut calculer des scores permettant par exemple de faire
de classification automatique de document par thème, de comparer la
similarité de deux documents. Elle est souvent utilisée en première analyse,
et elle reste la référence pour l’analyse de textes mal
structurés (tweets, dialogue tchat, etc.).
Les analyses tf-idf (term frequency-inverse document frequency) ou les
constructions d’indices de similarité cosinus reposent sur ce type d’approche.
Fréquence d’un mot
Avant de s’adonner à une analyse systématique du champ lexical de chaque
auteur, on se focaliser dans un premier temps sur un unique mot, le mot fear.
Note
L’exercice ci-dessous présente une représentation graphique nommée
waffle chart. Il s’agit d’une approche préférable aux
camemberts qui sont des graphiques manipulables car l’oeil humain se laisse
facilement berner par cette représentation graphique qui ne respecte pas
les proportions.
Exercice 2 : Fréquence d'un mot
Compter le nombre de phrases, pour chaque auteur, où apparaît le mot fear.
Utiliser pywaffle pour obtenir les graphiques ci-dessous qui résument
de manière synthétique le nombre d’occurrences du mot “fear” par auteur.
Refaire l’analyse avec le mot “horror”.
A l’issue de la question 1, vous devriez obtenir le tableau
de fréquence suivant :
Text
ID
wordtoplot
Author
EAP
This process, however, afforded me no means of...
2630511008096741351519322166071718908441148621...
70
HPL
It never once occurred to me that the fumbling...
1756912958197641888620836080752790708121117330...
160
MWS
How lovely is spring As we looked from Windsor...
2776322965009121673712799131170076400683052582...
211
Ceci permet d’obtenir le waffle chart suivant :
Figure 2: Répartition du terme fear dans le corpus de nos trois auteurs
On remarque ainsi de manière très intuitive
le déséquilibre de notre jeu de données
lorsqu’on se focalise sur le terme “peur”
où Mary Shelley représente près de 50%
des observations.
Si on reproduit cette analyse avec le terme “horror”, on peut
en conclure que la peur est plus évoquée par Mary Shelley
(sentiment assez naturel face à la créature du docteur Frankenstein) alors
que Lovecraft n’a pas volé sa réputation d’écrivain de l’horreur !
Premier wordcloud
Pour aller plus loin dans l’analyse du champ lexical de chaque auteur,
on peut représenter un wordcloud qui permet d’afficher chaque mot avec une
taille proportionnelle au nombre d’occurrence de celui-ci.
Exercice 3 : Wordcloud
En utilisant la fonction wordCloud, faire trois nuages de mot pour représenter les mots les plus utilisés par chaque auteur.
Calculer les 25 mots plus communs pour chaque auteur et représenter les trois histogrammes des décomptes.
Le wordcloud pour nos différents auteurs est le suivant :
Enfin, si on fait un histogramme des fréquences,
cela donnera :
On voit ici que ce sont des mots communs, comme “the”, “of”, etc. sont très
présents. Mais ils sont peu porteurs d’information, on peut donc les éliminer
avant de faire une analyse syntaxique poussée.
Ceci est une démonstration par l’exemple qu’il vaut mieux nettoyer le texte avant de
l’analyser (sauf si on est intéressé
par la loi de Zipf, cf. exercice suivant).
A noter que l’histogramme produit
par le biais de Matplotlib ou Seaborn est
peu lisible. Il vaut mieux privilégier Plotly
pour faire celui-ci afin d’avoir les mots qui s’affichent en
passant sa souris sur chaque barre.
Aparté : la loi de Zipf
La loi de Zipf
Dans son sens strict, la loi de Zipf prévoit que
dans un texte donné, la fréquence d’occurrence \(f(n_i)\) d’un mot est
liée à son rang \(n_i\) dans l’ordre des fréquences par une loi de la forme
\(f(n_i) = c/n_i\) où \(c\) est une constante. Zipf, dans les années 1930, se basait sur l’oeuvre
de Joyce, Ulysse pour cette affirmation.
Plus généralement, on peut dériver la loi de Zipf d’une distribution exponentielle des fréquences : \(f(n_i) = cn_{i}^{-k}\). Cela permet d’utiliser la famille des modèles linéaires généralisés, notamment les régressions poissonniennes, pour mesurer les paramètres de la loi. Les modèles linéaire traditionnels en log souffrent en effet, dans ce contexte, de biais (la loi de Zipf est un cas particulier d’un modèle gravitaire, où appliquer des OLS est une mauvaise idée, cf. Galiana et al. (2020) pour les limites).
Un modèle exponentiel peut se représenter par un modèle de Poisson ou, si
les données sont très dispersées, par un modèle binomial négatif. Pour
plus d’informations, consulter l’annexe de Galiana et al. (2020).
La technique économétrique associée pour l’estimation est
les modèles linéaires généralisés (GLM) qu’on peut
utiliser en Python via le
package statsmodels2:
Commençons par représenter la relation entre la fréquence et le rang:
g = sns.lmplot(y ="freq", x ="rank", hue ='Author', data = count_words, fit_reg =False)g.set(xscale="log", yscale="log")g
Nous avons bien, graphiquement, une relation log-linéaire entre les deux:
Avec statsmodels, vérifions plus formellement cette relation:
import statsmodels.api as smexog = sm.add_constant(np.log(count_words['rank'].astype(float)))model = sm.GLM(count_words['freq'].astype(float), exog, family = sm.families.Poisson()).fit()# Afficher les résultats du modèleprint(model.summary())
Le coefficient de la régression est presque 1 ce qui suggère bien une relation
quasiment log-linéaire entre le rang et la fréquence d’occurrence d’un mot.
Dit autrement, le mot le plus utilisé l’est deux fois plus que le deuxième
mois le plus fréquent qui l’est trois plus que le troisième, etc.
Nettoyage d’un texte
Les premières étapes dans le nettoyage d’un texte, qu’on a
développé au cours du chapitre précédent, sont :
suppression de la ponctuation ;
suppression des stopwords.
Cela passe par la tokenisation d’un texte, c’est-à-dire la décomposition
de celui-ci en unités lexicales (les tokens).
Ces unités lexicales peuvent être de différentes natures,
selon l’analyse que l’on désire mener.
Ici, on va définir les tokens comme étant les mots utilisés.
Plutôt que de faire soi-même ce travail de nettoyage,
avec des fonctions mal optimisées,
on peut utiliser la librairie nltk comme détaillé précédemment.
Exercice 4 : Nettoyage du texte
Repartir de train, notre jeu de données d’entraînement. Pour rappel, train a la structure suivante :
Tokeniser chaque phrase avec nltk.
Retirer les stopwords avec nltk.
Pour rappel, au début de l’exercice, le DataFrame présente l’aspect suivant :
Text
Author
ID
wordtoplot
Id
id26305
This process, however, afforded me no means of...
EAP
26305
0
id17569
It never once occurred to me that the fumbling...
HPL
17569
0
Après tokenisation, il devrait avoir cet aspect :
ID Author
00001 MWS [Idris, was, well, content, with, this, resolv...
00002 HPL [I, was, faint, even, fainter, than, the, hate...
dtype: object
Après le retrait des stopwords, cela donnera :
Hint
La méthode apply est très pratique ici car nous avons une phrase par ligne. Plutôt que de faire un DataFrame par auteur, ce qui n’est pas une approche très flexible, on peut directement appliquer la tokenisation
sur notre DataFrame grâce à apply, sans le diviser.
Ce petit nettoyage permet d’arriver à un texte plus intéressant en termes d’analyse lexicale. Par exemple, si on reproduit l’analyse précédente… :
Pour aller plus loin dans l’harmonisation d’un texte, il est possible de
mettre en place les classes d’équivalence développées dans la
partie précédente afin de remplacer différentes variations d’un même
mot par une forme canonique :
la racinisation (stemming) assez fruste mais rapide, notamment
en présence de fautes d’orthographe. Dans ce cas, chevaux peut devenir chev
mais être ainsi confondu avec chevet ou cheveux.
Cette méthode est généralement plus simple à mettre en oeuvre, quoique
plus fruste.
la lemmatisation qui requiert la connaissance des statuts
grammaticaux (exemple : chevaux devient cheval).
Elle est mise en oeuvre, comme toujours avec nltk, à travers un
modèle. En l’occurrence, un WordNetLemmatizer (WordNet est une base
lexicographique ouverte). Par exemple, les mots “women”, “daughters”
et “leaves” seront ainsi lemmatisés de la manière suivante :
from nltk.stem import WordNetLemmatizerlemm = WordNetLemmatizer()for word in ["women","daughters", "leaves"]:print("The lemmatized form of %s is: {}".format(lemm.lemmatize(word)) % word)
The lemmatized form of women is: woman
The lemmatized form of daughters is: daughter
The lemmatized form of leaves is: leaf
Note
Pour disposer du corpus nécessaire à la lemmatisation, il faut, la première fois,
télécharger celui-ci grâce aux commandes suivantes :
On va se restreindre au corpus d’Edgar Allan Poe et repartir de la base de données
brute:
Exercice 5 : Lemmatisation avec nltk
Utiliser un WordNetLemmatizer et observer le résultat.
Optionnel: Effectuer la même tâche avec spaCy
Le WordNetLemmatizer donnera le résultat suivant :
This process , however , afforded me no means of ascertaining the dimensions of my dungeon ; as I might make its circuit , and return to the point whence I set out , without being aware of the fact ; so perfectly
---------------------------
This process , however , afforded me no mean of ascertaining the dimension of my dungeon ; a I might make it circuit , and return to the point whence I set out , without being aware of the fact ; so perfectly
TF-IDF: calcul de fréquence
Le calcul tf-idf (term frequency–inverse document frequency)
permet de calculer un score de proximité entre un terme de recherche et un
document (c’est ce que font les moteurs de recherche).
La partie tf calcule une fonction croissante de la fréquence du terme de recherche dans le document à l’étude ;
La partie idf calcule une fonction inversement proportionnelle à la fréquence du terme dans l’ensemble des documents (ou corpus).
Le score total, obtenu en multipliant les deux composantes,
permet ainsi de donner un score d’autant plus élevé que le terme est surréprésenté dans un document
(par rapport à l’ensemble des documents).
Il existe plusieurs fonctions, qui pénalisent plus ou moins les documents longs,
ou qui sont plus ou moins smooth.
Exercice 6 : TF-IDF: calcul de fréquence
Utiliser le vectoriseur TF-IdF de scikit-learn pour transformer notre corpus en une matrice document x terms. Au passage, utiliser l’option stop_words pour ne pas provoquer une inflation de la taille de la matrice. Nommer le modèle tfidf et le jeu entraîné tfs.
Après avoir construit la matrice de documents x terms avec le code suivant, rechercher les lignes où les termes ayant la structure abandon sont non-nuls.
Trouver les 50 extraits où le score TF-IDF est le plus élevé et l’auteur associé. Vous devriez obtenir le classement suivant :
aaem
ab
aback
abaft
abandon
abandoned
abandoning
abandonment
abaout
abased
...
zodiacal
zoilus
zokkar
zone
zones
zopyrus
zorry
zubmizzion
zuro
á¼
0
0.0
0.0
0.0
0.0
0.0
0.000000
0.0
0.0
0.0
0.0
...
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
1
0.0
0.0
0.0
0.0
0.0
0.000000
0.0
0.0
0.0
0.0
...
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
2
0.0
0.0
0.0
0.0
0.0
0.000000
0.0
0.0
0.0
0.0
...
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
3
0.0
0.0
0.0
0.0
0.0
0.000000
0.0
0.0
0.0
0.0
...
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
4
0.0
0.0
0.0
0.0
0.0
0.253506
0.0
0.0
0.0
0.0
...
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
5 rows × 24937 columns
Les lignes où les termes de abandon sont non nuls
sont les suivantes :
['We could not fear we did not.' '"And now I do not fear death.'
'Be of heart and fear nothing.' 'I smiled, for what had I to fear?'
'Indeed I had no fear on her account.'
'I have not the slightest fear for the result.'
'At length, in an abrupt manner she asked, "Where is he?" "O, fear not," she continued, "fear not that I should entertain hope Yet tell me, have you found him?'
'"I fear you are right there," said the Prefect.'
'I went down to open it with a light heart, for what had I now to fear?']
On remarque que les scores les plus élévés sont soient des extraits courts où le mot apparait une seule fois, soit des extraits plus longs où le mot fear apparaît plusieurs fois.
Note
La matrice document x terms est un exemple typique de matrice sparse puisque, dans des corpus volumineux, une grande diversité de vocabulaire peut être trouvée.
Approche contextuelle: les n-grams
Jusqu’à présent, dans l’approche bag of words, l’ordre des mots n’avait pas d’importance.
On considère qu’un texte est une collection de
mots tirés indépendamment, de manière plus ou moins fréquente en fonction de leur probabilité
d’occurrence. Cependant, tirer un mot particulier n’affecte pas les chances de tirer certains mots
ensuite, de manière conditionnelle.
Une manière d’introduire des liens entre les séries de tokens sont les n-grams.
On s’intéresse non seulement aux mots et à leur fréquence, mais aussi aux mots qui suivent. Cette approche est essentielle pour désambiguiser les homonymes. Le calcul de n-grams3 constitue la méthode la plus simple pour tenir compte du contexte.
Pour être en mesure de mener cette analyse, il est nécessaire de télécharger un corpus supplémentaire :
[nltk_data] Downloading package genesis to /github/home/nltk_data...
[nltk_data] Package genesis is already up-to-date!
NLTK offre des methodes pour tenir compte du contexte. Pour ce faire, nous calculons les n-grams, c’est-à-dire l’ensemble des co-occurrences successives de mots n-à-n. En général, on se contente de bi-grams, au mieux de tri-grams:
les modèles de classification, analyse du sentiment, comparaison de documents, etc. qui comparent des n-grams avec n trop grands sont rapidement confrontés au problème de données sparse, cela réduit la capacité prédictive des modèles ;
les performances décroissent très rapidement en fonction de n, et les coûts de stockage des données augmentent rapidement (environ n fois plus élevé que la base de données initiale).
On va, rapidement, regarder dans quel contexte apparaît le mot fear dans
l’oeuvre d’Edgar Allan Poe (EAP). Pour cela, on transforme d’abord
le corpus EAP en tokens NLTK :
['This', 'process,', 'however,', 'afforded', 'me', 'no', 'means', 'of', 'ascertaining', 'the']
<Text: This process, however, afforded me no means of...>
Vous aurez besoin des fonctions BigramCollocationFinder.from_words et BigramAssocMeasures.likelihood_ratio :
Exercice 7 : n-grams et contexte du mot fear
Utiliser la méthode concordance pour afficher le contexte dans lequel apparaît le terme fear.
Sélectionner et afficher les meilleures collocation, par exemple selon le critère du ratio de vraisemblance.
Lorsque deux mots sont fortement associés, cela est parfois dû au fait qu’ils apparaissent rarement. Il est donc parfois nécessaire d’appliquer des filtres, par exemple ignorer les bigrammes qui apparaissent moins de 5 fois dans le corpus.
Refaire la question précédente en utilisant toujours un modèle BigramCollocationFinder suivi de la méthode apply_freq_filter pour ne conserver que les bigrammes présents au moins 5 fois. Puis, au lieu d’utiliser la méthode de maximum de vraisemblance, testez la méthode nltk.collocations.BigramAssocMeasures().jaccard.
Ne s’intéresser qu’aux collocations qui concernent le mot fear
Avec la méthode concordance (question 1),
la liste devrait ressembler à celle-ci:
Exemples d'occurences du terme 'fear' :
Displaying 13 of 13 matches:
d quick unequal spoken apparently in fear as well as in anger. What he said wa
hutters were close fastened, through fear of robbers, and so I knew that he co
to details. I even went so far as to fear that, as I occasioned much trouble,
years of age, was heard to express a fear "that she should never see Marie aga
ich must be entirely remodelled, for fear of serious accident I mean the steel
my arm, and I attended her home. 'I fear that I shall never see Marie again.'
clusion here is absurd. "I very much fear it is so," replied Monsieur Maillard
bt of ultimately seeing the Pole. "I fear you are right there," said the Prefe
er occurred before.' Indeed I had no fear on her account. For a moment there w
erhaps so," said I; "but, Legrand, I fear you are no artist. It is my firm int
raps with a hammer. Be of heart and fear nothing. My daughter, Mademoiselle M
e splendor. I have not the slightest fear for the result. The face was so far
arriers of iron that hemmed me in. I fear you have mesmerized" adding immediat
Même si on peut facilement voir le mot avant et après, cette liste est assez difficile à interpréter car elle recoupe beaucoup d’informations.
La collocation consiste à trouver les bi-grammes qui
apparaissent le plus fréquemment ensemble. Parmi toutes les paires de deux mots observées,
il s’agit de sélectionner, à partir d’un modèle statistique, les “meilleures”.
On obtient donc avec cette méthode (question 2):
"Gad Fly"
'Hum Drum,'
'Rowdy Dow,'
Brevet Brigadier
Barrière du
ugh ugh
Ourang Outang
Chess Player
John A.
A. B.
hu hu
General John
'Oppodeldoc,' whoever
mille, mille,
Brigadier General
Cette liste a un peu plus de sens,
on a des noms de personnages, de lieux mais aussi des termes fréquemment employés ensemble
(Chess Player par exemple).
Galiana, Lino, and Milena Suarez Castillo. 2022. “Fuzzy Matching on Big-Data an Illustration with Scanner Data and Crowd-Sourced Nutritional Data.”
Galiana, Lino, François Sémécurbe, Benjamin Sakarovitch, and Zbigniew Smoreda. 2020. “Residential Segregation, Daytime Segregation and Spatial Frictions: An Analysis from Mobile Phone Data.”
Footnotes
L’approche bag of words est déjà, si on la pousse à ses limites, très intéressante. Elle peut notamment
faciliter la mise en cohérence de différents corpus
par la méthode des appariements flous
(cf. Galiana and Castillo (2022).
Le chapitre sur ElasticSearch présent dans cette partie du cours présente quelques
éléments de ce travail sur les données de l’OpenFoodFacts.↩︎
La littérature sur les modèles gravitaires, présentée dans Galiana et al. (2020),
donne quelques arguments pour privilégier les modèles GLM à des modèles log-linéaires
estimés par moindres carrés ordinaires.↩︎
On parle de bigrams pour les co-occurences de mots deux-à-deux, trigrams pour les co-occurences trois-à-trois, etc.↩︎
Citation
BibTeX citation:
@book{galiana2023,
author = {Galiana, Lino},
title = {Python Pour La Data Science},
date = {2023},
url = {https://pythonds.linogaliana.fr/},
doi = {10.5281/zenodo.8229676},
langid = {en}
}
Note
L’exercice ci-dessous présente une représentation graphique nommée waffle chart. Il s’agit d’une approche préférable aux camemberts qui sont des graphiques manipulables car l’oeil humain se laisse facilement berner par cette représentation graphique qui ne respecte pas les proportions.