path = window.location.pathname.replace(".html", ".qmd");
path_modified = (path.includes('en/content')) ? path.replace('en/content', 'content/en') : path
html`${printBadges({fpath: path_modified})}`
Les chapitres suivants seront utiles au cours de ce chapitre:
!pip install pywaffle
!pip install spacy
!pip install plotnine
!pip install great_tables
!pip install wordcloud
Requirement already satisfied: pywaffle in /opt/conda/lib/python3.12/site-packages (1.1.1)
Requirement already satisfied: fontawesomefree in /opt/conda/lib/python3.12/site-packages (from pywaffle) (6.6.0)
Requirement already satisfied: matplotlib in /opt/conda/lib/python3.12/site-packages (from pywaffle) (3.9.2)
Requirement already satisfied: contourpy>=1.0.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (1.3.0)
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (4.54.1)
Requirement already satisfied: kiwisolver>=1.3.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (1.4.7)
Requirement already satisfied: numpy>=1.23 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (1.26.4)
Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (24.1)
Requirement already satisfied: pillow>=8 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (10.4.0)
Requirement already satisfied: pyparsing>=2.3.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (3.1.4)
Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.12/site-packages (from matplotlib->pywaffle) (2.9.0)
Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib->pywaffle) (1.16.0)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable.It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
Requirement already satisfied: spacy in /opt/conda/lib/python3.12/site-packages (3.7.5)
Requirement already satisfied: spacy-legacy<3.1.0,>=3.0.11 in /opt/conda/lib/python3.12/site-packages (from spacy) (3.0.12)
Requirement already satisfied: spacy-loggers<2.0.0,>=1.0.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (1.0.5)
Requirement already satisfied: murmurhash<1.1.0,>=0.28.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (1.0.10)
Requirement already satisfied: cymem<2.1.0,>=2.0.2 in /opt/conda/lib/python3.12/site-packages (from spacy) (2.0.8)
Requirement already satisfied: preshed<3.1.0,>=3.0.2 in /opt/conda/lib/python3.12/site-packages (from spacy) (3.0.9)
Requirement already satisfied: thinc<8.3.0,>=8.2.2 in /opt/conda/lib/python3.12/site-packages (from spacy) (8.2.5)
Requirement already satisfied: wasabi<1.2.0,>=0.9.1 in /opt/conda/lib/python3.12/site-packages (from spacy) (1.1.3)
Requirement already satisfied: srsly<3.0.0,>=2.4.3 in /opt/conda/lib/python3.12/site-packages (from spacy) (2.4.8)
Requirement already satisfied: catalogue<2.1.0,>=2.0.6 in /opt/conda/lib/python3.12/site-packages (from spacy) (2.0.10)
Requirement already satisfied: weasel<0.5.0,>=0.1.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (0.4.1)
Requirement already satisfied: typer<1.0.0,>=0.3.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (0.13.1)
Requirement already satisfied: tqdm<5.0.0,>=4.38.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (4.66.5)
Requirement already satisfied: requests<3.0.0,>=2.13.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (2.32.3)
Requirement already satisfied: pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4 in /opt/conda/lib/python3.12/site-packages (from spacy) (2.9.2)
Requirement already satisfied: jinja2 in /opt/conda/lib/python3.12/site-packages (from spacy) (3.1.4)
Requirement already satisfied: setuptools in /opt/conda/lib/python3.12/site-packages (from spacy) (74.1.2)
Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (24.1)
Requirement already satisfied: langcodes<4.0.0,>=3.2.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (3.5.0)
Requirement already satisfied: numpy>=1.19.0 in /opt/conda/lib/python3.12/site-packages (from spacy) (1.26.4)
Requirement already satisfied: language-data>=1.2 in /opt/conda/lib/python3.12/site-packages (from langcodes<4.0.0,>=3.2.0->spacy) (1.3.0)
Requirement already satisfied: annotated-types>=0.6.0 in /opt/conda/lib/python3.12/site-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy) (0.7.0)
Requirement already satisfied: pydantic-core==2.23.4 in /opt/conda/lib/python3.12/site-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy) (2.23.4)
Requirement already satisfied: typing-extensions>=4.6.1 in /opt/conda/lib/python3.12/site-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy) (4.12.2)
Requirement already satisfied: charset-normalizer<4,>=2 in /opt/conda/lib/python3.12/site-packages (from requests<3.0.0,>=2.13.0->spacy) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.12/site-packages (from requests<3.0.0,>=2.13.0->spacy) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/conda/lib/python3.12/site-packages (from requests<3.0.0,>=2.13.0->spacy) (1.26.20)
Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.12/site-packages (from requests<3.0.0,>=2.13.0->spacy) (2024.8.30)
Requirement already satisfied: blis<0.8.0,>=0.7.8 in /opt/conda/lib/python3.12/site-packages (from thinc<8.3.0,>=8.2.2->spacy) (0.7.11)
Requirement already satisfied: confection<1.0.0,>=0.0.1 in /opt/conda/lib/python3.12/site-packages (from thinc<8.3.0,>=8.2.2->spacy) (0.1.5)
Requirement already satisfied: click>=8.0.0 in /opt/conda/lib/python3.12/site-packages (from typer<1.0.0,>=0.3.0->spacy) (8.1.7)
Requirement already satisfied: shellingham>=1.3.0 in /opt/conda/lib/python3.12/site-packages (from typer<1.0.0,>=0.3.0->spacy) (1.5.4)
Requirement already satisfied: rich>=10.11.0 in /opt/conda/lib/python3.12/site-packages (from typer<1.0.0,>=0.3.0->spacy) (13.9.4)
Requirement already satisfied: cloudpathlib<1.0.0,>=0.7.0 in /opt/conda/lib/python3.12/site-packages (from weasel<0.5.0,>=0.1.0->spacy) (0.20.0)
Requirement already satisfied: smart-open<8.0.0,>=5.2.1 in /opt/conda/lib/python3.12/site-packages (from weasel<0.5.0,>=0.1.0->spacy) (7.0.5)
Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.12/site-packages (from jinja2->spacy) (2.1.5)
Requirement already satisfied: marisa-trie>=1.1.0 in /opt/conda/lib/python3.12/site-packages (from language-data>=1.2->langcodes<4.0.0,>=3.2.0->spacy) (1.2.1)
Requirement already satisfied: markdown-it-py>=2.2.0 in /opt/conda/lib/python3.12/site-packages (from rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy) (3.0.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /opt/conda/lib/python3.12/site-packages (from rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy) (2.18.0)
Requirement already satisfied: wrapt in /opt/conda/lib/python3.12/site-packages (from smart-open<8.0.0,>=5.2.1->weasel<0.5.0,>=0.1.0->spacy) (1.16.0)
Requirement already satisfied: mdurl~=0.1 in /opt/conda/lib/python3.12/site-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy) (0.1.2)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable.It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
Requirement already satisfied: plotnine in /opt/conda/lib/python3.12/site-packages (0.13.6)
Requirement already satisfied: matplotlib>=3.7.0 in /opt/conda/lib/python3.12/site-packages (from plotnine) (3.9.2)
Requirement already satisfied: pandas<3.0.0,>=2.1.0 in /opt/conda/lib/python3.12/site-packages (from plotnine) (2.2.3)
Requirement already satisfied: mizani~=0.11.0 in /opt/conda/lib/python3.12/site-packages (from plotnine) (0.11.4)
Requirement already satisfied: numpy>=1.23.0 in /opt/conda/lib/python3.12/site-packages (from plotnine) (1.26.4)
Requirement already satisfied: scipy>=1.7.0 in /opt/conda/lib/python3.12/site-packages (from plotnine) (1.13.0)
Requirement already satisfied: statsmodels>=0.14.0 in /opt/conda/lib/python3.12/site-packages (from plotnine) (0.14.4)
Requirement already satisfied: contourpy>=1.0.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib>=3.7.0->plotnine) (1.3.0)
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.12/site-packages (from matplotlib>=3.7.0->plotnine) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /opt/conda/lib/python3.12/site-packages (from matplotlib>=3.7.0->plotnine) (4.54.1)
Requirement already satisfied: kiwisolver>=1.3.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib>=3.7.0->plotnine) (1.4.7)
Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.12/site-packages (from matplotlib>=3.7.0->plotnine) (24.1)
Requirement already satisfied: pillow>=8 in /opt/conda/lib/python3.12/site-packages (from matplotlib>=3.7.0->plotnine) (10.4.0)
Requirement already satisfied: pyparsing>=2.3.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib>=3.7.0->plotnine) (3.1.4)
Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.12/site-packages (from matplotlib>=3.7.0->plotnine) (2.9.0)
Requirement already satisfied: pytz>=2020.1 in /opt/conda/lib/python3.12/site-packages (from pandas<3.0.0,>=2.1.0->plotnine) (2024.1)
Requirement already satisfied: tzdata>=2022.7 in /opt/conda/lib/python3.12/site-packages (from pandas<3.0.0,>=2.1.0->plotnine) (2024.2)
Requirement already satisfied: patsy>=0.5.6 in /opt/conda/lib/python3.12/site-packages (from statsmodels>=0.14.0->plotnine) (0.5.6)
Requirement already satisfied: six in /opt/conda/lib/python3.12/site-packages (from patsy>=0.5.6->statsmodels>=0.14.0->plotnine) (1.16.0)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable.It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
Requirement already satisfied: great_tables in /opt/conda/lib/python3.12/site-packages (0.12.0)
Requirement already satisfied: commonmark>=0.9.1 in /opt/conda/lib/python3.12/site-packages (from great_tables) (0.9.1)
Requirement already satisfied: htmltools>=0.4.1 in /opt/conda/lib/python3.12/site-packages (from great_tables) (0.6.0)
Requirement already satisfied: importlib-metadata in /opt/conda/lib/python3.12/site-packages (from great_tables) (8.5.0)
Requirement already satisfied: typing-extensions>=3.10.0.0 in /opt/conda/lib/python3.12/site-packages (from great_tables) (4.12.2)
Requirement already satisfied: numpy>=1.22.4 in /opt/conda/lib/python3.12/site-packages (from great_tables) (1.26.4)
Requirement already satisfied: Babel>=2.13.1 in /opt/conda/lib/python3.12/site-packages (from great_tables) (2.16.0)
Requirement already satisfied: importlib-resources in /opt/conda/lib/python3.12/site-packages (from great_tables) (6.4.5)
Requirement already satisfied: packaging>=20.9 in /opt/conda/lib/python3.12/site-packages (from htmltools>=0.4.1->great_tables) (24.1)
Requirement already satisfied: zipp>=3.20 in /opt/conda/lib/python3.12/site-packages (from importlib-metadata->great_tables) (3.20.2)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable.It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
Requirement already satisfied: wordcloud in /opt/conda/lib/python3.12/site-packages (1.9.3)
Requirement already satisfied: numpy>=1.6.1 in /opt/conda/lib/python3.12/site-packages (from wordcloud) (1.26.4)
Requirement already satisfied: pillow in /opt/conda/lib/python3.12/site-packages (from wordcloud) (10.4.0)
Requirement already satisfied: matplotlib in /opt/conda/lib/python3.12/site-packages (from wordcloud) (3.9.2)
Requirement already satisfied: contourpy>=1.0.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib->wordcloud) (1.3.0)
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.12/site-packages (from matplotlib->wordcloud) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /opt/conda/lib/python3.12/site-packages (from matplotlib->wordcloud) (4.54.1)
Requirement already satisfied: kiwisolver>=1.3.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib->wordcloud) (1.4.7)
Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.12/site-packages (from matplotlib->wordcloud) (24.1)
Requirement already satisfied: pyparsing>=2.3.1 in /opt/conda/lib/python3.12/site-packages (from matplotlib->wordcloud) (3.1.4)
Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.12/site-packages (from matplotlib->wordcloud) (2.9.0)
Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib->wordcloud) (1.16.0)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable.It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
1 Introduction
1.1 Rappel
Comme évoqué dans l’introduction de cette partie sur le traitement automatique du langage, l’objectif principal des techniques que nous allons explorer est la représentation synthétique du langage.
Le natural language processing (NLP) ou traitement automatisé du langage (TAL) en Français, vise à extraire de l’information de textes à partir d’une analyse statistique du contenu. Cette définition permet d’inclure de nombreux champs d’applications au sein du NLP (traduction, analyse de sentiment, recommandation, surveillance, etc. ).
Cette approche implique de transformer un texte, qui est une information compréhensible par un humain, en un nombre, information appropriée pour un ordinateur dans le cadre d’une approche statistique ou algorithmique.
Transformer une information textuelle en valeurs numériques propres à une analyse statistique n’est pas une tâche évidente. Les données textuelles sont non structurées puisque l’information cherchée, qui est propre à chaque analyse, est perdue au milieu d’une grande masse d’informations qui doit, de plus, être interprétée dans un certain contexte (un même mot ou une phrase n’ayant pas la même signification selon le contexte).
Si cette tâche n’était pas assez difficile comme ça, on peut ajouter d’autres difficultés propres à l’analyse textuelle car ces données sont :
- bruitées : ortographe, fautes de frappe…
- changeantes : la langue évolue avec de nouveaux mots, sens…
- complexes : structures variables, accords…
- ambiguës : synonymie, polysémie, sens caché…
- propres à chaque langue : il n’existe pas de règle de passage unique entre deux langues
- de grande dimension : des combinaisons infinies de séquences de mots
1.2 Objectif du chapitre
Dans ce chapitre, nous allons nous restreindre aux méthodes fréquentistes dans le paradigme bag of words. Celles-ci sont un peu old school par rapport aux approches plus raffinées que nous évoquerons ultérieurement. Néanmoins, les présenter nous permettra d’évoquer un certain nombre d’enjeux typiques des données textuelles qui restent centraux dans le NLP moderne.
Le principal enseignement à retenir de cette partie est que les données textuelles étant à très haute dimension - le langage étant un objet riche - nous avons besoin de méthodes pour réduire le bruit de nos corpus textuels afin de mieux prendre en compte le signal en leur sein.
Cette partie est une introduction s’appuyant sur quelques ouvrages classiques de la littérature française ou anglo-saxonne. Seront notamment présentées quelques librairies faisant parti de la boite à outil minimale des data scientists: NLTK
et SpaCy
. Les chapitres suivants permettront de se focaliser sur la modélisation du langage.
La librairie SpaCy
NTLK
est la librairie historique d’analyse textuelle en Python
. Elle existe
depuis les années 1990. L’utilisation industrielle du NLP dans le monde
de la data science est néanmoins plus récente et doit beaucoup à la collecte
accrue de données non structurées par les réseaux sociaux. Cela a amené à
un renouvellement du champ du NLP, tant dans le monde de la recherche que dans
sa mise en application dans l’industrie de la donnée.
Le package spaCy
est l’un des packages qui a permis
cette industrialisation des méthodes de NLP. Conçu autour du concept
de pipelines de données, il est beaucoup plus pratique à mettre en oeuvre
pour une chaîne de traitement de données textuelles mettant en oeuvre
plusieurs étapes de transformation des données.
1.3 Méthode
L’analyse textuelle vise à transformer le texte en données numériques manipulables. Pour cela il est nécessaire de se fixer une unité sémantique minimale. Cette unité textuelle peut être le mot ou encore une séquence de n mots (un ngram) ou encore une chaîne de caractères (e.g. la ponctuation peut être signifiante). On parle de token.
On peut ensuite utiliser diverses techniques (clustering, classification supervisée) suivant l’objectif poursuivi pour exploiter l’information transformée. Mais les étapes de nettoyage de texte sont indispensables. Sinon un algorithme sera incapable de détecter une information pertinente dans l’infini des possibles.
2 Bases d’exemple
2.1 Le Comte de Monte Cristo
La base d’exemple est le Comte de Monte Cristo d’Alexandre Dumas. Il est disponible gratuitement sur le site http://www.gutenberg.org (Project Gutemberg) comme des milliers d’autres livres du domaine public.
La manière la plus simple de le récupérer
est de télécharger avec le package request
le fichier texte et le retravailler
légèrement pour ne conserver que le corpus du livre :
from urllib import request
= "https://www.gutenberg.org/files/17989/17989-0.txt"
url = request.urlopen(url)
response = response.read().decode("utf8")
raw
= raw.split(
dumas "*** START OF THE PROJECT GUTENBERG EBOOK LE COMTE DE MONTE-CRISTO, TOME I ***"
1].split(
)["*** END OF THE PROJECT GUTENBERG EBOOK LE COMTE DE MONTE-CRISTO, TOME I ***"
)[0
1
]
import re
def clean_text(text):
= text.lower() # mettre les mots en minuscule
text = " ".join(text.split())
text return text
= clean_text(dumas)
dumas
10000:10500] dumas[
- 1
- On extrait de manière un petit peu simpliste le contenu de l’ouvrage
" mes yeux. --vous avez donc vu l'empereur aussi? --il est entré chez le maréchal pendant que j'y étais. --et vous lui avez parlé? --c'est-à-dire que c'est lui qui m'a parlé, monsieur, dit dantès en souriant. --et que vous a-t-il dit? --il m'a fait des questions sur le bâtiment, sur l'époque de son départ pour marseille, sur la route qu'il avait suivie et sur la cargaison qu'il portait. je crois que s'il eût été vide, et que j'en eusse été le maître, son intention eût été de l'acheter; mais je lu"
2.2 Le corpus anglo-saxon
Nous allons utiliser une base anglo-saxonne présentant trois auteurs de la littérature fantastique:
- Edgar Allan Poe, (EAP) ;
- HP Lovecraft (HPL) ;
- Mary Wollstonecraft Shelley (MWS).
Les données sont disponibles sur un CSV mis à disposition sur Github
. L’URL pour les récupérer directement est
https://github.com/GU4243-ADS/spring2018-project1-ginnyqg/raw/master/data/spooky.csv.
Le fait d’avoir un corpus confrontant plusieurs auteurs nous permettra de comprendre la manière dont les nettoyages de données textuelles favorisent les analyses comparatives.
Nous pouvons utiliser le code suivant pour lire et préparer ces données:
import pandas as pd
= "https://github.com/GU4243-ADS/spring2018-project1-ginnyqg/raw/master/data/spooky.csv"
url # 1. Import des données
= pd.read_csv(url, encoding="latin-1")
horror # 2. Majuscules aux noms des colonnes
= horror.columns.str.capitalize()
horror.columns # 3. Retirer le prefixe id
"ID"] = horror["Id"].str.replace("id", "")
horror[= horror.set_index("Id") horror
Le jeu de données met ainsi en regard un auteur avec une phrase qu’il a écrite :
horror.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. Si on utilise ultérieurement ce corpus pour de la modélisation, il sera nécessaire de tenir compte de ce déséquilibre.
"Author").plot(kind="barh")) (horror.value_counts(
3 Premières analyses de fréquence
L’approche usuelle en statistique, qui consiste à faire une analyse descriptive avant de mettre en oeuvre une modélisation, s’applique également à l’analyse de données textuelles. La fouille de documents textuels implique ainsi, en premier lieu, une analyse statistique afin de déterminer la structure du corpus.
Avant de s’adonner à une analyse systématique du champ lexical de chaque auteur, on va se focaliser dans un premier temps sur un unique mot, le mot fear.
3.1 Exploration ponctuelle
Tip
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 (pie chart) 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 1 : Fréquence d’un mot
Dans un premier temps, nous allons nous concentrer sur notre corpus anglo-saxon (horror
)
- 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”.
Le comptage obtenu devrait être le suivant
wordtoplot | |
---|---|
Author | |
EAP | 70 |
HPL | 160 |
MWS | 211 |
Ceci permet d’obtenir le waffle chart suivant :
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 retrouve la figure suivante:
3.2 Transformation d’un texte en tokens
Dans l’exercice précédent, nous faisions une recherche ponctuelle, qui ne passe pas vraiment à l’échelle. Pour généraliser cette approche, on découpe généralement un corpus en unités sémantiques indépendantes : les tokens.
Tip
Nous allons avoir besoin d’importer un certain nombre de corpus prêts à l’emploi pour utiliser les librairies NTLK
ou SpaCy
. Les instructions ci-dessous permettront de récupérer toutes ces ressources
Pour récupérer tous nos corpus NLTK
prêts à l’emploi, nous faisons
import nltk
"stopwords")
nltk.download("punkt")
nltk.download("genesis")
nltk.download("wordnet")
nltk.download("omw-1.4") nltk.download(
En ce qui concerne SpaCy
, il est nécessaire d’utiliser
la ligne de commande:
!python -m spacy download fr_core_news_sm
!python -m spacy download en_core_web_sm
Plutôt que d’implémenter soi-même un tokenizer inefficace, il est plus approprié d’en appliquer un issu d’une librairie spécialisée. Historiquement, le plus simple était de prendre le tokenizer de NLTK
, la librairie historique de text mining en Python
:
from nltk.tokenize import word_tokenize
10000:10500]) word_tokenize(dumas[
Comme on le voit, cette librairie ne fait pas les choses dans le détail et a quelques incohérences: j'y étais
est séparé en 4 sèmes (['j', "'", 'y', 'étais']
) là où l'acheter
reste un unique sème. NLTK
est en effet une librairie anglo-saxonne et l’algorithme de séparation n’est pas toujours adapté aux règles grammaticales françaises. Il vaut mieux dans ce cas privilégier SpaCy
, la librairie plus récente pour faire ce type de tâche. En plus d’être très bien documentée, elle est mieux adaptée pour les langues non anglo-saxonnes. En l’occurrence, comme le montre l’exemple de la documentation sur les tokenizers, l’algorithme de séparation présente un certain raffinement
Celui-ci peut être appliqué de cette manière:
import spacy
from spacy.tokenizer import Tokenizer
= spacy.load("fr_core_news_sm")
nlp = nlp(dumas[10000:10500])
doc
= []
text_tokenized for token in doc:
+= [token.text]
text_tokenized
", ".join(text_tokenized)
" , mes, yeux, ., --vous, avez, donc, vu, l', empereur, aussi, ?, --il, est, entré, chez, le, maréchal, pendant, que, j', y, étais, ., --et, vous, lui, avez, parlé, ?, --c', est, -, à, -, dire, que, c', est, lui, qui, m', a, parlé, ,, monsieur, ,, dit, dantès, en, souriant, ., --et, que, vous, a, -t, -il, dit, ?, --il, m', a, fait, des, questions, sur, le, bâtiment, ,, sur, l', époque, de, son, départ, pour, marseille, ,, sur, la, route, qu', il, avait, suivie, et, sur, la, cargaison, qu', il, portait, ., je, crois, que, s', il, eût, été, vide, ,, et, que, j', en, eusse, été, le, maître, ,, son, intention, eût, été, de, l', acheter, ;, mais, je, lu"
Comme on peut le voir, il reste encore beaucoup d’éléments polluants notre structuration de corpus, à commencer par la ponctuation. Nous allons néanmoins pouvoir facilement retirer ceux-ci ultérieurement, comme nous le verrons.
3.3 Le nuage de mot: une première analyse généralisée
A ce stade, nous n’avons encore aucune appréhension de la structure de notre corpus : nombre de mots, mots les plus représentés, etc.
Pour se faire une idée de la structure de notre corpus, on peut commencer par compter la distribution des mots dans l’oeuvre de Dumas. Commençons par le début de l’oeuvre, à savoir les 30 000 premiers mots et comptons les mots uniques :
from collections import Counter
= nlp(dumas[:30000])
doc
# Extract tokens, convert to lowercase and filter out punctuation and spaces
= [
tokens for token in doc if not token.is_punct and not token.is_space
token.text.lower()
]
# Count the frequency of each token
= Counter(tokens) token_counts
Nous avons déjà de nombreux mots différents dans le début de l’oeuvre.
len(token_counts)
1401
Nous voyons la haute dimensionnalité du corpus puisque nous avons près de 1500 mots différents sur les 30 000 premiers mots de l’oeuvre de Dumas.
= list(token_counts.items())
token_count_all
# Create a DataFrame from the list of tuples
= pd.DataFrame(token_count_all, columns=["word", "count"]) token_count_all
Si on regarde la distribution de la fréquence des mots, exercice que nous prolongerons ultérieurement en évoquant la loi de Zipf, nous pouvons voir que de nombreux mots sont unique (près de la moitié des mots), que la densité de fréquence descend vite et qu’il faudrait se concentrer un peu plus sur la queue de distribution que ne le permet la figure suivante :
from plotnine import *
+ geom_histogram(aes(x="count")) + scale_x_log10()) (ggplot(token_count_all)
/opt/conda/lib/python3.12/site-packages/plotnine/stats/stat_bin.py:109: PlotnineWarning: 'stat_bin()' using 'bins = 42'. Pick better value with 'binwidth'.
Maintenant, si on regarde les 25 mots les plus fréquents, on peut voir que ceux-ci ne sont pas très intéressants pour analyser le sens de notre document :
# Sort the tokens by frequency in descending order
= token_counts.most_common(25)
sorted_token_counts = pd.DataFrame(sorted_token_counts, columns=["word", "count"]) sorted_token_counts
/tmp/ipykernel_4900/3224890599.py:18: MapWithoutReturnDtypeWarning: Calling `map_elements` without specifying `return_dtype` can lead to unpredictable results. Specify `return_dtype` to silence this warning.
/tmp/ipykernel_4900/3224890599.py:23: MapWithoutReturnDtypeWarning: Calling `map_elements` without specifying `return_dtype` can lead to unpredictable results. Specify `return_dtype` to silence this warning.
Mot | Nombre d'occurrences | |
---|---|---|
de | 176 | |
le | 149 | |
et | 124 | |
à | 116 | |
l' | 102 | |
que | 100 | |
vous | 88 | |
la | 83 | |
il | 81 | |
un | 77 | |
je | 73 | |
en | 70 | |
est | 61 | |
qui | 53 | |
dantès | 53 | |
d' | 50 | |
dit | 50 | |
les | 49 | |
du | 46 | |
a | 41 | |
ne | 37 | |
n' | 37 | |
mon | 37 | |
son | 36 | |
pas | 36 | |
Nombre d'apparitions sur les 30 000 premiers caractères du Comte de Monte Cristo |
Si on représente graphiquement ce classement
(="word", y="count"))
ggplot(sorted_token_counts, aes(x+ geom_point(stat="identity", size=3, color="red")
+ scale_x_discrete(limits=sorted_token_counts.sort_values("count")["word"].tolist())
+ coord_flip()
+ theme_minimal()
+ labs(title="Word Frequency", x="Word", y="Count")
)
Nous nous concentrerons ultérieurement sur ces mots-valises car il sera important d’en tenir compte pour les analyses approfondies de nos documents.
Nous avons pu, par ces décomptes de mots, avoir une première intutition de la nature de notre corpus. Néanmoins, une approche un peu plus visuelle serait pertinente pour avoir un peu plus d’intuitions.
Les nuages de mots (wordclouds) sont des représentations graphiques assez pratiques pour visualiser
les mots les plus fréquents, lorsqu’elles ne sont pas utilisées à tort et à travers.
Les wordclouds sont très simples à implémenter en Python
avec le module Wordcloud
. Quelques paramètres de mise en forme
permettent même d’ajuster la forme du nuage à
une image.
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 du corpushorror
1. - Faire un nuage de mot du corpus
dumas
en utilisant un masque comme celui-ci
Exemple de masque pour la question 2
Les nuages de points obtenus à la question 1 sont les suivants:
Alors que celui obtenu à partir de l’oeuvre de Dumas prend la forme
Si nous n’en étions pas convaincus, ces visualisations montrent clairement qu’il est nécessaire de nettoyer notre texte. Par exemple, en ce qui concerne l’oeuvre du Dumas, le nom du personnage principal, Dantès, est ainsi masqué par un certain nombre d’articles ou mots de liaison qui perturbent l’analyse. En ce qui concerne le corpus anglo-saxon, ce sont des termes similaires, comme “the”, “of”, etc.
Ces mots sont des stop words. 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).
3.4 Aparté: la loi de Zipf
Zipf, dans les années 1930, a remarqué une régularité statistique dans Ulysse, l’oeuvre de Joyce. Le mot le plus fréquent apparaissait \(x\) fois, le deuxième mot le plus fréquent 2 fois moins, le suivant 3 fois moins que le premier, etc. D’un point de vue statistique, cela signifie que 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.
Plus généralement, on peut dériver la loi de Zipf d’une distribution exponentiellement décroissante des fréquences : \(f(n_i) = cn_{i}^{-k}\). Sur le plan empirique, cela signifie qu’on peut utiliser les régressions poissonniennes pour estimer les paramètres de la loi, ce qui prend la spécification suivante
\[ \mathbb{E}\bigg( f(n_i)|n_i \bigg) = \exp(\beta_0 + \beta_1 \log(n_i)) \]
Les modèles linéaires généralisés (GLM) permettent de faire ce type de régression. En Python
, ils sont disponibles par le biais du package statsmodels
, dont les sorties sont très inspirées des logiciels payants spécialisés dans l’économétrie comme Stata
.
= pd.DataFrame(
count_words
{"counter": horror.groupby("Author")
apply(lambda s: " ".join(s["Text"]).split())
.apply(lambda s: Counter(s))
.apply(lambda s: s.most_common())
.
.explode()
}
)"word", "count"]] = pd.DataFrame(
count_words[["counter"].tolist(), index=count_words.index
count_words[
)= count_words.reset_index()
count_words
= count_words.assign(
count_words =lambda x: (x.groupby("Author")["count"].transform("sum")),
tot_mots_auteur=lambda x: x["count"] / x["tot_mots_auteur"],
freq=lambda x: x.groupby("Author")["count"].transform("rank", ascending=False),
rank )
/tmp/ipykernel_4900/4009929367.py:3: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.
Commençons par représenter la relation entre la fréquence et le rang:
from plotnine import *
= (
g
ggplot(count_words)+ geom_point(aes(y="freq", x="rank", color="Author"), alpha=0.4)
+ scale_x_log10()
+ scale_y_log10()
+ theme_minimal()
)
Nous avons bien, graphiquement, une relation log-linéaire entre les deux :
Avec statsmodels
, vérifions plus formellement cette relation:
import statsmodels.api as sm
import numpy as np
= sm.add_constant(np.log(count_words["rank"].astype(float)))
exog
= sm.GLM(
model "freq"].astype(float), exog, family=sm.families.Poisson()
count_words[
).fit()
# Afficher les résultats du modèle
print(model.summary())
Generalized Linear Model Regression Results
==============================================================================
Dep. Variable: freq No. Observations: 69301
Model: GLM Df Residuals: 69299
Model Family: Poisson Df Model: 1
Link Function: Log Scale: 1.0000
Method: IRLS Log-Likelihood: -23.011
Date: Wed, 20 Nov 2024 Deviance: 0.065676
Time: 15:07:34 Pearson chi2: 0.0656
No. Iterations: 5 Pseudo R-squ. (CS): 0.0002431
Covariance Type: nonrobust
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
const -2.4388 1.089 -2.239 0.025 -4.574 -0.303
rank -0.9831 0.189 -5.196 0.000 -1.354 -0.612
==============================================================================
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 mot le plus fréquent qui l’est trois plus que le troisième, etc. On retrouve bien empiriquement cette loi sur ce corpus de trois auteurs.
4 Nettoyage de textes
4.1 Retirer les stop words
Nous l’avons vu, que ce soit en Français ou Anglais, un certain nombre de mots de liaisons, nécessaires sur le plan grammatical mais peu porteur d’information, nous empêchent de saisir les principaux mots vecteurs d’information dans notre corpus.
Il est donc nécessaire de nettoyer notre corpus en retirant ces termes. Ce travail de nettoyage va d’ailleurs au-delà d’un simple retrait de mots. C’est également l’occasion de retirer d’autres sèmes gênants, par exemple la ponctuation.
Commençons par télécharger le corpus de stopwords
import nltk
"stopwords") nltk.download(
[nltk_data] Downloading package stopwords to /github/home/nltk_data...
[nltk_data] Package stopwords is already up-to-date!
True
La liste des stopwords anglais dans NLTK
est la suivante:
from nltk.corpus import stopwords
", ".join(stopwords.words("english"))
"i, me, my, myself, we, our, ours, ourselves, you, you're, you've, you'll, you'd, your, yours, yourself, yourselves, he, him, his, himself, she, she's, her, hers, herself, it, it's, its, itself, they, them, their, theirs, themselves, what, which, who, whom, this, that, that'll, these, those, am, is, are, was, were, be, been, being, have, has, had, having, do, does, did, doing, a, an, the, and, but, if, or, because, as, until, while, of, at, by, for, with, about, against, between, into, through, during, before, after, above, below, to, from, up, down, in, out, on, off, over, under, again, further, then, once, here, there, when, where, why, how, all, any, both, each, few, more, most, other, some, such, no, nor, not, only, own, same, so, than, too, very, s, t, can, will, just, don, don't, should, should've, now, d, ll, m, o, re, ve, y, ain, aren, aren't, couldn, couldn't, didn, didn't, doesn, doesn't, hadn, hadn't, hasn, hasn't, haven, haven't, isn, isn't, ma, mightn, mightn't, mustn, mustn't, needn, needn't, shan, shan't, shouldn, shouldn't, wasn, wasn't, weren, weren't, won, won't, wouldn, wouldn't"
Celle de SpaCy
est plus riche (nous avons déjà téléchargé le corpus en_core_web_sm
en question):
= spacy.load("en_core_web_sm")
nlp_english = nlp_english.Defaults.stop_words
stop_words_english ", ".join(stop_words_english)
"this, is, such, eight, keep, myself, ‘d, their, up, can, besides, seeming, onto, seems, his, nowhere, within, never, otherwise, side, enough, upon, further, its, top, 'll, me, ‘ve, re, sixty, no, third, through, about, quite, afterwards, really, him, whereas, become, whenever, by, beforehand, below, they, twenty, for, regarding, rather, to, give, nine, over, someone, what, ’m, eleven, else, whence, per, one, other, still, anything, every, down, hers, we, whither, does, serious, empty, did, however, ’re, and, either, ourselves, bottom, make, several, during, each, how, many, into, always, mine, against, ‘ll, whatever, except, n’t, too, than, i, five, been, yourselves, six, just, although, but, hereafter, while, latter, indeed, go, themselves, herself, yours, you, an, now, part, here, anywhere, not, hereby, could, hereupon, us, call, those, well, using, put, done, under, yet, unless, throughout, at, ’ll, out, do, ’ve, therein, some, back, ‘re, nor, may, ten, becoming, cannot, thru, latterly, wherein, that, another, thence, her, must, he, have, on, two, 's, should, these, a, any, n't, 'd, whom, has, few, seemed, becomes, twelve, are, used, forty, whole, because, only, among, much, already, before, doing, alone, of, your, various, meanwhile, n‘t, see, ’d, full, next, most, as, though, behind, get, 'm, least, neither, almost, whereby, together, was, thereupon, were, ‘s, whereupon, everywhere, who, everything, take, would, ’s, very, she, perhaps, the, anyway, own, ‘m, 're, ca, everyone, again, show, which, am, whoever, three, in, along, across, since, former, via, both, beside, amongst, anyone, from, none, until, 've, wherever, nevertheless, with, first, thereby, then, mostly, them, yourself, once, sometime, without, please, might, last, whether, if, four, made, also, above, more, all, our, something, amount, where, will, say, itself, after, be, same, there, moreover, it, around, fifty, front, nobody, had, even, whose, being, why, hundred, nothing, anyhow, between, hence, others, towards, ever, when, move, so, herein, toward, somewhere, namely, often, fifteen, or, therefore, thereafter, himself, sometimes, name, beyond, my, whereafter, elsewhere, off, ours, less, due, formerly, thus, noone, somehow, seem, became"
Si cette fois on prend la liste des stopwords français dans NLTK
:
", ".join(stopwords.words("french"))
'au, aux, avec, ce, ces, dans, de, des, du, elle, en, et, eux, il, ils, je, la, le, les, leur, lui, ma, mais, me, même, mes, moi, mon, ne, nos, notre, nous, on, ou, par, pas, pour, qu, que, qui, sa, se, ses, son, sur, ta, te, tes, toi, ton, tu, un, une, vos, votre, vous, c, d, j, l, à, m, n, s, t, y, été, étée, étées, étés, étant, étante, étants, étantes, suis, es, est, sommes, êtes, sont, serai, seras, sera, serons, serez, seront, serais, serait, serions, seriez, seraient, étais, était, étions, étiez, étaient, fus, fut, fûmes, fûtes, furent, sois, soit, soyons, soyez, soient, fusse, fusses, fût, fussions, fussiez, fussent, ayant, ayante, ayantes, ayants, eu, eue, eues, eus, ai, as, avons, avez, ont, aurai, auras, aura, aurons, aurez, auront, aurais, aurait, aurions, auriez, auraient, avais, avait, avions, aviez, avaient, eut, eûmes, eûtes, eurent, aie, aies, ait, ayons, ayez, aient, eusse, eusses, eût, eussions, eussiez, eussent'
On voit que celle-ci n’est pas très riche et mériterait d’être plus complète. Celle de SpaCy
correspond mieux à ce qu’on attend
= nlp.Defaults.stop_words
stop_words_french ", ".join(stop_words_french)
"la, je, certaine, votre, cette, va, malgre, parce, specifiques, moindres, mon, dite, suis, sa, moi-même, suffisante, ou, ouvert, bas, suit, avait, ah, me, quoique, revoila, hi, quant-à-soi, possibles, il, quatre-vingt, m’, soi, devant, ni, c', puis, nos, elle-meme, façon, tout, ta, qu’, dedans, diverse, celle-ci, envers, allons, nous, eux, possible, lui-même, chacun, autre, vas, etant, plus, ai, tels, dit, onze, faisant, pas, seront, pour, exactement, celles-ci, déja, reste, j’, differentes, anterieur, sept, spécifique, te, alors, autres, celui-là, mienne, auront, ouverte, enfin, par, premier, car, de, premièrement, toutes, sera, parle, mille, celles-là, aussi, l’, compris, memes, n', parfois, vers, celle, ne, seul, semblable, devers, egalement, restant, voila, étais, fais, directe, auxquels, chaque, où, directement, ont, sixième, maintenant, prealable, lors, i, deuxième, tres, cinquante, dans, merci, lequel, certain, six, rend, chacune, lui, assez, antérieur, avoir, neanmoins, pendant, mien, seuls, telle, ceci, cela, pense, differente, semble, y, quatorze, effet, gens, serait, houp, ce, proche, ceux-ci, pourquoi, autrement, j', miens, celui-la, quatrième, tant, également, hormis, laisser, ton, ainsi, eu, relativement, fait, une, des, outre, pourrait, sans, m', dès, sien, vingt, seule, parler, durant, da, elles, cinquantième, autrui, ils, abord, ayant, siennes, derrière, celui, cinquième, â, etre, quatre, personne, auquel, auraient, different, est, tente, delà, huitième, nombreux, nombreuses, sinon, ma, parlent, retour, lesquelles, cinq, basee, toute, hui, déjà, malgré, ci, vos, certes, quand, meme, ceux, sont, pourrais, les, on, tellement, hue, semblaient, derriere, d’, dix-huit, a, elle, dessus, sienne, peu, pu, elles-memes, desquels, excepté, mêmes, et, quelconque, ho, sent, bat, cent, première, certaines, septième, miennes, dix, quelles, quarante, nouveau, avais, tenant, aupres, hors, plusieurs, ça, sauf, duquel, cependant, auxquelles, selon, cinquantaine, vous-mêmes, eh, aurait, as, hem, lès, nous-mêmes, un, lui-meme, moi-meme, nôtre, son, revoilà, pouvait, partant, vôtres, comme, sait, hou, seraient, nul, desormais, se, suivre, si, celui-ci, du, désormais, tous, peuvent, faisaient, le, étant, suivants, nôtres, importe, l', près, leurs, spécifiques, procedant, plutot, trois, suffit, lorsque, tenir, tiennes, uns, dont, diverses, aie, attendu, cet, unes, lesquels, ès, peut, treize, ceux-là, quelques, voilà, voici, qui, donc, notamment, dire, via, encore, telles, s', suivante, très, toi, parmi, que, aux, puisque, suffisant, hep, entre, dix-sept, tiens, avec, deux, différent, jusque, chez, seulement, allaient, dejà, moi, avons, etais, jusqu, trente, doivent, font, être, dessous, là, ses, t', hé, ha, ouste, revoici, quelque, vont, quatrièmement, sous, c’, differents, quel, longtemps, celles, vé, ait, desquelles, mais, facon, environ, après, quoi, était, seules, souvent, na, juste, soixante, dits, quiconque, tienne, tes, depuis, vous, préalable, apres, dehors, neuvième, es, anterieure, rendre, tel, néanmoins, notre, t’, celle-là, semblent, combien, avant, huit, seize, elle-même, douze, vôtre, elles-mêmes, ô, avaient, vais, dixième, suivantes, quelle, en, mes, touchant, deuxièmement, afin, ouias, différente, certains, même, précisement, moins, eux-mêmes, au, divers, etc, concernant, troisième, maint, etait, celle-la, toi-même, relative, sur, ces, surtout, etaient, quels, precisement, siens, votres, restent, tien, feront, or, vu, différents, o, specifique, toi-meme, dix-neuf, onzième, tend, suivant, soit, quant, douzième, pres, s’, antérieures, plutôt, quelqu'un, deja, qu', celles-la, antérieure, comment, soi-même, laquelle, peux, leur, anterieures, tu, aura, devra, différentes, ouverts, étaient, soi-meme, doit, permet, n’, stop, debout, à, té, quinze, troisièmement, d', toujours"
Exercice 4 : Nettoyage du texte
- Reprendre l’ouvrage de Dumas et nettoyer celui-ci avec
Spacy
. Refaire le nuage de mots et conclure. - Faire ce même exercice sur le jeu de données anglo-saxon. Idéalement, vous devriez être en mesure d’utiliser la fonctionnalité de pipeline de
SpaCy
.
Ces retraitements commencent à porter leurs fruits puisque des mots ayant plus de sens commencent à se dégager, notamment les noms des personnages (Dantès, Danglart, etc.):
4.2 Racinisation et lemmatisation
Pour aller plus loin dans l’harmonisation d’un texte, il est possible de mettre en place des classes d’équivalence entre des mots. Par exemple, quand on désire faire une analyse fréquentiste, on peut être intéressé par considérer que “cheval” et “chevaux” sont équivalents. Selon les cas, différentes formes d’un même mot (pluriel, singulier, conjugaison) pourront être considérées comme équivalentes et seront remplacées par une même forme dite canonique.
Il existe deux approches dans le domaine :
- la lemmatisation qui requiert la connaissance des statuts grammaticaux (exemple : “chevaux” devient “cheval”) ;
- la racinisation (stemming) plus fruste mais plus rapide, notamment en présence de fautes d’orthographes. Dans ce cas, “chevaux” peut devenir “chev” mais être ainsi confondu avec “chevet” ou “cheveux”.
Cette approche a l’avantage de réduire la taille du vocabulaire à maîtriser pour l’ordinateur et le modélisateur. Il existe plusieurs algorithmes de stemming, notamment le Porter Stemming Algorithm ou le Snowball Stemming Algorithm.
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 :
import nltk
"wordnet")
nltk.download("omw-1.4") nltk.download(
Prenons cette chaine de caractère,
"naples. comme d'habitude, un pilote côtier partit aussitôt du port, rasa le château d'if, et alla aborder le navire entre le cap de morgion et l'île de rion. aussitôt, co"
La version racinisée est la suivante:
"napl,.,comm,d'habitud,,,un,pilot,côti,part,aussitôt,du,port,,,ras,le,château,d'if,,,et,alla,abord,le,navir,entre,le,cap,de,morgion,et,l'îl,de,rion,.,aussitôt,,,co"
A ce niveau, les mots commencent à être moins intelligibles par un humain mais peuvent rester intelligible pour la machine. Ce choix n’est néanmoins pas neutre et sa pertinence dépend du cas d’usage.
Les lemmatiseurs permettront des harmonisations plus subtiles. Ils s’appuient sur des bases de connaissance, par exemple WordNet, 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 WordNetLemmatizer
= WordNetLemmatizer()
lemm
for word in ["women", "daughters", "leaves"]:
print(f"The lemmatized form of {word} is: {lemm.lemmatize(word)}")
The lemmatized form of women is: woman
The lemmatized form of daughters is: daughter
The lemmatized form of leaves is: leaf
Exercice 5 : Lemmatisation avec nltk
Sur le modèle précédent, utiliser un WordNetLemmatizer
sur le corpus dumas[1030:1200]
et observer le résultat.
La version lemmatisée de ce petit morceau de l’oeuvre de Dumas est la suivante:
"naples, ., comme, d'habitude, ,, un, pilote, côtier, partit, aussitôt, du, port, ,, rasa, le, château, d'if, ,, et, alla, aborder, le, navire, entre, le, cap, de, morgion, et, l'île, de, rion, ., aussitôt, ,, co"
4.3 Limite
Dans les approches fréquentistes, où on recherche la proximité entre des textes par la co-occurrence de termes, cette question de la création de classes d’équivalence est fondamentale. Les mots sont identiques ou différents, il n’y a pas de différence subtile entre eux. Par exemple, on devra soit déclarer que “python” et “pythons” sont équivalents, soient qu’ils sont différents, sans différence de degré entre “pythons”, “anaconda” ou “table” par rapport à “python”. Les approches modernes, non plus exclusivement basées sur des fréquences d’apparition, permettront d’introduire de la subtilité dans la synthétisation de l’information présente dans les données textuelles.
Informations additionnelles
environment files have been tested on.
Latest built version: 2024-11-20
Python version used:
'3.12.6 | packaged by conda-forge | (main, Sep 30 2024, 18:08:52) [GCC 13.3.0]'
Package | Version |
---|---|
affine | 2.4.0 |
aiobotocore | 2.15.1 |
aiohappyeyeballs | 2.4.3 |
aiohttp | 3.10.8 |
aioitertools | 0.12.0 |
aiosignal | 1.3.1 |
alembic | 1.13.3 |
altair | 5.4.1 |
aniso8601 | 9.0.1 |
annotated-types | 0.7.0 |
appdirs | 1.4.4 |
archspec | 0.2.3 |
asttokens | 2.4.1 |
attrs | 24.2.0 |
babel | 2.16.0 |
bcrypt | 4.2.0 |
beautifulsoup4 | 4.12.3 |
black | 24.8.0 |
blinker | 1.8.2 |
blis | 0.7.11 |
bokeh | 3.5.2 |
boltons | 24.0.0 |
boto3 | 1.35.23 |
botocore | 1.35.23 |
branca | 0.7.2 |
Brotli | 1.1.0 |
bs4 | 0.0.2 |
cachetools | 5.5.0 |
cartiflette | 0.0.2 |
Cartopy | 0.24.1 |
catalogue | 2.0.10 |
cattrs | 24.1.2 |
certifi | 2024.8.30 |
cffi | 1.17.1 |
charset-normalizer | 3.3.2 |
chromedriver-autoinstaller | 0.6.4 |
click | 8.1.7 |
click-plugins | 1.1.1 |
cligj | 0.7.2 |
cloudpathlib | 0.20.0 |
cloudpickle | 3.0.0 |
colorama | 0.4.6 |
comm | 0.2.2 |
commonmark | 0.9.1 |
conda | 24.9.1 |
conda-libmamba-solver | 24.7.0 |
conda-package-handling | 2.3.0 |
conda_package_streaming | 0.10.0 |
confection | 0.1.5 |
contextily | 1.6.2 |
contourpy | 1.3.0 |
cryptography | 43.0.1 |
cycler | 0.12.1 |
cymem | 2.0.8 |
cytoolz | 1.0.0 |
dask | 2024.9.1 |
dask-expr | 1.1.15 |
databricks-sdk | 0.33.0 |
debugpy | 1.8.6 |
decorator | 5.1.1 |
Deprecated | 1.2.14 |
diskcache | 5.6.3 |
distributed | 2024.9.1 |
distro | 1.9.0 |
docker | 7.1.0 |
duckdb | 0.10.1 |
en-core-web-sm | 3.7.1 |
entrypoints | 0.4 |
et_xmlfile | 2.0.0 |
exceptiongroup | 1.2.2 |
executing | 2.1.0 |
fastexcel | 0.11.6 |
fastjsonschema | 2.20.0 |
fiona | 1.10.1 |
Flask | 3.0.3 |
folium | 0.17.0 |
fontawesomefree | 6.6.0 |
fonttools | 4.54.1 |
fr-core-news-sm | 3.7.0 |
frozendict | 2.4.4 |
frozenlist | 1.4.1 |
fsspec | 2023.12.2 |
funcy | 2.0 |
gensim | 4.3.2 |
geographiclib | 2.0 |
geopandas | 1.0.1 |
geoplot | 0.5.1 |
geopy | 2.4.1 |
gitdb | 4.0.11 |
GitPython | 3.1.43 |
google-auth | 2.35.0 |
graphene | 3.3 |
graphql-core | 3.2.4 |
graphql-relay | 3.2.0 |
graphviz | 0.20.3 |
great-tables | 0.12.0 |
greenlet | 3.1.1 |
gunicorn | 22.0.0 |
h11 | 0.14.0 |
h2 | 4.1.0 |
hpack | 4.0.0 |
htmltools | 0.6.0 |
hyperframe | 6.0.1 |
idna | 3.10 |
imageio | 2.36.0 |
importlib_metadata | 8.5.0 |
importlib_resources | 6.4.5 |
inflate64 | 1.0.0 |
ipykernel | 6.29.5 |
ipython | 8.28.0 |
itsdangerous | 2.2.0 |
jedi | 0.19.1 |
Jinja2 | 3.1.4 |
jmespath | 1.0.1 |
joblib | 1.4.2 |
jsonpatch | 1.33 |
jsonpointer | 3.0.0 |
jsonschema | 4.23.0 |
jsonschema-specifications | 2024.10.1 |
jupyter-cache | 1.0.0 |
jupyter_client | 8.6.3 |
jupyter_core | 5.7.2 |
kaleido | 0.2.1 |
kiwisolver | 1.4.7 |
langcodes | 3.5.0 |
language_data | 1.3.0 |
lazy_loader | 0.4 |
libmambapy | 1.5.9 |
locket | 1.0.0 |
lxml | 5.3.0 |
lz4 | 4.3.3 |
Mako | 1.3.5 |
mamba | 1.5.9 |
mapclassify | 2.8.1 |
marisa-trie | 1.2.1 |
Markdown | 3.6 |
markdown-it-py | 3.0.0 |
MarkupSafe | 2.1.5 |
matplotlib | 3.9.2 |
matplotlib-inline | 0.1.7 |
mdurl | 0.1.2 |
menuinst | 2.1.2 |
mercantile | 1.2.1 |
mizani | 0.11.4 |
mlflow | 2.16.2 |
mlflow-skinny | 2.16.2 |
msgpack | 1.1.0 |
multidict | 6.1.0 |
multivolumefile | 0.2.3 |
munkres | 1.1.4 |
murmurhash | 1.0.10 |
mypy-extensions | 1.0.0 |
narwhals | 1.14.1 |
nbclient | 0.10.0 |
nbformat | 5.10.4 |
nest_asyncio | 1.6.0 |
networkx | 3.3 |
nltk | 3.9.1 |
numexpr | 2.10.1 |
numpy | 1.26.4 |
opencv-python-headless | 4.10.0.84 |
openpyxl | 3.1.5 |
opentelemetry-api | 1.16.0 |
opentelemetry-sdk | 1.16.0 |
opentelemetry-semantic-conventions | 0.37b0 |
outcome | 1.3.0.post0 |
OWSLib | 0.28.1 |
packaging | 24.1 |
pandas | 2.2.3 |
paramiko | 3.5.0 |
parso | 0.8.4 |
partd | 1.4.2 |
pathspec | 0.12.1 |
patsy | 0.5.6 |
Pebble | 5.0.7 |
pexpect | 4.9.0 |
pickleshare | 0.7.5 |
pillow | 10.4.0 |
pip | 24.2 |
platformdirs | 4.3.6 |
plotly | 5.24.1 |
plotnine | 0.13.6 |
pluggy | 1.5.0 |
polars | 1.8.2 |
preshed | 3.0.9 |
prometheus_client | 0.21.0 |
prometheus_flask_exporter | 0.23.1 |
prompt_toolkit | 3.0.48 |
protobuf | 4.25.3 |
psutil | 6.0.0 |
ptyprocess | 0.7.0 |
pure_eval | 0.2.3 |
py7zr | 0.20.8 |
pyarrow | 17.0.0 |
pyarrow-hotfix | 0.6 |
pyasn1 | 0.6.1 |
pyasn1_modules | 0.4.1 |
pybcj | 1.0.2 |
pycosat | 0.6.6 |
pycparser | 2.22 |
pycryptodomex | 3.21.0 |
pydantic | 2.9.2 |
pydantic_core | 2.23.4 |
Pygments | 2.18.0 |
pyLDAvis | 3.4.1 |
PyNaCl | 1.5.0 |
pynsee | 0.1.8 |
pyogrio | 0.10.0 |
pyOpenSSL | 24.2.1 |
pyparsing | 3.1.4 |
pyppmd | 1.1.0 |
pyproj | 3.7.0 |
pyshp | 2.3.1 |
PySocks | 1.7.1 |
python-dateutil | 2.9.0 |
python-dotenv | 1.0.1 |
python-magic | 0.4.27 |
pytz | 2024.1 |
pyu2f | 0.1.5 |
pywaffle | 1.1.1 |
PyYAML | 6.0.2 |
pyzmq | 26.2.0 |
pyzstd | 0.16.2 |
querystring_parser | 1.2.4 |
rasterio | 1.4.2 |
referencing | 0.35.1 |
regex | 2024.9.11 |
requests | 2.32.3 |
requests-cache | 1.2.1 |
retrying | 1.3.4 |
rich | 13.9.4 |
rpds-py | 0.21.0 |
rsa | 4.9 |
Rtree | 1.3.0 |
ruamel.yaml | 0.18.6 |
ruamel.yaml.clib | 0.2.8 |
s3fs | 2023.12.2 |
s3transfer | 0.10.2 |
scikit-image | 0.24.0 |
scikit-learn | 1.5.2 |
scipy | 1.13.0 |
seaborn | 0.13.2 |
selenium | 4.26.1 |
setuptools | 74.1.2 |
shapely | 2.0.6 |
shellingham | 1.5.4 |
six | 1.16.0 |
smart-open | 7.0.5 |
smmap | 5.0.0 |
sniffio | 1.3.1 |
sortedcontainers | 2.4.0 |
soupsieve | 2.5 |
spacy | 3.7.5 |
spacy-legacy | 3.0.12 |
spacy-loggers | 1.0.5 |
SQLAlchemy | 2.0.35 |
sqlparse | 0.5.1 |
srsly | 2.4.8 |
stack-data | 0.6.2 |
statsmodels | 0.14.4 |
tabulate | 0.9.0 |
tblib | 3.0.0 |
tenacity | 9.0.0 |
texttable | 1.7.0 |
thinc | 8.2.5 |
threadpoolctl | 3.5.0 |
tifffile | 2024.9.20 |
toolz | 1.0.0 |
topojson | 1.9 |
tornado | 6.4.1 |
tqdm | 4.66.5 |
traitlets | 5.14.3 |
trio | 0.27.0 |
trio-websocket | 0.11.1 |
truststore | 0.9.2 |
typer | 0.13.1 |
typing_extensions | 4.12.2 |
tzdata | 2024.2 |
Unidecode | 1.3.8 |
url-normalize | 1.4.3 |
urllib3 | 1.26.20 |
wasabi | 1.1.3 |
wcwidth | 0.2.13 |
weasel | 0.4.1 |
webdriver-manager | 4.0.2 |
websocket-client | 1.8.0 |
Werkzeug | 3.0.4 |
wheel | 0.44.0 |
wordcloud | 1.9.3 |
wrapt | 1.16.0 |
wsproto | 1.2.0 |
xgboost | 2.1.1 |
xlrd | 2.0.1 |
xyzservices | 2024.9.0 |
yarl | 1.13.1 |
yellowbrick | 1.5 |
zict | 3.0.0 |
zipp | 3.20.2 |
zstandard | 0.23.0 |
View file history
SHA | Date | Author | Description |
---|---|---|---|
c641de0 | 2024-08-22 11:37:13 | Lino Galiana | A series of fix for notebooks that were bugging (#545) |
0908656 | 2024-08-20 16:30:39 | Lino Galiana | English sidebar (#542) |
5108922 | 2024-08-08 18:43:37 | Lino Galiana | Improve notebook generation and tests on PR (#536) |
8d23a53 | 2024-07-10 18:45:54 | Julien PRAMIL | Modifs 02_exoclean.qmd (#523) |
7595008 | 2024-07-08 17:24:29 | Julien PRAMIL | Changes into NLP/01_intro.qmd (#517) |
56b6442 | 2024-07-08 15:05:57 | Lino Galiana | Version anglaise du chapitre numpy (#516) |
a3dc832 | 2024-06-24 16:15:19 | Lino Galiana | Improve homepage images (#508) |
e660d76 | 2024-06-19 14:31:09 | linogaliana | improve output NLP 1 |
4f41cf6 | 2024-06-14 15:00:41 | Lino Galiana | Une partie sur les sacs de mots plus cohérente (#501) |
06d003a | 2024-04-23 10:09:22 | Lino Galiana | Continue la restructuration des sous-parties (#492) |
005d89b | 2023-12-20 17:23:04 | Lino Galiana | Finalise l’affichage des statistiques Git (#478) |
3437373 | 2023-12-16 20:11:06 | Lino Galiana | Améliore l’exercice sur le LASSO (#473) |
4cd44f3 | 2023-12-11 17:37:50 | Antoine Palazzolo | Relecture NLP (#474) |
deaafb6 | 2023-12-11 13:44:34 | Thomas Faria | Relecture Thomas partie NLP (#472) |
4c060a1 | 2023-12-01 17:44:17 | Lino Galiana | Update book image location |
1f23de2 | 2023-12-01 17:25:36 | Lino Galiana | Stockage des images sur S3 (#466) |
a1ab3d9 | 2023-11-24 10:57:02 | Lino Galiana | Reprise des chapitres NLP (#459) |
a771183 | 2023-10-09 11:27:45 | Antoine Palazzolo | Relecture TD2 par Antoine (#418) |
154f09e | 2023-09-26 14:59:11 | Antoine Palazzolo | Des typos corrigées par Antoine (#411) |
8082302 | 2023-08-25 17:48:36 | Lino Galiana | Mise à jour des scripts de construction des notebooks (#395) |
3bdf3b0 | 2023-08-25 11:23:02 | Lino Galiana | Simplification de la structure 🤓 (#393) |
f2905a7 | 2023-08-11 17:24:57 | Lino Galiana | Introduction de la partie NLP (#388) |
78ea2cb | 2023-07-20 20:27:31 | Lino Galiana | Change titles levels (#381) |
a9b384e | 2023-07-18 18:07:16 | Lino Galiana | Sépare les notebooks (#373) |
29ff3f5 | 2023-07-07 14:17:53 | linogaliana | description everywhere |
f21a24d | 2023-07-02 10:58:15 | Lino Galiana | Pipeline Quarto & Pages 🚀 (#365) |
164fa68 | 2022-11-30 09:13:45 | Lino Galiana | Travail partie NLP (#328) |
f10815b | 2022-08-25 16:00:03 | Lino Galiana | Notebooks should now look more beautiful (#260) |
494a85a | 2022-08-05 14:49:56 | Lino Galiana | Images featured ✨ (#252) |
d201e3c | 2022-08-03 15:50:34 | Lino Galiana | Pimp la homepage ✨ (#249) |
12965ba | 2022-05-25 15:53:27 | Lino Galiana | :launch: Bascule vers quarto (#226) |
9c71d6e | 2022-03-08 10:34:26 | Lino Galiana | Plus d’éléments sur S3 (#218) |
3299f1d | 2022-01-08 16:50:11 | Lino Galiana | Clean NLP notebooks (#215) |
495599d | 2021-12-19 18:33:05 | Lino Galiana | Des éléments supplémentaires dans la partie NLP (#202) |
4f67528 | 2021-12-12 08:37:21 | Lino Galiana | Improve website appareance (#194) |
2a8809f | 2021-10-27 12:05:34 | Lino Galiana | Simplification des hooks pour gagner en flexibilité et clarté (#166) |
04f8b8f | 2021-09-08 11:55:35 | Lino Galiana | echo = FALSE sur la page tuto NLP |
048e3dd | 2021-09-02 18:36:23 | Lino Galiana | Fix problem with Dumas corpus (#134) |
2e4d586 | 2021-09-02 12:03:39 | Lino Galiana | Simplify badges generation (#130) |
49e2826 | 2021-05-13 18:11:20 | Lino Galiana | Corrige quelques images n’apparaissant pas (#108) |
4cdb759 | 2021-05-12 10:37:23 | Lino Galiana | :sparkles: :star2: Nouveau thème hugo :snake: :fire: (#105) |
48ed9d2 | 2021-05-01 08:58:58 | Lino Galiana | lien mort corrigé |
7f9f97b | 2021-04-30 21:44:04 | Lino Galiana | 🐳 + 🐍 New workflow (docker 🐳) and new dataset for modelization (2020 🇺🇸 elections) (#99) |
d164635 | 2020-12-08 16:22:00 | Lino Galiana | :books: Première partie NLP (#87) |
Notes de bas de page
Pour avoir les mêmes résultats que ci-dessous, vous pouvez fixer l’argument
random_state=21
.↩︎
Citation
BibTeX
@book{galiana2023,
author = {Galiana, Lino},
title = {Python pour la data science},
date = {2023},
url = {https://pythonds.linogaliana.fr/},
doi = {10.5281/zenodo.8229676},
langid = {fr}
}
Veuillez citer ce travail comme suit :
Galiana, Lino. 2023. Python pour la data science. https://doi.org/10.5281/zenodo.8229676.