Textmining, analyse de textes

en langage R

L'essentiel de cette page !

Un résumé de ce que je sais en sur le textming sous R.

Vous trouverez aussi des sous liens pour la détection du plagiat, mais aussi les méta-analyse où R peut s'avérer utile pour construire un réseau mettant en relation les notions qu'on retrouve colocalisées ensemble dans les publications scientifiques.

1- Ouvrir un corpus

Ouvrir les 2 libraires les plus pratiques pour le text-mining

library("readtext") # Ouverture de plusieurs documents de tous les types

library(quanteda) # textmining

Indiquer que l'on désire travailler ne langue française pour la stemmatisation (couper les fins de mots) :

# Deprecated quanteda_options(language = "french")

Attention, quanteda évolue beaucoup, pour charger ses fonctions statistiques, il faut maintenant faire :

install.packages(quanteda.textmodels)

library(quanteda.textmodels)

install.packages("quanteda.textstats")

library("quanteda.textstats")

install.packages("quanteda.textplots")

library("quanteda.textplots")

1.0- Charger un corpus à partir du web (web scraping)

On peut charger du contenu web avec la librairie rvest, en voici un exemple :

install.packages("rvest")

library("rvest")

page_wiki <- read_html("https://fr.wikipedia.org/wiki/Transylvanie_(région)") # Exemple d'URL

code_html <- html_text(page_wiki)

tableaux_de_la_page <- html_node(page_wiki,"table") %>% html_table(.,fill=T)

Remarque : les pipes "%>%" peuvent nécessiter l'exécution de la librairie magrittr.

1.1- Ouvrir un à plusieurs fichiers

Ouvrir un seul fichier

P1 <- readtext(file.choose()) # Attention, cete fonction combinée avec file.choose() peut débloquer en fonction du nom du fichier.

Regrouper différents corpus ou fichiers avec la fonction rbind()

P <- rbind(P1,P2)

Ouvrir plusieurs fichiers d'un coup

P <- readtext("*") # Ouvre tous les fichiers du répertoire de travail

P <- readtext("*.xlsx") # Ouvre tous les fichiers Excel du répertoire de travail

Remarque : attention si vous donnez une liste de fichier à ouvrir par readtext, il ne les ouvrira pas dans l'ordre demandé mais dans l'ordre alphabétique.

Attention à l'encodage du fichier pour éviter les problèmes de caractères spéciaux :

P <- readtext("*",encoding ="UTF-8")

1.2- Créer le corpus avec la fonction corpus()

Regrouper différents corpus ou fichiers avec la fonction rbind()

mon_corpus <- corpus(P)

Si votre corpus correspond à un document qu'il faut couper à chaque balise (tel un dialogue, on peut le faire avec la fonction corpus_segment() :

mon_corpus <- corpus_segment(mon_corpus,pattern="dialogue")

1.3- Décrire le corpus

  • Nombre de documents dans le corpus

ndoc(corpus)

  • Noms des fichiers du corpus

corpus$doc_id

ou sinon :

docnames(corpus)

  • DocVars, les variable qui décrivent un corpus

docvars(corpus,"id") <- 1:10 # Ajouter un id numéroté de 1 à 10 pour un corpus de 10 documents.

# Filtrer le corpus

corpus_subset(corpus,id<5) # Récupérer les documents dont l'id est en dessous de 5

  • Lisibilité

Un conseil, appliqué la lisibilité à d'autres ouvrages pour pondérer les valeurs de sorties qui n'entrent pas exactement dans la gamme de lisibilité attendue pour les tests de Flesch/Fog...

textstat_readability(corpus,measure="Flesch.Kincaid")

textstat_readability(corpus,measure="FOG")

  • Entropie

require(quanteda.textstats) # install.packages("quanteda.textstats")

textstat_entropy(corpus)

2- Nettoyer un corpus (très important !)

