library(tidyverse)
library(readtext)
library(quanteda)
library(quanteda.textstats)
library(quanteda.textplots)1 Introduction
Dans ce TP, nous allons explorer les bases de la textométrie ou lexicométrie. Cette discipline, à la croisée de la linguistique, de la statistique et de l’informatique, permet d’analyser des corpus de textes de manière quantitative. L’objectif est de complémenter la lecture (l’analyse qualitative) pour identifier des motifs, des récurrences ou des spécificités qui ne sont pas immédiatement visibles à l’oeil nu dans un corpus.
Nous utiliserons encore une fois quanteda, ainsi que quelque un des ses packages supplémentaires :
1.1 Chargement et préparation du corpus
On charge notre corpus avec readtext(). Pour ce TP, nous travaillons encore une fois sur le corpus des manifestes politiques français :
texts_df = readtext("data/manifesto/*.txt")
texts_dfreadtext object consisting of 9 documents and 0 docvars.
# A data frame: 9 × 2
doc_id text
<chr> <chr>
1 en_marche_2017.txt "\"Emmanuel M\"..."
2 europe_ecologie_les_verts_2017.txt "\"Il y a urg\"..."
3 france_insoumise_2017.txt "\"L'AVENIR E\"..."
4 front_national_2017.txt "\"Au nom du \"..."
5 mouvement_democratique_2017.txt "\"La France \"..."
6 parti_communiste_francais_2017.txt "\"Le peuple \"..."
# ℹ 3 more rows
On crée les deux objets quanteda fondamentaux : - Le corpus : contient les textes bruts et leurs métadonnées. - Les tokens : le texte segmenté en unités.
corpus_obj = corpus(texts_df)
tokens_obj = tokens(corpus_obj)Notez qu’il est possible d’enlever plusieurs types de tokens dans la fonction tokens() avec les options remove_punct, remove_symbols, remove_numbers, remove_url, et remove_separators. On peut également sélectionner ou enlever des tokens choisis de l’objet de type tokens après sa création, à l’aide des fonctions token_select() ou token_remove() (nous le verrons plus tard).
2 Statistiques et indices descriptifs
Le package quanteda.textstats propose plusieurs fonctions permettant d’obtenir divers statistiques et indices portant sur chaque texte. Cela permet de quantifier le “style” d’écriture de chaque document.
2.1 Statistiques globales
Pour obtenir des statistiques globales, on peut utiliser textstat_summary() sur l’objet de type corpus :
textstat_summary(corpus_obj) document chars sents tokens types puncts numbers
1 en_marche_2017.txt 41182 333 6775 1835 930 75
2 europe_ecologie_les_verts_2017.txt 23726 127 3911 1291 493 132
3 france_insoumise_2017.txt 139096 506 21987 4359 2283 342
4 front_national_2017.txt 36170 235 5910 1809 618 66
5 mouvement_democratique_2017.txt 64903 424 10297 2647 1348 57
6 parti_communiste_francais_2017.txt 3072 17 543 276 67 3
7 parti_radical_de_gauche_2017.txt 84406 346 13435 3250 1486 81
8 parti_socialiste_2017.txt 8973 26 1510 620 160 55
9 udi_republicains_2017.txt 31438 238 5262 1539 504 35
symbols urls tags emojis
1 1 0 0 0
2 57 0 0 0
3 8 0 0 0
4 1 0 0 0
5 4 0 0 0
6 0 0 0 0
7 2 0 0 0
8 2 0 0 0
9 2 0 0 0
2.2 Diversité lexicale
La diversité lexicale mesure la “richesse” du vocabulaire. L’indice le plus simple est le TTR (Type-Token Ratio) : le nombre de mots uniques (types) divisé par le nombre total de mots (tokens). Cet indice présente cependant des défauts : il est très sensible à la longueur du texte (plus un texte est long, plus le TTR baisse).
Il existe de nombreux indices qui essaie de corriger ce problème, et que l’on peut obtenir avec textstat_lexdiv() sur l’objet tokens :
textstat_lexdiv(tokens_obj, measure="all") document TTR C R CTTR
1 en_marche_2017.txt 0.3095857 0.8646090 23.51425 16.627088
2 europe_ecologie_les_verts_2017.txt 0.3682255 0.8763529 20.92415 14.795609
3 france_insoumise_2017.txt 0.2173194 0.8453611 30.23318 21.378090
4 front_national_2017.txt 0.3374163 0.8730976 24.38985 17.246230
5 mouvement_democratique_2017.txt 0.2926418 0.8648543 27.58915 19.508475
6 parti_communiste_francais_2017.txt 0.5560254 0.9047033 12.09275 8.550868
7 parti_radical_de_gauche_2017.txt 0.2692567 0.8601397 29.33045 20.739758
8 parti_socialiste_2017.txt 0.4447022 0.8868971 15.99074 11.307161
9 udi_republicains_2017.txt 0.3181529 0.8646273 21.86014 15.457453
U S K I D Vm Maas
1 27.77955 0.8901815 105.6898 8.966826 0.01057081 0.1009079 0.1897306
2 28.37970 0.8948609 130.3033 10.251784 0.01303437 0.1117989 0.1877138
3 27.72117 0.8845843 119.3774 3.942812 0.01193836 0.1084051 0.1899303
4 29.29879 0.8966593 122.7049 9.183431 0.01227284 0.1090626 0.1847460
5 29.21888 0.8942822 114.4410 7.431584 0.01144538 0.1056983 0.1849985
6 28.06878 0.8982128 119.7878 23.933910 0.01200416 0.1014429 0.1887506
7 29.13124 0.8927454 110.3836 6.531493 0.01103929 0.1039694 0.1852766
8 27.51123 0.8942627 147.4415 13.033152 0.01475557 0.1173815 0.1906536
9 27.14013 0.8882211 119.1259 8.395214 0.01191512 0.1070450 0.1919526
MATTR MSTTR lgV0 lgeV0
1 0.7205450 0.7166667 6.472111 14.90259
2 0.6939105 0.6950000 6.384421 14.70067
3 0.7128107 0.7136269 6.783791 15.62026
4 0.7140616 0.7144231 6.658359 15.33144
5 0.7159916 0.7148864 6.802759 15.66393
6 0.6964171 0.7150000 5.680085 13.07888
7 0.7222232 0.7250847 6.870723 15.82042
8 0.6888442 0.6950000 5.973734 13.75503
9 0.7037278 0.7048936 6.322815 14.55882
On trouve par exemple, parmi ces derniers : L’indice R de Guiraud : version normalisée du TTR, qui divise le nombre de types par la racine carrée du nombre de tokens. Le MATTR : Moving-Average Type-Token Ratio, qui calcule le TTR sur des fenêtres mobiles de taille fixe, puis fait la moyenne de ces TTR. Regardez l’aide de la fonction (?textstat_lexdiv) pour comprendre les nombreux autres indices présentés.
2.3 Indices de lisibilité
Ces indices estiment la facilité de lecture d’un texte en se basant sur des critères formels. Ces indices s’obtiennent avec textstat_readability() sur l’objet de type corpus :
textstat_readability(corpus_obj, measure="all") document ARI ARI.simple ARI.NRI Bormuth.MC
1 en_marche_2017.txt 12.29987 65.23874 11.38699 -3.037219
2 europe_ecologie_les_verts_2017.txt 16.77707 74.21938 14.82920 -4.897926
3 france_insoumise_2017.txt 24.25972 89.05313 21.51447 -7.238259
4 front_national_2017.txt 16.45072 73.39402 15.48566 -4.050422
5 mouvement_democratique_2017.txt 15.00788 70.57408 13.98209 -3.763505
6 parti_communiste_francais_2017.txt 17.06000 74.83551 14.78838 -5.221406
7 parti_radical_de_gauche_2017.txt 23.52018 87.43930 21.63892 -6.396699
8 parti_socialiste_2017.txt 30.42420 101.41493 26.27149 -9.756687
9 udi_republicains_2017.txt 14.29691 69.16250 13.35819 -3.530319
Bormuth.GP Coleman Coleman.C2 Coleman.Liau.ECP Coleman.Liau.grade
1 72933678 33.71184 35.32706 34.48173 13.61582
2 302779111 33.83931 32.49773 33.67544 13.83675
3 976694580 28.78451 26.28411 25.73647 16.01205
4 171874141 26.43753 26.92871 25.64689 16.03660
5 138558731 28.61969 29.32466 29.32159 15.02972
6 366202934 30.91049 29.54491 36.01111 13.19676
7 674369895 25.12067 23.47917 19.20919 17.80056
8 2381921725 30.74868 27.10666 26.72625 15.74085
9 113985507 30.49408 31.42298 30.15268 14.80199
Coleman.Liau.short Dale.Chall Dale.Chall.old Dale.Chall.PSK Danielson.Bryan
1 13.61668 -39.24680 19.64873 15.39123 6.685901
2 13.83786 -46.37638 20.21261 16.02624 7.567809
3 16.01335 -54.16176 20.72465 16.68095 9.347405
4 16.03768 -43.53440 20.03477 15.79063 7.715390
5 15.03074 -41.99528 19.87082 15.63779 7.333860
6 13.19788 -46.28524 20.08725 15.97403 7.557590
7 17.80186 -52.04878 20.66642 16.53339 9.412435
8 15.74222 -61.96275 21.17088 17.31203 10.594875
9 14.80297 -40.60579 19.71728 15.49774 7.172765
Danielson.Bryan.2 Dickes.Steiwer DRP ELF Farr.Jenkins.Paterson
1 78.05384 -387.3800 403.7219 7.774775 40.019794
2 79.64151 -505.6547 589.7926 11.952756 30.491315
3 77.86411 -671.9333 823.8259 18.770751 12.030911
4 75.10772 -477.2756 505.0422 11.263830 25.909573
5 76.51546 -445.8735 476.3505 10.202830 30.047080
6 80.89432 -527.5869 622.1406 13.352941 25.142157
7 74.26924 -641.4034 739.6699 17.601156 12.058213
8 79.96053 -841.7612 1075.6687 24.230769 1.203903
9 76.62979 -430.4035 453.0319 9.340336 33.577626
Flesch Flesch.PSK Flesch.Kincaid FOG FOG.PSK FOG.NRI FORCAST
1 42.228039 7.059644 11.75303 15.08413 6.722164 724.97123 11.60909
2 26.894488 8.105828 16.26259 20.40933 9.660864 691.68295 11.59427
3 8.820876 9.356740 21.76825 26.32214 13.165868 5955.67655 12.18203
4 22.192159 8.253699 15.79429 19.91044 8.765860 929.30349 12.45494
5 28.522429 7.880480 14.56070 18.55976 8.192705 1437.60346 12.20120
6 32.441611 7.846795 15.90956 18.96638 9.594897 95.68912 11.93483
7 5.427794 9.434751 21.12328 25.36756 12.162841 3278.38903 12.60806
8 -1.224892 10.200291 26.41531 30.88562 16.518317 530.86212 11.95364
9 33.092558 7.607082 13.62779 17.48280 7.724797 704.01628 11.98325
FORCAST.RGL Fucks Linsear.Write LIW nWS nWS.2 nWS.3
1 11.20000 93.31231 14.16216 49.99759 9.32680 9.718725 8.192257
2 11.18369 142.07874 25.92126 60.20375 11.84057 12.283662 11.127217
3 11.83024 217.14229 41.71542 76.73948 14.96100 15.334147 14.235992
4 12.13043 127.74894 24.57872 61.26691 12.55271 12.865153 11.236428
5 11.85132 116.46698 21.37736 57.39707 11.51669 11.847533 10.385243
6 11.55831 147.47059 21.41176 60.45058 11.25018 11.587118 9.879195
7 12.29887 203.34393 39.86127 75.24533 15.05229 15.363308 14.005114
8 11.57901 285.42308 52.15385 88.39874 16.66340 17.080919 16.234036
9 11.61157 109.45378 18.97479 55.20439 10.96490 11.330390 9.713317
nWS.4 RIX Scrabble SMOG SMOG.C SMOG.simple SMOG.de Spache
1 8.499429 5.708709 1.471613 13.87839 13.41065 13.30613 8.306125 10.73456
2 12.068537 8.976378 1.487949 17.67173 16.98097 16.94308 11.943079 11.94939
3 16.019000 14.715415 1.469752 21.57769 20.68333 20.68801 15.688008 13.35626
4 11.766123 8.748936 1.462236 17.29012 16.62034 16.57720 11.577202 11.43966
5 10.851975 7.681604 1.483653 16.33573 15.71958 15.66216 10.662156 11.19849
6 11.063774 9.117647 1.521299 16.34635 15.72959 15.67234 10.672341 12.10326
7 15.403764 14.069364 1.491435 21.16304 20.28953 20.29045 15.290446 12.89612
8 19.034564 18.884615 1.480736 23.75715 22.75525 22.77761 17.777610 14.86982
9 10.123647 7.050420 1.468236 15.57148 14.99968 14.92941 9.929414 11.00328
Spache.old Strain Traenkle.Bailer Traenkle.Bailer.2 Wheeler.Smith
1 11.65481 9.179279 -415.7480 -277.7923 77.74775
2 13.06344 14.688189 -526.0310 -269.3198 119.52756
3 14.70832 21.996047 -698.4586 -300.0273 187.70751
4 12.46505 12.990638 -503.4141 -308.7776 112.63830
5 12.19221 11.812500 -474.1453 -294.5480 102.02830
6 13.24868 14.858824 -537.5076 -258.2996 133.52941
7 14.16228 20.450289 -668.8883 -330.7128 176.01156
8 16.47990 28.730769 -851.7963 -291.9283 242.30769
9 11.97071 10.912185 -457.9543 -292.6777 93.40336
meanSentenceLength meanWordSyllables
1 17.64565 1.734003
2 27.18898 1.800753
3 39.20356 1.870242
4 22.66383 1.910627
5 21.25236 1.852736
6 28.88235 1.714868
7 34.70231 1.964354
8 52.26923 1.832230
9 20.06303 1.812984
La quasi-totalité de ces indices reposent sur deux variables mathématiques simples : la longueur des mots (en lettres ou en syllabes) et la longueur des phrases (en mots).
Pour prendre un exemple, on y trouve par exemple l’indice Flesch, un des plus célèbres, qui se lit sur une échelle de 0 à 100. Plus le score est élevé, plus le texte est facile à lire (80-100 : livre pour enfant, 50-80 : texte standard), alors qu’un score en dessous de 50 indique un texte très complexe (académique ou juridique). Malheureusement, la plupart de ces indices sont calibrés pour l’anglais et leur interprétation pour le français s’en trouve souvent faussée.
Nous n’allons pas aller plus loin ici. Une étude approfondie de l’aide de cette de fonction et de ses sources est donc nécessaire pour utiliser judicieusement ces différents indices.
3 Collocations et Concordance
3.1 Collocations
Les collocations sont des suites de mots qui apparaissent ensemble plus souvent que le simple hasard ne le prédirait (p.ex.: “services publics” ou “développement durable”). Pour les extraire, on utilise textstat_collocations() sur l’objet corpus. L’argument size permet de définir le nombre de termes constituant ces collocations (bigrammes, trigrammes, etc.) :
colloc_df = textstat_collocations(corpus_obj, size=2)
as_tibble(colloc_df)# A tibble: 6,885 × 6
collocation count count_nested length lambda z
<chr> <int> <int> <dbl> <dbl> <dbl>
1 de la 671 0 2 1.90 38.4
2 doit être 85 0 2 6.10 34.5
3 à la 269 0 2 2.09 28.5
4 la france 148 0 2 3.65 26.4
5 services publics 36 0 2 7.60 24.6
6 notre pays 37 0 2 5.08 23.9
7 créer un 53 0 2 4.74 23.9
8 tous les 100 0 2 4.35 23.1
9 dans les 123 0 2 2.39 22.3
10 service public 22 0 2 6.76 21.6
# ℹ 6,875 more rows
Par défaut, ces collocations sont classées selon le score z de l’indice lambda. Deux choses sont évaluées ici :
La force de l’association (le lambda) : Cet indice compare la fréquence de co-occurrence de deux mots par rapport à ce que prédirait le simple hasard. Par exemple, des termes comme “services” et “publics” s’attirent fortement, contrairement à des mots ultra-fréquents qui se croisent par hasard (comme “dans” et “le”).
La fiabilité du résultat (le score z) : Il mesure la significativité statistique de cette attraction. Il nous garantit que l’association observée est solide et ne relève pas d’un simple hasard lié à un échantillon de texte trop petit.
On peut également s’intéresser aux collocations de plus grand ordre :
colloc_df = textstat_collocations(corpus_obj, size=3)
as_tibble(colloc_df)# A tibble: 3,518 × 6
collocation count count_nested length lambda z
<chr> <int> <int> <dbl> <dbl> <dbl>
1 pour en faire 5 0 3 6.84 4.92
2 plus en plus 9 0 3 9.97 4.56
3 a pas de 6 0 3 4.58 4.41
4 en la matière 4 0 3 8.95 4.20
5 de plus en 9 0 3 6.04 4.03
6 pas plus de 4 0 3 3.57 3.86
7 sur tout le 8 0 3 6.27 3.66
8 au pas la 2 0 3 6.00 3.60
9 à ne pas 2 0 3 7.71 3.51
10 est un des 2 0 3 5.92 3.49
# ℹ 3,508 more rows
3.2 Concordances
Les concordances permettent d’observer les mots en contexte, c’est-à-dire de voir les mots qui apparaissent avant et après un mot de requête donné. Pour obtenir un concordancier, on utilise la fonction kwic() (Key Word In Context) sur notre objet tokens avec un mot de requête (ici "france"). Le résultat de cette fonction peut ensuite être transformé en tibble pour un affiche plus élégant :
requete = "france"
kwic_obj = kwic(tokens_obj, requete)
as_tibble(kwic_obj)# A tibble: 257 × 7
docname from to pre keyword post pattern
<chr> <int> <int> <chr> <chr> <chr> <fct>
1 en_marche_2017.txt 14 14 de conquête pour bâtir … France nouv… france
2 en_marche_2017.txt 31 31 DE CONQUÊTE POUR BÂTIR … FRANCE NOUV… france
3 en_marche_2017.txt 200 200 sa place . Et la France a to… france
4 en_marche_2017.txt 1151 1151 morts par an rien qu’en France , de… france
5 en_marche_2017.txt 1455 1455 . · Nous placerons la France en t… france
6 en_marche_2017.txt 1512 1512 · Nous ferons de la France le l… france
7 en_marche_2017.txt 1668 1668 mêmes règles pour tous … France , la… france
8 en_marche_2017.txt 2596 2596 pour tous nos enfants La France a ét… france
9 en_marche_2017.txt 2645 2645 grâce à eux que la France fait… france
10 en_marche_2017.txt 3233 3233 . Car redonner à la France son … france
# ℹ 247 more rows
On peut aussi utiliser des expressions régulières comme requêtes :
requete = "fran"
kwic_obj = kwic(tokens_obj, requete, valuetype="regex")
as_tibble(kwic_obj)# A tibble: 473 × 7
docname from to pre keyword post pattern
<chr> <int> <int> <chr> <chr> <chr> <fct>
1 en_marche_2017.txt 14 14 de conquête pour bâtir … France nouv… fran
2 en_marche_2017.txt 31 31 DE CONQUÊTE POUR BÂTIR … FRANCE NOUV… fran
3 en_marche_2017.txt 200 200 sa place . Et la France a to… fran
4 en_marche_2017.txt 575 575 au Cœur des préoccupati… França… , c’… fran
5 en_marche_2017.txt 1151 1151 morts par an rien qu’en France , de… fran
6 en_marche_2017.txt 1455 1455 . · Nous placerons la France en t… fran
7 en_marche_2017.txt 1512 1512 · Nous ferons de la France le l… fran
8 en_marche_2017.txt 1668 1668 mêmes règles pour tous … France , la… fran
9 en_marche_2017.txt 2263 2263 la protection de tous l… França… , pa… fran
10 en_marche_2017.txt 2329 2329 · Au plus près des França… , el… fran
# ℹ 463 more rows
Ou une “phrase” (voir l’aide ?phrase pour savoir comment les construire) :
requete = phrase("* social*")
kwic_obj = kwic(tokens_obj, requete)
as_tibble(kwic_obj)# A tibble: 192 × 7
docname from to pre keyword post pattern
<chr> <int> <int> <chr> <chr> <chr> <fct>
1 en_marche_2017.txt 129 130 est le moteur de la progre… . No… * soci…
2 en_marche_2017.txt 332 333 Nous rétablirons les ex… cotisa… sur … * soci…
3 en_marche_2017.txt 731 732 charges · et supprimero… Régime… des … * soci…
4 en_marche_2017.txt 812 813 à embaucher en baissant… cotisa… empl… * soci…
5 en_marche_2017.txt 911 912 . · Nous redéfinirons le dialog… . · … * soci…
6 en_marche_2017.txt 1973 1974 lutte contre la fraude … presta… sera… * soci…
7 en_marche_2017.txt 2143 2144 à toutes les formes de concur… délo… * soci…
8 en_marche_2017.txt 4229 4230 on part du bas de l’éche… , pl… * soci…
9 en_marche_2017.txt 4391 4392 . · Nous créerons un versem… uniq… * soci…
10 en_marche_2017.txt 4398 4399 unique . · Toutes les alloca… ( AP… * soci…
# ℹ 182 more rows
On peut afficher comment notre requête se répartit dans les textes avec la fonction textplot_xray() du package quanteda.textplots :
textplot_xray(kwic_obj)
4 Matrice documents-termes et spécificités
4.1 Matrice documents-termes
Nous l’avons déjà vu : pour obtenir la matrice documents-termes, on utilise la fonction dfm() de quanteda (document-features matrix, les “features” étant généralement les termes) :
dfm_obj = dfm(tokens_obj)
dfm_objDocument-feature matrix of: 9 documents, 8,865 features (77.91% sparse) and 0 docvars.
features
docs emmanuel macron président . “ retrouver
en_marche_2017.txt 1 1 3 349 1 2
europe_ecologie_les_verts_2017.txt 0 0 0 210 0 1
france_insoumise_2017.txt 0 2 6 519 0 2
front_national_2017.txt 0 0 0 243 0 2
mouvement_democratique_2017.txt 0 0 4 426 0 2
parti_communiste_francais_2017.txt 0 1 1 18 0 0
features
docs notre esprit de conquête
en_marche_2017.txt 45 2 351 2
europe_ecologie_les_verts_2017.txt 19 0 195 0
france_insoumise_2017.txt 34 3 1202 0
front_national_2017.txt 17 0 329 0
mouvement_democratique_2017.txt 25 0 566 0
parti_communiste_francais_2017.txt 1 0 27 0
[ reached max_ndoc ... 3 more documents, reached max_nfeat ... 8,855 more features ]
Afin d’obtenir une matrice plus compacte et plus pertinente (pour les analyses ultérieurs) il peut être utile de nettoyer notre objet tokens au préalable.
On commence donc par recréer un objet de type tokens en supprimant la ponctuation, les nombres, etc. :
tokens_filtered_obj = tokens(corpus_obj, remove_punct=T, remove_symbols=T,
remove_numbers=T, remove_url=T,
remove_separators=T)Nous allons également séparer les tokens par apostrophe (deux caractères présents dans le corpus):
tokens_filtered_obj = tokens_filtered_obj %>%
tokens_split("'") %>%
tokens_split("’")Finalement, on supprime les stopwords avec le fichier “frenchST.txt” que nous avons téléchargé précédemment (voir la section “Préparation du corpus” du TP 1) :
stopwords = readLines("data/stopwords/frenchST.txt")
tokens_filtered_obj = tokens_filtered_obj %>%
tokens_remove(stopwords)On obtient alors :
dfm_obj = dfm(tokens_filtered_obj)
dfm_objDocument-feature matrix of: 9 documents, 7,674 features (78.31% sparse) and 0 docvars.
features
docs emmanuel macron président retrouver esprit
en_marche_2017.txt 1 1 3 2 4
europe_ecologie_les_verts_2017.txt 0 0 0 1 0
france_insoumise_2017.txt 0 2 6 2 5
front_national_2017.txt 0 0 0 2 1
mouvement_democratique_2017.txt 0 0 4 2 1
parti_communiste_francais_2017.txt 0 1 1 0 0
features
docs conquête bâtir france nouvelle contrat
en_marche_2017.txt 2 2 21 5 2
europe_ecologie_les_verts_2017.txt 0 0 38 2 0
france_insoumise_2017.txt 0 1 64 17 7
front_national_2017.txt 0 1 36 1 3
mouvement_democratique_2017.txt 0 4 35 10 8
parti_communiste_francais_2017.txt 0 0 0 1 0
[ reached max_ndoc ... 3 more documents, reached max_nfeat ... 7,664 more features ]
Cette matrice peut être utilisée plus tard pour des analyses avancées, par exemple pour faire une Analyse Factorielle des Correspondances (AFC). Elle est à la racine de toutes les méthodes de type Bag-of-Words (l’ordre des mots n’est pas pris en compte).
4.2 Spécificités
Les spécificités (en anglais: keyness) sont obtenues en comparant la fréquence des mots d’un document “cible” par rapport au reste du corpus. Elles permettent de répondre à la questions : “Quels mots caractérisent le mieux ce texte par rapport aux autres ?”. Elles s’obtiennent avec la fonction textplot_keyness() (de quanteda.textstats) appliquée à un objet de type dfm. On doit préciser dans l’argument target quel document observer en contraste avec le reste du corpus. On peut désigner ce document avec un indice :
keyness_obj = textstat_keyness(dfm_obj, target=3) # n°3 -> "france_insoumise_2017.txt"
as_tibble(keyness_obj)# A tibble: 7,674 × 5
feature chi2 p n_target n_reference
<chr> <dbl> <dbl> <dbl> <dbl>
1 proposons 155. 0 82 4
2 réaliser 155. 0 80 3
3 suivantes 147. 0 78 4
4 mesures 82.3 0 85 34
5 écologique 54.2 1.83e-13 44 12
6 onu 43.6 4.06e-11 21 0
7 refuser 28.9 7.46e- 8 23 6
8 humaine 21.9 2.85e- 6 12 0
9 paix 20.5 5.82e- 6 16 4
10 impót 19.9 8.37e- 6 11 0
# ℹ 7,664 more rows
Ou avec son nom :
keyness_obj = textstat_keyness(dfm_obj, target="front_national_2017.txt")
as_tibble(keyness_obj)# A tibble: 7,674 × 5
feature chi2 p n_target n_reference
<chr> <dbl> <dbl> <dbl> <dbl>
1 français 53.1 3.24e-13 32 80
2 rétablir 47.6 5.32e-12 12 11
3 revaloriser 31.0 2.56e- 8 8 7
4 nationalité 26.0 3.39e- 7 6 4
5 française 23.3 1.35e- 6 13 28
6 expulsion 20.4 6.42e- 6 3 0
7 majoration 20.4 6.42e- 6 3 0
8 startups 20.4 6.42e- 6 3 0
9 immigration 20.0 7.64e- 6 6 6
10 maintenir 17.9 2.34e- 5 7 10
# ℹ 7,664 more rows
Notez que l’on peut afficher ce résultat sous la forme d’un graphique, ce qui montre aussi les mots les “moins caractéristiques” du corpus (les mots sous-représentés par rapport à la référence, i.e. le bas des tableaux précédents) :
textplot_keyness(keyness_obj)
4.3 Nuage de mots
Pour terminer, on peut faire un nuage de mots à partir de notre objet de type “dfm” avec la fonction textplot_wordcloud() du package quanteda.textplots. Ce n’est pas le graphique le plus adapté à une analyse rigoureuse, mais il possède un certain intérêt esthétique :
textplot_wordcloud(dfm_obj, max_words=100)
On peut utiliser dfm_subset() pour selectionner un sous-ensemble de document (selon plusieurs conditions logiques) :
# On affiche le nuage de mots de "en_marche_2017.txt"
textplot_wordcloud(dfm_subset(dfm_obj, docname_=="en_marche_2017.txt"), max_words=100)
5 Co-occurrences
La table des co-occurrences permet de voir quels mots apparaissent le plus fréquemment ensembles dans un contexte donné. Elle se construit à partir d’un objet de type tokens, avec la fonction fcm() (feature co-occurrence matrix). Notez que, de base, fcm() distingue la casse, c’est pourquoi nous passons notre objet tokens dans tokens_tolower(), afin d’enlever toutes les lettres majuscules :
tokens_low_obj = tokens_tolower(tokens_filtered_obj)
fcm_obj = fcm(tokens_low_obj)
fcm_objFeature co-occurrence matrix of: 7,674 by 7,674 features.
features
features emmanuel macron président retrouver esprit conquête bâtir france
emmanuel 1 5 7 4 8 2 2 91
macron 0 2 20 8 18 2 4 219
président 0 0 40 28 50 18 28 783
retrouver 0 0 0 4 24 4 16 385
esprit 0 0 0 0 17 8 18 545
conquête 0 0 0 0 0 2 4 84
bâtir 0 0 0 0 0 0 7 282
france 0 0 0 0 0 0 0 4980
nouvelle 0 0 0 0 0 0 0 0
contrat 0 0 0 0 0 0 0 0
features
features nouvelle contrat
emmanuel 17 32
macron 52 46
président 200 116
retrouver 74 55
esprit 128 84
conquête 20 6
bâtir 68 46
france 1970 1431
nouvelle 217 307
contrat 0 158
[ reached max_nfeat ... 7,664 more features, reached max_nfeat ... 7,664 more features ]
Par défaut, fcm() compte le nombre de paires de tokens (distinctes) selon le contexte “document”. Si l’on désire compter le nombre de contextes possédant une ou plusieurs co-occurrences, on doit faire :
fcm_obj = fcm(tokens_low_obj, count="boolean")
fcm_objFeature co-occurrence matrix of: 7,674 by 7,674 features.
features
features emmanuel macron président retrouver esprit conquête bâtir france
emmanuel 1 2 2 2 2 1 1 2
macron 0 2 4 3 3 1 2 3
président 0 0 5 4 4 2 3 5
retrouver 0 0 0 4 5 1 4 6
esprit 0 0 0 0 3 1 4 5
conquête 0 0 0 0 0 2 1 2
bâtir 0 0 0 0 0 0 2 4
france 0 0 0 0 0 0 0 8
nouvelle 0 0 0 0 0 0 0 0
contrat 0 0 0 0 0 0 0 0
features
features nouvelle contrat
emmanuel 2 2
macron 4 3
président 6 5
retrouver 6 5
esprit 5 5
conquête 2 2
bâtir 4 4
france 7 7
nouvelle 6 6
contrat 0 5
[ reached max_nfeat ... 7,664 more features, reached max_nfeat ... 7,664 more features ]
Ce qui veut dire, qu’avec 9 documents, les composantes ne peuvent dépasser 9.
On peut également une fenêtre d’un certain nombre de mots comme contexte :
fcm_obj = fcm(tokens_low_obj, context="window", window=5)
fcm_objFeature co-occurrence matrix of: 7,674 by 7,674 features.
features
features emmanuel macron président retrouver esprit conquête bâtir france
emmanuel 0 3 1 1 1 1 0 0
macron 0 0 2 1 1 1 1 0
président 0 0 0 1 1 1 1 2
retrouver 0 0 0 0 2 2 3 4
esprit 0 0 0 0 0 2 2 4
conquête 0 0 0 0 0 0 2 2
bâtir 0 0 0 0 0 0 0 3
france 0 0 0 0 0 0 0 64
nouvelle 0 0 0 0 0 0 0 0
contrat 0 0 0 0 0 0 0 0
features
features nouvelle contrat
emmanuel 0 0
macron 0 0
président 3 0
retrouver 3 1
esprit 4 2
conquête 3 2
bâtir 3 2
france 14 8
nouvelle 2 3
contrat 0 12
[ reached max_nfeat ... 7,664 more features, reached max_nfeat ... 7,664 more features ]
Il nous est ensuite possible d’afficher ces co-occurrences avec un graphe, grâce à la fonction textplot_network().
Cette fonction affiche au maximum 1000 noeuds, et même avec ce nombre, le graphe n’est pas très lisible. Nous allons donc d’abord sélectionner les 100 tokens les plus fréquents :
tokens_names = tokens_low_obj %>%
dfm() %>%
topfeatures(n=100) %>%
names()
tokens_names [1] "france" "politique" "entreprises" "faire"
[5] "travail" "pays" "mesures" "français"
[9] "créer" "ans" "droit" "vie"
[13] "sociale" "plan" "mettre" "proposons"
[17] "publics" "sécurité" "loi" "réaliser"
[21] "entreprise" "santé" "suivantes" "emploi"
[25] "publique" "europe" "place" "notamment"
[29] "état" "moyens" "salariés" "développement"
[33] "droits" "renforcer" "temps" "nationale"
[37] "garantir" "services" "république" "formation"
[41] "européenne" "économie" "public" "service"
[45] "égalité" "accès" "femmes" "assurer"
[49] "écologique" "lutte" "lutter" "européen"
[53] "mise" "développer" "défense" "justice"
[57] "rendre" "pouvoir" "impôt" "territoires"
[61] "nouvelles" "personnes" "protection" "fiscale"
[65] "numérique" "euros" "recherche" "jeunes"
[69] "social" "politiques" "nouvelle" "monde"
[73] "école" "matière" "outre-mer" "niveau"
[77] "logement" "création" "publiques" "fin"
[81] "enseignement" "économique" "soutenir" "citoyens"
[85] "territoire" "cadre" "aide" "sociales"
[89] "française" "système" "ensemble" "favoriser"
[93] "national" "action" "européens" "compte"
[97] "contrat" "emplois" "société" "retraite"
On crée un nouvel objet fcm qui ne contient que ces tokens :
fcm_filtered_obj = fcm_select(fcm_obj, tokens_names)Et on fait un graphe, avec les liens qui s’affichent seulement si le compte est de minimum 9 (seuil trouvé après tâtonnements) :
textplot_network(fcm_filtered_obj, min_freq=9)