Textométrie

Tables lexicales, co-occurrences et corrélations, spécificités

Lise Vaudor

ISIG, UMR 5600 EVS

2024-10-01

Manipulation de tableaux

Avant de réaliser quelques graphiques pour montrer le contenu lexical de nos textes il faudra la plupart du temps quelques calculs basiques sur nos données (par exemple agréger les données pour obtenir les fréquences d’occurrence des termes).

Pour réaliser ces quelques transformations on peut généralement utiliser les fonctions de dplyr.

Corpus wp_flood_words

Dans la suite de cet ouvrage, nous considérerons un nouveau vrai corpus de textes correspondant à l’ensemble des communiqués de presse du Ministère de la Transition écologique et de la Cohésion des territoires mis en ligne ici.

Considérons les tables suivantes, qui résultent d’un travail de récupération (par web scraping), nettoyage, tokenisation, lemmatisation de ce corpus :

Contenu lexical

Ici, on repart du tableau wp_flood_words.

On pourrait être amené à

  • calculer la fréquence des mots
  • calculer la fréquence des mots pour différentes parties du corpus
  • ne garder que les \(n\) mots les plus fréquents
  • etc.

Fréquences d’occurrence

Le descripteur le plus basique que l’on puisse fournir concernant les lemmes est leur fréquence. On la calcule très simplement à l’aide des fonctions de dplyr:

freq_lemmes <- wp_flood_words %>%
  group_by(lemma) %>% 
  summarise(freq=n()) %>% 
  arrange(desc(freq)) %>% 
  na.omit()
head(freq_lemmes)
lemma freq
person 8309
water 8218
river 7272
area 6834
rain 5952
damage 5372

Bon. La version ci-dessus est la “version longue”, faite pour remobiliser les verbes de base de dplyr. Full disclosure: il existe une version raccourcie:

freq_lemmes=wp_flood_words %>% 
  count(lemma, sort=TRUE)
head(freq_lemmes)
lemma n
person 8309
water 8218
river 7272
area 6834
rain 5952
damage 5372

Table lexicale

Si l’on veut caractériser les fréquences de termes non pas dans l’absolu (dans le corpus entier) mais selon une partition du corpus on peut calculer la table lexicale:

tib_lex=table(wp_flood_words$lemma,wp_flood_words$flood)
dim(tib_lex)
[1] 9221  285
tib_lex[1:5,1:5]
             
              wd:Q100293305 wd:Q10310765 wd:Q1033970
  aba                     0            0           0
  abandon                 0            0           0
  abandonment             0            1           0
  abate                   0            0           0
  abbe                    0            0           0
             
              wd:Q103821287 wd:Q10436112
  aba                     0            0
  abandon                 0            0
  abandonment             0            0
  abate                   0            0
  abbe                    0            0

Ici la table lexicale porte sur l’ensemble des lemmes elle a donc pour dimension 8453 lignes (le nombre de lemme distincts) x 4 colonnes (le nombre de ministres).

Table lexicale “longue”

La table lexicale ci-dessus permet ainsi de visualiser les effectifs croisés lemma * événement d’inondation. Cependant, pour toute analyse ultérieure de ces effectifs mieux vaut s’en tenir à une mise en forme “tidy” longue:

tib_lex_long=wp_flood_words %>% 
  group_by(lemma,flood,flood_label) %>% 
  summarise(n=n())
head(tib_lex_long)
lemma flood flood_label n
aba wd:Q13765056 2013 China flood 4
aba wd:Q96784050 2020 China floods 1
abandon wd:Q107596384 2021 Henan floods 2
abandon wd:Q108933919 2002 La Paz floods 2
abandon wd:Q113330937 July 2022 United States floods 1
abandon wd:Q113482445 2022 Central Korean floods 1

Co-occurrences, corrélations

Si l’on s’intéresse non plus seulement à la fréquence individuelle des termes mais qu’on cherche à caractériser les termes qui sont souvent employés ensemble (au sein d’un même document par exemple) on peut utiliser les fonctions du package widyr.

Considérons une paire de mots: motA et motB. - mot:T (pour TRUE) correspond à l’apparition du mot - mot:F (pour FALSE) correspond à la non-apparition du mot - \(N_{A=x,B=y}\) correspond à un effectif croisé des cas où motA:x (T ou F) et motB:y (T ou F)

motA:T motA:F Total
motB:T \(N_{TT}\) \(N_{TF}\) \(N_{T.}\)
motB:F \(N_{FT}\) \(N_{FF}\) \(N_{F.}\)
Total \(N_{.T}\) \(N_{.F}\) N