Nettoyer le texte est très important en textmining :

  • Que faire des stopwords : ces mots banaux (j'ai, et, que) qui diluent l'information textuelle ?

  • Comment se débarrasser de la ponctuations, des smileys, etc ?

  • Comment faire comprendre à l'ordinateur que regarde et regardent et regardes c'est la même chose , la racine du verbe : "regarde" ?

2.1- Utiliser la librairie textclean pour forcer un nettoyage du corpus

textclean est performante pour supprimer tous les caractères spéciaux, non ascii, les chiffres, les émoticônes. Je vous invite à aller explorer ses fonctionnalités. Voici 2 fonctions de nettoyage qui peuvent être utiles :

  • replace_symbol() - Élimine tous les symboles mais garde le reste intact

corpus<- replace_symbol(corpus)

  • replace_non_ascii() - Force l'élimination de tous les caractères non ascii. Attention, les caractères d'autres langues comme les caractères grecs, par exemple, seront éliminés.

corpus<- replace_non_ascii(corpus,replacement="")

  • strip() - Éliminer tous les caractères spéciaux, ou tous les chiffres, ou toutes les apostrophes.

corpus<- strip(corpus, char.keep = "~~", digit.remove = TRUE, apostrophe.remove = F)

2.2- Identifier les différents types de caractères contenus dans un corpus

Cette fonction est utile pour voir si des caractères étranges ne sont pas apparus (problème d'encodage) ou s'il ne persiste pas des caractères spéciaux non nettoyés comme des apostrophes courbes ou des espaces insécables.

mes_caracteres <- table(unlist(strsplit (corpus,"")))

print(mes_caracteres)

uniq_car <- names(mes_caracteres) # pour afficher tous les types de caractères

print(uniq_car)

Dans cet exemple de résultats, on voit sur la ligne 1 (caractère 3 et 4) qu'il y a 2 types d'espaces, ce qui va poser problème lors du découpage du corpus.

On voit aussi qu'il y a plusieurs types de guillemets ("), («) et (») à nettoyer.

' - \n ! " ( ) * , .

8335 2298 116134 1867 3808 974 2 14 14 1 6448 9213


: ; ? @ \\ « » Δ Κ Χ ά έ

643 13 1009 1 1 10 10 1 2 1 1 1

2.3- Remplacer un type de caractères particuliers

Parfois, un caractère spécial semble ne pas vouloir s'éliminer (apostrophe courbe, espace particulière), on va pouvoir forcer son remplacement sans faire appel à son code ascii (difficile à trouver). Exemple :

Je m'aperçois que le 2ème caractère de ma liste de caractère devrait être remplacé par une espace, je vais utiliser str_replace_all() de la librairie stringr (ne pas confondre avec str_replace() qui ne fera qu'un seul remplacement dans le document).

corpus <- str_replace_all(corpus,uniq_car[2], " ") # != str_replace

Pour d'autres remplacements/nettoyage (majuscules, fin des mots), d'autres nettoyage peuvent se faire ultérieurement.

A ce stade, on peut considérer que le corpus est prêt.

Vous faites toujours les mêmes nettoyages : un conseil, regroupez vos traitements de nettoyage dans une fonction clean

clean <- function(corpus) { ... }

Exemple :

clean <- function(corpus) {

corpus<- replace_non_ascii(corpus,replacement="")

corpus <- str_replace_all(corpus, "'", " ") # != str_replace

return(corpus)

}

3- Extraire du texte grâce aux expressions régulières

Souvent, dans un document, on souhaite extraire une section particulière, comme tous les chapitres, toutes les réponses à un questionnaire.

On peut donc faire appel à différentes fonctions qui reconnaîtront des motifs très particuliers (pattern) rédigés sous la forme d'expression régulière.

Pour manipuler les expression régulières, vous trouverez un complément sur la page données de type texte.

Fonctions utiles de la librairie stringr

str_detect(texte, pattern = " ") # détecte si il y a des espaces.

str_replace(texte, pattern = "a?", "") # remplace le premier "a" suivi d'un caractère quelconque par rien ("").

str_replace_all(texter, pattern = "rouge-gorge", "oiseau") # remplace tous les rouge-gorge par oiseau.

str_split() # découpe le texte sur un motif


Exemple 1 : j'ai un roman que je souhaite découper pour séparer dans un corpus le préambule, chaque chapitre numéroté et l'épilogue

mon_corpus <- unlist(str_split(corpus,"Préambule")) # unlist est nécessaire pour réajuster le format de sortie

# Coupe chaque chapire qui est suivi par au moins 1 chiffre 1 à 9 puis, à la rigueur un autre chiffre 0 à 9.

mon_corpus <- unlist(str_split(mon_corpus,"Chapitre.[1-9]{1}[0-9]{0,1}"))

mon_corpus <- unlist(str_split(mon_corpus,"Épilogue"))


Exemple 2 : une fonction pour récupérer la réponse entre 2 questions

wash <- function(corpus,quest1,quest2,clean=T) {

if (clean == T) {

before <- clean(quest1)

after <- clean(quest2)

}

my_pattern <- paste0(quest1,".*",quest2)

reponse <- str_extract(corpus,pattern=my_pattern)

t_before <- nchar(quest1)+1

t_after <- nchar(quest2)

t_reponse <- nchar(reponse)

reponse <- str_sub(reponse, t_before, (t_reponse-t_after ))

return(reponse)

}

reponse <- wash(corpus[1], "Quel est votre adresse ?", "Que pensez-vous du confinement ?")

print(reponse) # On a récupéré l'adresse

Remarque : faire appel à une boucle for pour une extraction sur plusieurs documents du corpus

4- DFM/DTM & tokens

4.1- Générer les tokens

Le token est la première approche du textming : on découpe le corpus sur chaque espace. Un token est donc un ensemble de textes découpés mot par mot.

Une suite de ligne suivi de "pipes" représentées par %>% permet de faire l'ensemble des traitements nécessaires pour la fabrication d'un token.

toks_news <- tokens(corpus,remove_punct=T,remove_numbers=F) %>%

tokens_remove(., pattern = stopwords('fr'), valuetype = 'fixed') %>% tokens_tolower(., keep_acronyms = FALSE)

  • remove_punct = T : éliminer la ponctuation

  • remove_numbers : éliminer les nombres

  • tokens_remove() : éliminer des motifs, ici on éliminer les mots banaux (stopwords) d'un dictionnaire pré-installé.

On peut continuer le nettoyage du token par la suite pour éliminer les mots que l'on ne souhaite pas conserver, souvent ceux qui n'apporte par un sens particulier (stopwords)

# Charger un dictionnaire en ligne

swfr1 <- stopwords(language='fr', source='stopwords-iso' ) # permet d'avoir un dictionnaire plus complet que celui par défaut

# Compléter ce dictionnaire

swfr1 <- c(swfr1,"jai","nest","qua","quil","etions","ca","etait","ete","faire","etaient","fit","semblaient","voulait","lorsqu","aller") # on,peut ajouter ainsi, à ce dictionnaire, son propre dictionnaire

Ou en faire un plus complexe avec racinisation ou lemmisation :

swfr2 <- list("Aimer" = c("Aime","Aimes","Amant")) ; swfr2 <- dictionary(swfr2)

Ou encore : Charger son propre dictionnaire rédigé dans un bloc note ou chaque mot est à la ligne

swfr2 <- readtext(file.choose())$text %>% str_split(.,pattern="\n") %>% unlist(.)

Application des dictionnaires pour nettoyer

toks_news <- tokens_remove(toks_news, swfr1)

toks_news <- tokens_remove(toks_news, swfr2)

Remarque : il existe d'autres approches pour les dictionnaires, en particulier pour regrouper les déclinaisons d'un verbe ou trier les mots par sentiments.

4.2- Mettre en place la DFM

La DFM est une matrice qui se construit à partir d'un token. C'est un tableau qui répertorie le nombre d’occurrences de chaque mot.

Grâce à la DTM, un text va ainsi être converti en une structure numérique qui pourra être étudiée par des outils mathématiques.

my_dfm<- dfm(toks_news)

  • cf. dfm_remove() pour supprimer les stopwords et la ponctuation

  • cf. dfm_wordstem() "stem" qui permet de ne conserver que la racine des mots pour réduire les différences liées aux conjugaisons et aux pluriel

  • tolower pour tout convertir en minuscules

  • Pondérer la DFM

Il peut être pertinent de pondérer la DFM, surtout si les oeuvres à comparer ont des tailles différentes.

Un mot peut apparaître 10 fois dans un ouvrage de 100 pages et 20 fois dans un ouvrage de 200 pages, si on ne met pas la DFM/DTM en %, (pondération), on ne pourra voir que le mot a la même fréquence dans les deux documents.

dtm_ajust <- dfm_weight(my_dfm, scheme='prop') # Existe d'autres schema tel logcount

  • Identifier les mots qui ressortent en comparant les ouvrages d'un corpus

docfreq(dtm, scheme='inverse')

5- Afficher un wordcloud (un nuage de mots)

On peut analyser un texte ou un corpus pour voir quels mots y apparaissent le plus :

topfeatures(my_dfm[1])) # Quels mots sont les plus fréquents dans le doc 1 de mon corpus

