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")
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'.
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
Liens externes