La fréquence de cooccurrence d’une paire mots correspond ainsi au nombre de fois où ces deux mots apparaissent dans une même entité (ci-dessous, le “doc”). En se référant au tableau ci-dessus, il s’agirait de \(N_{TT}\)

Co-occurrences

Pour calculer les cooccurences et corrélations (un processus assez calculatoire) je vais d’abord filtrer tib_lemmes pour ne garder que les mots ayant une fréquence d’occurrence haute…

tib_lemmes_light <- wp_flood_words %>%
  group_by(lemma) %>% 
  mutate(n=n()) %>% 
  filter(n>2000) %>% 
  ungroup()

On utilise le package widyr pour calculer les effectifs croisés:

mots_cooc <- tib_lemmes_light %>% 
  widyr::pairwise_count(lemma,feature=flood,sort=TRUE)

head(mots_cooc)
item1 item2 n
cause area 279
water area 279
area cause 279
area water 279
water cause 276
cause water 276

Corrélation

La corrélation, quant à elle, correspond à :

\[Cor=\frac{N_{TT}N_{FF}-N_{TF}N_{FT}}{\sqrt{N_{T.}N_{F.}N_{.F}N_{.T}}}\]

mots_cors <- tib_lemmes_light %>% 
  widyr::pairwise_cor(lemma,flood,sort=TRUE)
head(mots_cors)
item1 item2 correlation
rain heavy 0.5504098
heavy rain 0.5504098
rain rainfall 0.5422798
rainfall rain 0.5422798
high level 0.4757340
level high 0.4757340

TF-IDF

Le \(TF-IDF\) (pour “term frequency-inverse document frequency”) est une métrique qui reflète l’importance relative d’un mot dans un document.

TF{d,w} est la fréquence (ou nombre d’occurrences) du terme \(w\) dans le document \(d\). Elle mesure l’importance du terme dans le document.

IDF{d,w} est la fréquence inverse de document. Elle mesure l’importance du terme dans le corpus.

Elle se calcule comme suit, où \(D\) est le nombre total de documents et \(\{d_j: w_i \in d_j\}\) est le nombre de documents qui contiennent le terme \(w_i\): \[ IDF_{w}=log\left(\frac{D}{\{d_j: w_i \in d_j\}}\right)\]

Le \(TF-IDF\) se calcule comme le produit \(TF*IDF\). La multiplication par \(IDF\) sert notamment à minimiser la valeur de \(TF-IDF\) pour les termes les plus fréquents, qu’on suppose peu discriminants.

TF-IDF

Le calcul du TF-IDF est proposé dans le package tidytext:

tib_tfidf <- wp_flood_words %>% 
  count(flood,lemma) %>% 
  tidytext::bind_tf_idf(lemma,flood,n) %>% 
  arrange(desc(tf_idf))
head(tib_tfidf, n=30)
flood lemma n tf idf tf_idf
wd:Q13415724 spa 340 0.1268657 3.349904 0.4249878
wd:Q1129089 molasses 373 0.0555804 5.652489 0.3141676
wd:Q3005548 seine 48 0.0341637 4.553877 0.1555773
wd:Q4592745 fork 73 0.0486342 3.167582 0.1540530
wd:Q1337934 glacier 191 0.0537725 2.819276 0.1515996
wd:Q4568382 hunter 21 0.0403846 3.455265 0.1395395
wd:Q20088998 zoo 109 0.0445990 3.013432 0.1343961
wd:Q4592745 grand 73 0.0486342 2.607967 0.1268365
wd:Q4626536 manila 15 0.0248756 4.959342 0.1233667
wd:Q6413525 ash 66 0.0375213 3.254594 0.1221167
wd:Q120690224 underpass 58 0.0396717 3.013432 0.1195479
wd:Q38276364 prefecture 28 0.0459770 2.474435 0.1137671
wd:Q37725207 crore 23 0.0421245 2.607967 0.1098594
wd:Q4613171 eagle 30 0.0277264 3.860730 0.1070443
wd:Q4942250 snake 32 0.0325203 3.254594 0.1058405
wd:Q117705441 fort 43 0.0410697 2.516995 0.1033723
wd:Q21654555 prefecture 27 0.0417311 2.474435 0.1032608
wd:Q6363593 moraine 20 0.0205128 4.959342 0.1017301
wd:Q4592748 fork 23 0.0320781 3.167582 0.1016100
wd:Q120669170 advisory 443 0.0378083 2.561447 0.0968440
wd:Q955641 epic 43 0.0223958 4.266195 0.0955450
wd:Q2712444 var 39 0.0209565 4.553877 0.0954332
wd:Q65041394 pearl 31 0.0252855 3.706579 0.0937226
wd:Q10436112 myth 32 0.0251969 3.706579 0.0933941
wd:Q36687031 leone 64 0.0187904 4.959342 0.0931879
wd:Q3151449 tunnel 58 0.0465490 1.988927 0.0925825
wd:Q1337934 ice 166 0.0467342 1.914820 0.0894876
wd:Q110291626 mina 19 0.0178404 4.959342 0.0884765
wd:Q4736086 ice 37 0.0452876 1.914820 0.0867177
wd:Q105105866 debacle 16 0.0152672 5.652489 0.0862975