Le mieux est toutefois de le visualiser sous forme de diagramme en barres (barplot(topfeatures(my_dfm[1]))) ou d'un nuage de mots.

Ou encore :

my_tsf <- textstat_frequency(my_dfm,n=15)

plot(my_tsf$frequency)

text(x=2:15,y=my_tsf$frequency,labels=my_tsf$feature)

Nuage de mots simple

textplot_wordcloud(my_dfm[3],min_count = 20,col=1:7)


# Ici on n'affiche que les mots qui apparaissent plus de 20 fois

# L'affichage s'est fait sur le 3ème document de mon corpus d'om le my_dfm[3]

Nuage de mots réalisé sur les "Lettres de mon Moulin" d'Alphonse Daudet.

Nuage de mots sur NGRAMs

Afficher les ngrams : association de 2 mots ou plus (paramètre n)

N2 <- tokens_ngrams(toks_news[3], n=2, skip = 0, concatenator = " ") %>% dfm(.)

textplot_wordcloud(N2,min_count = 10)

Afficher les skipgrams : association de n mots dans un contexte de x mots (paramètres skip)

N2 <- tokens_skipgrams(toks_news[4], n=2, skip = 0:5, concatenator = " ") %>% dfm(.)

textplot_wordcloud(N2,min_count = 10, col=1:10)

NGRAM en skipgram sur l'oeuvre Bel Ami de Maupassant

6- Identifier les mots caractéristiques : keyness

Il est particulièrement intéressant d'identifier les mots qui caractérisent une oeuvre/un document par comparaison avec d'autres.

C'est le rôle du keyness :


tstat_key <- textstat_keyness(my_dfm, target=3,measure = c("chi"))

Méthode :

  • "chi2" ; Très efficace.

  • "exact" (test exact de Fisher) ;

  • "lr" pour le rapport de vraisemblance ;

  • "pmi" pour l'information mutuelle ponctuelle. - s'intéresse aux mots rares plutôt que ceux à forte fréquence

# Visualiser

textplot_keyness(tstat_key)

Résultats de keyness chi2 sur les "lettres de mon moulin" comparé à 5 autres œuvres.

Le mieux est aussi de visualiser les résultats du keyness sous forme de wordcloud, en faisant une petite fonction

keyness_wordcloud <- function(tstat_key, n=20, col=1:7) {

temp <- c()

for (i in 1:n) {

temp <- c(temp,rep(tstat_key$feature[i],tstat_key$n_target[i]))

}

my_dfm <- dfm(temp)

textplot_wordcloud(my_dfm, min_count = 1, col=col)

}

keyness_wordcloud(tstat_key,n=30)

Résultats de keyness pmi sur les "lettres de mon moulin" comparé à 5 autres œuvres. Wordcloud sur les 30 premiers mots.