Spécificités

Le package textometry comprend une fonction qui permet de calculer des scores de spécificité. Le but de ce score est (comme pour le TF-IDF) d’identifier les termes spécifiques à une partie du corpus.

Le modèle probabiliste associé à cet indice est expliqué [ici] (https://txm.sourceforge.net/doc/manual/0.7.9/fr/manual59.xhtml). On peut expliquer ce modèle à travers l’image de boules et d’urnes (oui oui, comme quand on fait des probabilités au lycée…).

Spécificités

Considérons ainsi que les mots dans des parties d’un texte sont assimilables à des boules dans des urnes. On s’interroge quant à la spécificité d’un mot particulier -boule rose- dans une partie du corpus -urne A-, autrement dit on se demande si le nombre de boules roses dans l’urne A ci-dessous pourrait être due à une distribution au hasard ou si au contraire cette fréquence relativement élevée démontre que les boules roses sont “spécifiques” de cette urne.

Spécificités

Pour calculer les chances d’observer cette fréquence simplement par hasard, on utilise la loi de probabilité hypergéométrique.

Cette loi quantifie en effet la probabilité d’effectuer un tirage sans remise de \(f\) boules roses dans une urne/partie comprenant \(t\) boules en tout, en tirant dans un nombre total de boules de \(T\) boules dont \(F\) boules roses.

Dans l’exemple ci-dessus on a:

  • \(f=3\) le nombre de boules roses dans A
  • \(t=11\) le nombre de boules dans A
  • \(F=4\) le nombre total de boules roses
  • \(T=32\) le nombre total de boules

Spécificités

La probabilité dans ce contexte de tirer 11 boules et d’obtenir 3 boules roses ou plus est de:

#pr(X>=3)=1-pr(X<2)
1-phyper(q=2,m=4,n=32-4,k=11)
[1] 0.1055339

On observe donc une fréquence (3) qui n’est pas complètement improbable (\(pr(X>2)\approx11\)%) sous hypothèse d’une distribution au hasard des boules. En effet, par convention, on tend à considérer un événement comme très improbable si sa probabilité est inférieure à 5% voire 1%.

Spécificités

Généralisation:

La spécificité se calcule ainsi comme suit:

\[Spec=-log10(pr(X \geq f))\] Elle donne donc un ordre de grandeur (ou en l’occurrence un ordre de petitesse) de la probabilité d’observer une fréquence égale ou supérieure du terme dans la partie considérée.

  • \(-log10(0.01)=2\)
  • \(-log10(0.001)=3\)
  • \(-log10(0.00001)=5\)
  • \(-log10(1e-100)=100\)
  • etc.

Autrement dit, plus la fréquence est forte et improbable sous hypothèse de distribution au hasard, plus le score est élevé, et un score \(\geq\) 2 correspond à une probabilité \(\leq\) 1%. C’est ainsi usuellement cette valeur seuil qu’on utilise pour considérer le score de spécificité comme étant significatif.

Spécificités

Les spécificités peuvent évidemment être calculées à la volée pour l’ensemble des termes (et non terme par terme comme on l’a fait ci-dessus à des fins pédagogiques). On utilise pour cela la fonction specificities() du package textometry.

Pour plus de compatibilité avec le tidyverse on peut utiliser la fonction tidy_specificities() qui utilise la fonction du package textometry et reformatte la sortie de cette fonction pour renvoyer un résultat tabulaire conforme à la logique “tidyverse”.

tib_spec <- mixr::tidy_specificities(wp_flood_words,
                                     lemma,
                                     flood)
head(tib_spec)
lemma flood spec n
molasses wd:Q1129089 Inf 373
drill wd:Q1145882 Inf 210
mud wd:Q1145882 Inf 409
dam wd:Q119123254 Inf 959
advisory wd:Q120669170 Inf 443
heavy wd:Q120669170 Inf 993