Nuage de mots en TreeMap

Une représentation pertinente est d'avoir recours au treemap, la forêt de mots pour visualiser leur importance respective en termes de surfaces.

#Représentation en Treemap

# Les 20 mots les plus spécifiques, la surface représente la valeur de chi2

install.packages("treemap")

library("treemap")

treemap(tstat_key[1:20,],

index="feature",

vSize="chi2")

Mots clefs keyness représentatifs de Bel-Ami.

7- Comparaison de textes : wordscore, wordfish et composantes

Dans cette rubrique, on s'attachera à la comparaison de documents. Pour cela, on ouvrira un corpus de 5-6 romans téléchargés ici.

Ces romans ont été nettoyés selon la méthode de la partie 2, puis tokenisés (toks_new) et convertis en DFM (my_dfm).

7.1- Matrice TF-IDF

La matrice TF-IDF (term frequency-inverse document frequency) ne va pas recenser les mots les plus fréquents comme une DFM classique mais au contraire les mots les plus rares en comparant les documents entre eux, ce afin d'en extraire le sens subtile.

dfm_idf <- dfm_tfidf(my_dfm, scheme_tf='count', scheme_df='inverse')

7.2- Wordfish : comparaison non supervisée de documents

Cette méthode permet de façon non supervisée d'attribuer un score à chaque document qui traduit leur proximité relative.

# Analyse de wordfish

wf <- textmodel_wordfish(my_dfm) # Apprentissage non supervisée

C'est long ! Soyons patient !

# Représentation sur une seule dimension = Wordfish

wf$theta

textplot_scale1d(wf,doclabels=P$doc_id)

7.3- Wordscore : évaluation de documents après apprentissage

Le wordscore permet de faire des apprentissages supervisées.

Exemple : si j'ai un ensemble de documents évalués. Je vais donner ces documents en apprentissage à R qui sera alors capable d'en évaluer d'autres.

ws <- textmodel_wordscores(my_dfm,y= Eval)

Eval correspond ici à la liste des scores des documents.

On peut aussi le faire avec la matrice tf_idm en complément.


ws_pred <- predict(ws, new_data=my_dfm, se.fit=TRUE)

ws_pred$fit

Il est parfois utile d'établir une régression pour rétablir la correspondance entre score prédit et score connu :

my_reg<- lm(Eval~ws_pred$fit)

#summary(my_reg)

Scor1 <- coef(my_reg)[1]+ws_pred$fit*coef(my_reg)[2]

plot(Eval,Scor1,cex=0.5,col=2,pch=16,xlab="Score réel",ylab="Predict")

points(Eval[spl],Scor1[spl],cex=2,col="#00AA0088",pch=16)

On pourra par la suite ,faire des prédiction en donnant la dfm de documents à évaluer.

Une correction par l'équation my_reg permettra de les évaluer.

7.4- Décomposition de documents en composantes pour faire des regroupements/régressions

ca <- textmodel_ca(my_dfm)

plot(ca$rowcoord[,1:2],pch=1,col=c(1:5),cex=2,xlim=c(-2.6,1.3),ylim=c(-1.3,1.3))

text(ca$rowcoord[,1:2],labels=P$doc_id)

On voit sur cette représentation que la numérisation permet de voir quels textes sont les plus proches. Sans surprise, les 2 recueils du même auteur (Maupassant) restent regroupés.

Cette représentation se prête aussi à des régressions linéaires ou des catégorisation de type svm.

7.5- Dendrograms et clusters de textes

mydi<-dist(my_dfm)

# ou avec

mydi<-dist(dfm_idf)

myclust <- hclust(mydi, method="ward.D2")

plot(myclust,labels=P$doc_id)

Consulter la méthodologie sur les clustering pour en savoir plus.

7.6- Evaluer la distance ou la similitude entre des documents

textstat_simil() # Deux documents similaires auront une valeur de 1

textstat_dist() # Deux documents similaires, auront une distance de 0

8- Établir des liens sémantiques

Les liens sémantiques se font facilement sous quanteda avec la fonction fcm() mais le rendu graphique est assez faible.

Exemple :

mymat <- fcm(toks_news, context = "window", count = "weighted", window = 15, ordered = TRUE, tri = FALSE) # Window : taille de la fenêtre...

jpeg() # nécessaire car l'affichage du réseau à l'écran directement dans R dysfonctionne en général.

mywd <- names(topfeatures(mymat , 14))

fcm_select(mymat , pattern = mywd) %>%

textplot_network(min_freq = 0.5)

dev.off()

Pour cette raison, on va voir comment développer les cooccurrences sous forme de igraph.

8.1- Etudier les cooccurrences dans des commentaires sur le web ou autres

Un arbre est la représentation idéale pour mettre en relation des mots.

On partira pour cela d'une matrice de cooccurrences (syn, matrices d'association de mots qui apparaîtront dans le même commentaire ou tweet d'une personne).

Dans cette représentation, on a associé les mots qui se retrouvent ensemble dans les mêmes commentaires d'étudiants qui ont évalué une séance de TP en télétravail (en 2020 pendant la session de COVID-19).

On voit apparaître 4 groupes sémantique : 1) Ceux qui s'attachent à l'absence de manipulation - 2) La quantité de travail - 3) L'innovation que représente un TP virtuel sous forme de vidéos + site internet et 4) Les évaluations qui ont suivi.

Cette analyse est entièrement automatisée, non-supervisée.

0) Initialisation

Partons d'un cluster, ici, il s'agit ce 81 commentaires qui représentent 81 documents dans un seul et même corpus.

2) Identifier les termes de travail

Ici nous allons prendre les 30 termes les plus récurrents :

taille = 30

tsf <- textstat_frequency(my_dfm,n=taille)

mywd <- tsf$feature

3) Générer la matrice de cooccurrence

# Générer une matrice de cooccurrence - Matrice d'associations de mots coocurrences

matx <- t(my_dfm)%*%my_dfm

# Utiliser la fonction match pour ne récupérer que les lignes/colonnes correspondants aux termes retenus en 2)

position <- na.omit(match(rownames(matx),mywd)) # ou inverser les termes de match

matx <- matx[position, position]

nrow(matx)

Remarque : si on veut rester dans quanteda, la fonction dfm_select() peut remplacer match().

4) Générer le premier igraph (théorie des graphs)

library(igraph);

g <- graph.adjacency(matx, weighted=T,

mode = "undirected")

plot(g)

A ce stade, le igraph présente de nombreux défauts :

  • Mise en forme plus que bof

  • Liens récurrents (dans les 2 sens entre deux sommets)

  • Auto-relations : des liens partant d'un même sommet (cercle) pour y revernir

5) Nettoyage et mise en forme

net <- graph_from_adjacency_matrix(matx, weighted=T)

net <- simplify(net, remove.multiple = T, remove.loops = TRUE) # élaguer les liens redondants

E(net)$arrow.size <- 0 # Exemple de sauvegarde d'un paramètre qu'on aurait plus ainsi à mettre dans comme paramètre de plot comme c'est le cas ci-dessous...

E(net)$edge.color <- "black"

# Afficher le tout en tenant compte avec betweenness du poids relatifs d'un mot pour lui donner une taille

plot(net ,vertex.size=sqrt(betweenness(net))+10,vertex.color="green",

edge.arrow.size =0,arrow.mode=0,edge.color="black")

6) Détecter les catégories pour les coloriser

Détection de communautés basée sur la propagation des labels : il existe de nombreuses méthodes pour définir ces catégories par regroupements des labels, prise en compte des distances ou prise en compte d'un vertex de référence...

Un site intéressant pour parler de regroupement.

  • Définir une classe :

#clp <- cluster_fast_greedy(net)

#clp <- cluster_label_prop(net)

clp <- cluster_optimal(net)

class(clp)

  • Représenter les communities (classe)

l <- layout_with_fr(net)

plot(clp, net, layout = l, vertex.size=sqrt(betweenness(net))+10)

7) Élaguer l'arbre pour ne garder que les branches les plus épaisses.

Non encore développer sur ce site : il faut penser à éliminer les cooccurrences ponctuels entre deux mots.

Pour cela, il faut nettoyer et faire figurer l'importance des relations sur le igraph.

En projet...

8.2- Etudier les cooccurrences au sein d'un même document, pour étudier la relation entre des personnages d'un roman

Étudions en guise d'exemple le roman Bel Ami de ce cher Maupassant.


On va étudier la relation entre les protagonistes.

Je n'ai pas nettoyé les branches pour aller vite : mais on voit ici qu'un petit ménage serait utile...

1) Ouverture du corpus

library("readtext") ; library(quanteda) ;

P <- readtext(file.choose(),encoding="UTF-8") ; mon_corpus <- corpus(P)

library(textclean)

corpus<- replace_symbol(mon_corpus)

2) Recherche des prénoms du roman

library("stringr")

mot_maj <- str_extract_all(mon_corpus,pattern="(?<![\\!\\.]\\s)\\b[:upper:][:lower:]+\\b" )

names(sort(table(mot_maj),rev=T))

On ne va pas détailler plus ici : c'est un travail long et fastidieux. On doit arriver à la fin à avoir ce dictionnaire des personnages.

Attention : ne pas nettoyer le corpus et le dictionnaire de façon différente, sinon, on se cassera les dents à cause des majuscules, des caractères accentués ou du "oe" de soeur. Attention aussi au je de narration qui est souvent j'.

Dictionnaire_Bel_Ami.xlsx

3) Charger le dictionnaire des mots à chercher (ici les personnages du roman)

library("openxlsx")

dico_perso <- read.xlsx(file.choose())

#dico_perso<- data.frame(lapply(dico_perso, function(x) {gsub(" ", "", x)}))

# Initialisation d'une liste vide

liste <- list()

# Construction de la liste avec les termes du

for(i in 1:nrow(dico_perso)){

# data.fr[i,1] = item; #str_split décompose la seconde col.

#model <- str_replace_all(dico_perso[i,2]," ","")

model <- dico_perso[i,2]

liste[dico_perso[i,1]] <- str_split(model, pattern=',')

}

liste

dic <- dictionary(liste)

4) tokenisation et application du dictionnaire identifier les personnages à travers leurs noms et surnoms

Les mots ne correspondant pas à un personnage seront remplacé par _UNMATCHED

toks_news <- tokens(corpus,remove_punct=T,remove_numbers=F)

tn <- tokens_lookup(toks_news, dic, exclusive=T, levels=1:2, nomatch = "_UNMATCHED")

5) Matrice d'adjacence, de cooccurrence des personnages

mywd <- dico_perso[,1] ; nperso <- length(mywd)

mymat <- fcm(tn, context = "window", count = "weighted", window = 12,

, ordered = TRUE, tri = FALSE)

positionr <- na.omit(match(mywd,rownames(mymat)))

positionc <- na.omit(match(mywd,colnames(mymat)))

matx <- mymat[positionr, positionc]

nrow(matx) ; matx

6) Théorie des graphs, représentation du réseau des personnages

library(igraph)

net <- graph_from_adjacency_matrix(matx, weighted=T)

net <- simplify(net, remove.multiple = T, remove.loops = TRUE) # élaguer les liens redondants

E(net)$arrow.size <- 0 # Exemple de sauvegarde d'un paramètre qu'on aurait plus ainsi à mettre dans comme paramètre de plot comme c'est le cas ci-dessous...

E(net)$edge.color <- "black"

# Afficher le tout en tenant compte avec betweenness du poids relatifs d'un mot pour lui donner une taille

clp <- cluster_optimal(net)

class(clp)

l <- layout_with_fr(net)

plot(clp, net, layout = l, vertex.size=(betweenness(net))+10)

En projet ! élaguer les branches légères ou le bruit de fond des cooccurrences inappropriées