Twitter est un réseau social créé en 2006 par Jack Dorsey, Noah Glass, Biz Stone et Evan Williams. Twitter fournit une plateforme pour partager et recevoir des messages de manière rapide, avec plus de 500 millions de tweets envoyés par jour. Le réseau social est identifié internationalement par son logo d’oiseau populairement connu sous le nom de « Larry l’oiseau» inspiré par Larry Bird des Boston Celtics de la NBA. Twitter est un service de réseautage social et de microblogging, permettant aux utilisateurs enregistrés de lire et d’afficher des messages courts, appelés tweets. Ces messages sont limités à 140 caractères.
Dans ce projet, nous allons utiliser les informations mises à disposition par Twitter afin d’étudier un phénomène qui fait l’actualité en France : le mouvement des gilets jaunes. L’objectif de ce projet est de répondre à la problématique suivante : « Quel est le profil politique des manifestants du mouvement des gilets jaunes ? »
Afin de répondre à cette question nous allons effectuer Une analyse textuelle du contenu des tweets des principaux représentants des parties majeurs en france ainsi que les représentants des gilets jaunes. Nous allons essayer de trouver un lien entre ce mouvement et les parties politiques avec les méthodes suivantes: ACP, kmeans,nuage de mots, analyse de l’état émotionnel d’un utilisateur…)
Pour effectuer ces analyses, nous utilisons un package R : twitteR. De plus, Twitter autorise les utilisateurs qui en font la demande de récupérer des données via des APIs. Une fois notre compte validé, il nous a suffi de renseigner les paramètres suivants : api_key, api_secret, access_token, access_token_secret qui sont fournis par l’API Twitter pour récupérer des tweets.
A prendre en compte: À l’origine plutôt ouvert, le réseau social est devenu de plus en plus fermé sur l’utilisation d’APIs (indispensable afin de récolter des données tels que des tweets). Cette stratégie a entraîné la fermeture de plusieurs projets tiers, et notamment de clients Twitter, ou d’outils d’analyse. Dernièrement, Twitter a supprimé l’API « compteur de Tweets » permettant aux médias d’indiquer dans les articles combien de fois un article avait été partagé. Durant notre projet, on a donc été limité par des restrictions tels que la quantité de tweets récoltés, l’intervalle de temps sur laquelle on peut les obtenir, la géolocalisation etc…
Durant ce projet les packages suivants seront utilisés:
library(NLP)
library(twitteR)
library(stringr)
library(tm)
library(RColorBrewer)
library(wordcloud)
library(SnowballC)
library(openxlsx)
library(cluster)
library(FactoMineR)
library(ggplot2)
library(syuzhet)
library(stringi)
library(topicmodels)
Notre stratégie: Cibler les comptes Twitter des représentants des principaux parties politiques ainsi que ceux du mouvement des gilets jaunes. Pour cela nous allons essayer de récupérer les 900 derniers tweets par groupe avec les codes suivants:
A noté: Il a été facile d’avoir 900 tweets de la part de politiciens. il nous a suffi de récupérer 300 dernier tweets de 3 politiciens. Pour les gilets jaunes il nous a fallu 6 comptes twitter pour arriver à 889 tweets.
## Récupération tweets des Gilets Jaunes et conversion en dataframe
#Jerome Rodrigues
JR_tweet = userTimeline("J_Rodrigues_Off", n=300, includeRts = TRUE)
JR_tweet_df = twListToDF(JR_tweet)
#Maxime Nicolle
Nicolle_tweet = userTimeline("FlyRiderGj", n=300, includeRts = TRUE)
Nicolle_tweet_df = twListToDF(Nicolle_tweet)
#Priscillia Ludosky
Ludosky_tweet = userTimeline("PLudosky", n=300, includeRts = TRUE)
Ludosky_tweet_df = twListToDF(Ludosky_tweet)
#François Boulo
FB_tweet = userTimeline("BouloGiletJaune", n=300, includeRts = TRUE)
FB_tweet_df = twListToDF(FB_tweet)
#Benjamin cauchy
BC_tweet = userTimeline("BenjaminCauchy", n=300, includeRts = TRUE)
BC_tweet_df = twListToDF(BC_tweet)
#Eric Drouet
Drouet_tweet = userTimeline("EricDrouetGJ", n=300, includeRts = TRUE)
Drouet_tweet_df = twListToDF(BC_tweet)
# Aggrégation des tweets_df des GJ
GJ_tweet_df=rbind(JR_tweet_df,Nicolle_tweet_df,Ludosky_tweet_df,FB_tweet_df,BC_tweet_df,Drouet_tweet_df )
## Récupération tweets du parti Les républicains et conversion en dataframe
#Edouard philippe
EP_tweet = userTimeline("EPhilippePM", n=300, includeRts = TRUE)
EP_tweet_df = twListToDF(EP_tweet)
#Alain Juppe
AJ_tweet = userTimeline("alainjuppe", n=300, includeRts = TRUE)
AJ_tweet_df = twListToDF(AJ_tweet)
#Laurent Wauquiez
LW_tweet = userTimeline("laurentwauquiez", n=300, includeRts = TRUE)
LW_tweet_df = twListToDF(LW_tweet)
# Aggrégation des tweets_df des LR
LR_tweet_df=rbind(EP_tweet_df,AJ_tweet_df,LW_tweet_df)
## Récupération tweets du parti La République En Marche et conversion en dataframe
#Benjamin Griveaux
BG_tweet = userTimeline("BGriveaux", n=300, includeRts = TRUE)
BG_tweet_df = twListToDF(BG_tweet)
#Emmanuel Macron
EM_tweet = userTimeline("EmmanuelMacron", n=300, includeRts = TRUE)
EM_tweet_df = twListToDF(EM_tweet)
#Christophe Castaner
CC_tweet = userTimeline("CCastaner", n=300, includeRts = TRUE)
CC_tweet_df = twListToDF(CC_tweet)
# Aggrégation des tweets_df des RM
RM_tweet_df=rbind(BG_tweet_df,EM_tweet_df,CC_tweet_df)
## Récupération tweets du parti La France Insoumise et conversion en dataframe
#Jean-Luc Mélenchon
JM_tweet = userTimeline("JLMelenchon", n=300, includeRts = TRUE)
JM_tweet_df = twListToDF(JM_tweet)
#Adrien Quatennens
AQ_tweet = userTimeline("AQuatennens", n=300, includeRts = TRUE)
AQ_tweet_df = twListToDF(AQ_tweet)
#Alexis Corbière
AC_tweet = userTimeline("alexiscorbiere", n=300, includeRts = TRUE)
AC_tweet_df = twListToDF(AC_tweet)
# Aggrégation des tweets_df des FI
FI_tweet_df=rbind(JM_tweet_df,AQ_tweet_df,AC_tweet_df)
## Récupération tweets du parti Le Rassemblement National et conversion en dataframe
#Marine Le Pen
MP_tweet = userTimeline("MLP_officiel", n=300, includeRts = TRUE)
MP_tweet_df = twListToDF(MP_tweet)
#Gilbert Collard
GC_tweet = userTimeline("GilbertCollard", n=300, includeRts = TRUE)
GC_tweet_df = twListToDF(GC_tweet)
#Jordan Bardella
JB_tweet = userTimeline("J_Bardella", n=300, includeRts = TRUE)
JB_tweet_df = twListToDF(JB_tweet)
# Aggrégation des tweets_df des RN
RN_tweet_df=rbind(MP_tweet_df,GC_tweet_df,JB_tweet_df)
Ci-dessous un tableau récapitulatif du nombre de tweets récupérés ainsi que leur provenance (le code couleur sera respecté tout au long du rapport):
source : https://www.math.u-bordeaux.fr/~arichou/site/projets2017/Camara-Canal.html
clean.text = function(x_text, word = stopwords("fr"), lang = "fr")
{
if (lang == "fr"){
x_text = str_replace_all(x_text, "à", "a")
x_text = str_replace_all(x_text, "â", "a")
x_text = str_replace_all(x_text, "ç", "c")
x_text = str_replace_all(x_text, "é", "e")
x_text = str_replace_all(x_text, "è", "e")
x_text = str_replace_all(x_text, "ê", "e")
x_text = str_replace_all(x_text, "ù", "u")
x_text = str_replace_all(x_text, "ï", "i")
x_text = str_replace_all(x_text, "û", "u")
x_text = str_replace_all(x_text, "ô", "o")
x_text = str_replace_all(x_text, "î", "i")
}
x_text = sapply(x_text,function(x) iconv(x, "latin1", "ASCII", sub=""))
x_text = str_replace_all(x_text, "(RT|via)((?:\\b\\W*@\\w+)+)", " ")
x_text = str_replace_all(x_text, "(\r)", " ")
x_text = str_replace_all(x_text, "@\\w+", " ")
x_text = str_replace_all(x_text, "http.+", " ")
x_text = str_replace_all(x_text, "[[:punct:]]", " ")
x_text = str_replace_all(x_text, "[[:digit:]]", " ")
x_text = str_replace_all(x_text, "[ \t]{2,}", " ")
x_text = str_replace_all(x_text, "^\\s+|\\s+$", "")
x_text = str_replace_all(x_text, "[^\x01-\x7F<>+]", "")
x_text = str_trim(x_text)
return(x_text)
}
#Récupération texte de chaque data frame
LR_text=LR_tweet_df$text
RM_text=RM_tweet_df$text
FI_text=FI_tweet_df$text
RN_text=RN_tweet_df$text
GJ_text=GJ_tweet_df$text
#Nettoyage des textes
LR_text=clean.text(LR_text)
RM_text=clean.text(RM_text)
FI_text=clean.text(FI_text)
RN_text=clean.text(RN_text)
GJ_text=clean.text(GJ_text)
Les tweets sont maintenant “propres”, nous pouvons commençer à les analyser!
########################## ACP ##################################
# Couper la liste des tweets de chaque parti en 10 sous-listes aléatoirement
# Cela veut dire qu'on regroupe les tweets par packet de 90.
set.seed(75)
n=10
FISplit<- split(FI_text, sample(rep(1:n, length.out = length(FI_text))))
RMSplit<- split(RM_text, sample(rep(1:n, length.out = length(RM_text))))
LRSplit <- split(LR_text, sample(rep(1:n, length.out = length(LR_text))))
RNSplit <- split(RN_text, sample(rep(1:n, length.out = length(RN_text))))
GJSplit <- split(GJ_text, sample(rep(1:n, length.out = length(GJ_text))))
# Aggréger les tweets d'un même groupe de tweets
FIGroup <- lapply(FISplit , function(x) paste(x, collapse = " "))
RMGroup <- lapply(RMSplit , function(x) paste(x, collapse = " "))
LRGroup <- lapply(LRSplit , function(x) paste(x, collapse = " "))
RNGroup <- lapply(RNSplit , function(x) paste(x, collapse = " "))
GJGroup <- lapply(GJSplit , function(x) paste(x, collapse = " "))
all <- c(FIGroup, RMGroup, LRGroup, RNGroup, GJGroup)
# corpus
corpus.all = Corpus(VectorSource(all))
# convert to lower case
corpus.all= tm_map(corpus.all, tolower)
# remove stoprwords
corpus.all = tm_map(corpus.all, removeWords, c(stopwords("french")))
# remove extra white-spaces
corpus.all = tm_map(corpus.all, stripWhitespace)
# term-document matrix
tdm = TermDocumentMatrix(corpus.all)
# convert as matrix
all_matrix = as.matrix(tdm)
all_matrix_t=t(all_matrix)
#### Nous transformons cette matrice pour obtenir la fréquence des mots,
#####autrement dit le nombre d'occurrences divisé par le nombre total d'un groupe de tweets donné.
somme_ligne = rowSums(all_matrix_t)
all_matrix_freq=all_matrix_t/somme_ligne
label=c(rep("FI",10),rep("RM",10),rep("LR",10),rep("RN",10),rep("GJ",10))
class(all_matrix_freq) <- "numeric"
all_dataframe <- as.data.frame(all_matrix_freq)
all_dataframe_label=cbind(all_dataframe,label)
#ACP: Chaque observation correponds à un regroupement de 90 tweets labélisés par partie. Ici les variables sont les fréquences des mots utilités dans le groupe de tweet.
library(FactoMineR)
#library(PCAmixdata)
res <- PCA(all_dataframe_label, quali.sup=9226, graph=FALSE, scale.unit = TRUE, ncp = 10) # original data in input
plot(res,choix="ind", axes = c(1,2), cex=1.5,title="", habillage = 9226, label="none", invisible = "quali")
title(main = "ACP de tweets sur les dimensions 1 et 2")
plot(res,choix="ind", axes = c(1,3), cex=1.5,title="", habillage = 9226,label="none")
title(main = "ACP de tweets sur les dimensions 1 et 3")
plot(res,choix="ind", axes = c(2,3), cex=1.5,title="", habillage = 9226, label="none")
title(main = "ACP de tweets sur les dimensions 2 et 3")
Résultat: SUr les dimensions 1-2, 1-3 ou 2-3 les tweets du parti de la france insoumise, du rassemblement national ainsi que ds gilets jaunes sont semblables (proche sur les plans). Ils sembleraient donc que les Gilets Jaunes partagent les mêmes idées que les partis extrêmes en France. Comment montrer ces liens?
Une première approche est de comparer les mots les plus utilisés par les diffénts groupes:
################### LR fréquence mots ######################
# corpus
LR_corpus = Corpus(VectorSource(LR_text))
# convertir en minuscule
LR_corpus = tm_map(LR_corpus, tolower)
# supprimer les mots courants
LR_corpus = tm_map(LR_corpus, removeWords, stopwords("french"))
# Supprimer les extra espaces blancs
LR_corpus = tm_map(LR_corpus, stripWhitespace)
# term-document matrix
tdm = TermDocumentMatrix(LR_corpus)
# convertir en matrice
LR_matrix = as.matrix(tdm)
wf = rowSums(LR_matrix) #word frequence
LR_matrix_wf=cbind(LR_matrix,wf)
LR_matrix_wf_sort <- LR_matrix_wf[order(wf,decreasing=T),] # tri décroissant en fonction de la colonne wf
#top-40 par fréquence
top40 = head(LR_matrix_wf_sort[,901], 40)
# barplot
barplot(top40, border=NA, las=2, main="Top 40 des mots les plus utilisés par les représentants des républicains", cex.main=1)
# corpus
RM_corpus = Corpus(VectorSource(RM_text))
# convert to lower case
RM_corpus = tm_map(RM_corpus, tolower)
# remove stoprwords
RM_corpus = tm_map(RM_corpus, removeWords, c(stopwords("french"),stopwords("english"), "giletsjaunes"))
# remove extra white-spaces
RM_corpus = tm_map(RM_corpus, stripWhitespace)
# term-document matrix
tdm = TermDocumentMatrix(RM_corpus)
# convert as matrix
RM_matrix = as.matrix(tdm)
wf = rowSums(RM_matrix) #wordfrequence
RM_matrix_wf=cbind(RM_matrix,wf)
RM_matrix_wf_sort <- RM_matrix_wf[order(wf,decreasing=T),] # tri décroissant en fonction de la colonne wf
#top-40 most frequent
top40 = head(RM_matrix_wf_sort[,901], 40)
# barplot
barplot(top40, border=NA, las=2, main="Top 40 des mots les plus utilisés par les représentants de la république en marche", cex.main=1)
# corpus
FI_corpus = Corpus(VectorSource(FI_text))
# convert to lower case
FI_corpus = tm_map(FI_corpus, tolower)
# remove stoprwords
FI_corpus = tm_map(FI_corpus, removeWords, stopwords("french"))
# remove extra white-spaces
FI_corpus = tm_map(FI_corpus, stripWhitespace)
# term-document matrix
tdm = TermDocumentMatrix(FI_corpus)
# convert as matrix
FI_matrix = as.matrix(tdm)
wf = rowSums(FI_matrix) #wordfrequence
FI_matrix_wf=cbind(RM_matrix,wf)
FI_matrix_wf_sort <- FI_matrix_wf[order(wf,decreasing=T),] # tri décroissant en fonction de la colonne wf
#top-40 most frequent
top40 = head(FI_matrix_wf_sort[,901], 40)
# barplot
barplot(top40, border=NA, las=2, main="Top 40 des mots les plus utilisés par les représentants de la france insoumise", cex.main=1)
# corpus
RN_corpus = Corpus(VectorSource(RN_text))
# convert to lower case
RN_corpus = tm_map(RN_corpus, tolower)
# remove stoprwords
RN_corpus = tm_map(RN_corpus, removeWords,stopwords("french"))
# remove extra white-spaces
RN_corpus = tm_map(RN_corpus, stripWhitespace)
# term-document matrix
tdm = TermDocumentMatrix(RN_corpus)
# convert as matrix
RN_matrix = as.matrix(tdm)
wf = rowSums(RN_matrix) #wordfrequence
RN_matrix_wf=cbind(RN_matrix,wf)
RN_matrix_wf_sort <- RN_matrix_wf[order(wf,decreasing=T),] # tri décroissant en fonction de la colonne wf
#top-40 most frequent
RN_matrix_wf_sort <- RN_matrix_wf_sort[,901]
top40 = head(RN_matrix_wf_sort, 40)
# barplot
barplot(top40, border=NA, las=2, main="Top 40 des mots les plus utilisés par les représentants du rassemblement national", cex.main=1)
# corpus
GJ_corpus = Corpus(VectorSource(GJ_text))
# convert to lower case
GJ_corpus = tm_map(GJ_corpus, tolower)
# remove stoprwords
GJ_corpus = tm_map(GJ_corpus, removeWords,stopwords("french"))
# remove extra white-spaces
GJ_corpus = tm_map(GJ_corpus, stripWhitespace)
# term-document matrix
tdm = TermDocumentMatrix(GJ_corpus)
# convert as matrix
GJ_matrix = as.matrix(tdm)
wf = rowSums(GJ_matrix) #wordfrequence
GJ_matrix_wf=cbind(GJ_matrix,wf)
GJ_matrix_wf_sort <- GJ_matrix_wf[order(wf,decreasing=T),] # tri décroissant en fonction de la colonne wf
#top-40 most frequent
GJ_matrix_wf_sort <- GJ_matrix_wf_sort[,890]
top40 = head(GJ_matrix_wf_sort, 40)
# barplot
barplot(top40, border=NA, las=2, main="Top 40 des mots les plus utilisés par les représentants des gilets jaunes", cex.main=1)
Résultats: Il est extremement difficile d’analyser le lien entre les différents groupes à partir des top mots représenté en barplot. Cette approche n’est pas appropriée à notre problème. Il existe un outil plus efficace appelé le nuage de mot qui est plus adapté.
Etudions les mots utilisés par les représentants des gilets jaunes.
Drouet_text = Drouet_tweet_df$text
Ludosky_text = Ludosky_tweet_df$text
Nicolle_text = Nicolle_tweet_df$text
JR_text = JR_tweet_df$text
FB_text = FB_tweet_df$text
BC_text = BC_tweet_df$text
Drouet_text=clean.text(Drouet_text)
Ludosky_text=clean.text(Ludosky_text)
Nicolle_text=clean.text(Nicolle_text)
JR_text=clean.text(JR_text)
FB_text=clean.text(FB_text)
BC_text=clean.text(BC_text)
Drouet = paste(Drouet_text, collapse=" ")
Ludosky = paste(Ludosky_text, collapse=" ")
Nicolle = paste(Nicolle_text, collapse=" ")
Jerome = paste(JR_text, collapse=" ")
Francois=paste(FB_text, collapse=" ")
Benjamin=paste(BC_text, collapse=" ")
all = c(Drouet, Ludosky, Nicolle,Jerome,Francois,Benjamin)
corpus = Corpus(VectorSource(all))
corpus = tm_map(corpus, removeWords, stopwords("french"))
tdm = TermDocumentMatrix(corpus)
tdm = as.matrix(tdm)
# add column names
colnames(tdm) = c("Eric Drouet", "Priscillia Ludosky", "Maxime Nicolle","Jerome Rodriguez","Francois Boulo","Benjamin Cauchy")
comparison.cloud(tdm, random.order=FALSE, colors = c("#00B2FF", "red", "#FF0099","#000000","green","orange"), title.size=1.5, max.words=250)
Le nuage de comparaison nous montre la différence de fréquence des mots utilisés entre différents acteurs. Nous allons, ci-dessous, utiliser cette méthode pour comparer les fréquences des mots utilisés par les différents partis politiques avec ceux des gilets jaunes. Nous traçerons aussi le nuage de mots communs afin de voir la fréquence des mots communs utilisés, qui est plus adapté pour expliquer les liens entre des tweets de différents groupes.
LR = paste(LR_text, collapse=" ")
GJ = paste(GJ_text, collapse=" ")
all = c(LR, GJ)
corpus = Corpus(VectorSource(all))
corpus = tm_map(corpus, removeWords, stopwords("french"))
tdm = TermDocumentMatrix(corpus)
tdm = as.matrix(tdm)
# add column names
colnames(tdm) = c("LR", "GJ")
# comparison cloud
#dev.new(width = 550, height = 500, unit = "px")
comparison.cloud(tdm, random.order=FALSE, colors = c("#10A91B", "red"), title.size=1.5, max.words=250)
commonality.cloud(tdm, random.order=FALSE, colors = c("orange"), title.size=1.5, max.words=250)
RM = paste(RM_text, collapse=" ")
GJ = paste(GJ_text, collapse=" ")
all = c(RM, GJ)
corpus = Corpus(VectorSource(all))
corpus = tm_map(corpus, removeWords, stopwords("french"))
tdm = TermDocumentMatrix(corpus)
tdm = as.matrix(tdm)
# add column names
colnames(tdm) = c("RM", "GJ")
# comparison cloud
#dev.new(width = 550, height = 500, unit = "px")
comparison.cloud(tdm, random.order=FALSE, colors = c("#102AA9", "red"), title.size=1.5, max.words=250)
commonality.cloud(tdm, random.order=FALSE, colors = c("orange"), title.size=1.5, max.words=250)
FI = paste(FI_text, collapse=" ")
GJ = paste(GJ_text, collapse=" ")
all = c(FI, GJ)
corpus = Corpus(VectorSource(all))
corpus = tm_map(corpus, removeWords, stopwords("french"))
tdm = TermDocumentMatrix(corpus)
tdm = as.matrix(tdm)
# add column names
colnames(tdm) = c("FI", "GJ")
# comparison cloud
#dev.new(width = 550, height = 500, unit = "px")
comparison.cloud(tdm, random.order=FALSE, colors = c("#000000", "red"), title.size=1.5, max.words=250)
commonality.cloud(tdm, random.order=FALSE, colors = c("orange"), title.size=1.5, max.words=250)
RN = paste(RN_text, collapse=" ")
GJ = paste(GJ_text, collapse=" ")
all = c(RN, GJ)
corpus = Corpus(VectorSource(all))
corpus = tm_map(corpus, removeWords, stopwords("french"))
tdm = TermDocumentMatrix(corpus)
tdm = as.matrix(tdm)
# add column names
colnames(tdm) = c("RN", "GJ")
# comparison cloud
#dev.new(width = 550, height = 500, unit = "px")
comparison.cloud(tdm, random.order=FALSE, colors = c("#31D4C9", "red"), title.size=1.5, max.words=250)
commonality.cloud(tdm, random.order=FALSE, colors = c("orange"), title.size=1.5, max.words=250)
Résultat: On peut observer à travers les différents nuages de comparaisons (1er plot de chaque partie), les différences de fréquences des mots utilisés entre chaque partis politiques et les gilets jaunes. Le nuage de mots communs (2 eme plot de chaque partie), nous montre le résultat le plus intérressant: Le terme “giletsjaunes” est au centre de chaque nuage pour le Rassemblement national ainsi que pour la France insoumise. Ce qui signifie que ce terme est très utilisé par ces 2 parties. Ce qui peut expliquer leurs rapprochements sur l’ACP. Isolons ce terme, et traçons un barplot de ses occurences dans les tweets récoltés:
barplot(occ.data$Occurence_term_giletsjaunes, names = occ.data$partie,
xlab = "Parties", ylab = "Occurences terme giletsjaunes",
main = "Nombre de fois le terme 'giletsjaunes' a été utilisé par partie")
Ce barplot confirme que les gilets Jaunes sont très soutenus par les partis comme le Rassemblement National et la France Insoumise. Le score de la république en marche et des républicains est très bas, ces partis préfèrent donc ne pas nommer les manifestants par les “gilets jaunes” dans leur tweet.
Nous allons essayer dans cette partie de classifié les top 200 mots utilisés par chaque groupe en des thèmes communs en utilisant une analyse factorielle et de l’apprentissage non supervisé avec les Kmeans. Nous déterminerons les nombres de classes avec la “méthode des coudes” trouvé sur https://thinkr.fr/premiers-pas-en-machine-learning-avec-r-volume-3-k-means-clustering/
# dataframe des 200 termes les plus fréquents
matrix_top200 = LR_matrix_wf_sort[1:200,-901]
# Elimine tous le colonnes avec que des zéros
matrix_top200 = matrix_top200[,colSums(matrix_top200)!=0]
#Nbr cluster?
ratio_ss <- data.frame(cluster = seq(from = 1, to = 30, by = 1))
for (k in 1:30) {
km_model <- kmeans(matrix_top200, k, nstart = 20)
ratio_ss$ratio[k] <- km_model$tot.withinss / km_model$totss
}
ggplot(ratio_ss, aes(cluster, ratio)) + ggtitle("Technique coude pour trouver le nombre de cluster k optimale")+
geom_line() +
geom_point()
# Analyse factorielle
result_ca = CA(matrix_top200, graph=FALSE)
# Découpage en 8 clusters
k = 4
# Kmeans
result_k_means = kmeans(result_ca$row$coord[,1:2], k)
# Obtenir clusters
clusters = result_k_means$cluster
# création data frame
rei_words_df = data.frame(
words = rownames(matrix_top200),
dimatrix_top200 = result_ca$row$coord[,1],
dim2 = result_ca$row$coord[,2],
freq = rowSums(matrix_top200),
cluster = as.factor(clusters))
# plot
ggplot(rei_words_df, aes(x=dimatrix_top200, y=dim2, label=words)) + ggtitle("Analyse factorielle des principaux mots de tweets des républicains")+
geom_text(aes(size=freq, colour=cluster), alpha=0.7) +
scale_size_continuous(breaks=seq(20,80,by=10), range=c(3,8)) +
scale_colour_manual(values=brewer.pal(8, "Dark2")) +
labs(x="", y="")
# dataframe des 200 termes les plus fréquents
matrix_top200 = RM_matrix_wf_sort[1:200,-901]
# Elimine tous le colonnes avec que des zéros
matrix_top200 = matrix_top200[,colSums(matrix_top200)!=0]
#Nbr cluster?
ratio_ss <- data.frame(cluster = seq(from = 1, to = 30, by = 1))
for (k in 1:30) {
km_model <- kmeans(matrix_top200, k, nstart = 20)
ratio_ss$ratio[k] <- km_model$tot.withinss / km_model$totss
}
ggplot(ratio_ss, aes(cluster, ratio)) + ggtitle("Technique coude pour trouver le nombre de cluster k optimale")+
geom_line() +
geom_point()
# Analyse factorielle
result_ca = CA(matrix_top200, graph=FALSE)
# Découpage en 8 clusters
k = 3
# Kmeans
result_k_means = kmeans(result_ca$row$coord[,1:2], k)
# Obtenir clusters
clusters = result_k_means$cluster
# création data frame
rei_words_df = data.frame(
words = rownames(matrix_top200),
dimatrix_top200 = result_ca$row$coord[,1],
dim2 = result_ca$row$coord[,2],
freq = rowSums(matrix_top200),
cluster = as.factor(clusters))
# plot
ggplot(rei_words_df, aes(x=dimatrix_top200, y=dim2, label=words)) + ggtitle("Analyse factorielle des principaux mots de tweets de la république en marche")+
geom_text(aes(size=freq, colour=cluster), alpha=0.7) +
scale_size_continuous(breaks=seq(20,80,by=10), range=c(3,8)) +
scale_colour_manual(values=brewer.pal(8, "Dark2")) +
labs(x="", y="")
# dataframe des 200 termes les plus fréquents
matrix_top200 = FI_matrix_wf_sort[1:200,-901]
# Elimine tous le colonnes avec que des zéros
matrix_top200 = matrix_top200[,colSums(matrix_top200)!=0]
#Nbr cluster?
ratio_ss <- data.frame(cluster = seq(from = 1, to = 30, by = 1))
for (k in 1:30) {
km_model <- kmeans(matrix_top200, k, nstart = 20)
ratio_ss$ratio[k] <- km_model$tot.withinss / km_model$totss
}
ggplot(ratio_ss, aes(cluster, ratio)) + ggtitle("Technique coude pour trouver le nombre de cluster k optimale")+
geom_line() +
geom_point()
# Analyse factorielle
result_ca = CA(matrix_top200, graph=FALSE)
# Découpage en 8 clusters
k = 4
# Kmeans
result_k_means = kmeans(result_ca$row$coord[,1:2], k)
# Obtenir clusters
clusters = result_k_means$cluster
# création data frame
rei_words_df = data.frame(
words = rownames(matrix_top200),
dimatrix_top200 = result_ca$row$coord[,1],
dim2 = result_ca$row$coord[,2],
freq = rowSums(matrix_top200),
cluster = as.factor(clusters))
# plot
ggplot(rei_words_df, aes(x=dimatrix_top200, y=dim2, label=words)) + ggtitle("Analyse factorielle des principaux mots de tweets de la france insoumise")+
geom_text(aes(size=freq, colour=cluster), alpha=0.7) +
scale_size_continuous(breaks=seq(20,80,by=10), range=c(3,8)) +
scale_colour_manual(values=brewer.pal(8, "Dark2")) +
labs(x="", y="")
# dataframe des 200 termes les plus fréquents
matrix_top200 = RN_matrix_wf[order(wf,decreasing=T),]
matrix_top200 = matrix_top200[1:200,-901]
# Elimine tous le colonnes avec que des zéros
matrix_top200 = matrix_top200[,colSums(matrix_top200)!=0]
#Nbr cluster?
ratio_ss <- data.frame(cluster = seq(from = 1, to = 30, by = 1))
for (k in 1:30) {
km_model <- kmeans(matrix_top200, k, nstart = 20)
ratio_ss$ratio[k] <- km_model$tot.withinss / km_model$totss
}
ggplot(ratio_ss, aes(cluster, ratio)) + ggtitle("Technique coude pour trouver le nombre de cluster k optimale")+
geom_line() +
geom_point()
# Analyse factorielle
result_ca = CA(matrix_top200, graph=FALSE)
# Découpage en 3 clusters
k = 3
# Kmeans
result_k_means = kmeans(result_ca$row$coord[,1:2], k)
# Obtenir clusters
clusters = result_k_means$cluster
# création data frame
rei_words_df = data.frame(
words = rownames(matrix_top200),
dimatrix_top200 = result_ca$row$coord[,1],
dim2 = result_ca$row$coord[,2],
freq = rowSums(matrix_top200),
cluster = as.factor(clusters))
# plot
ggplot(rei_words_df, aes(x=dimatrix_top200, y=dim2, label=words)) + ggtitle("Analyse factorielle des principaux mots de tweets du rassemblement national")+
geom_text(aes(size=freq, colour=cluster), alpha=0.7) +
scale_size_continuous(breaks=seq(20,80,by=10), range=c(3,8)) +
scale_colour_manual(values=brewer.pal(8, "Dark2")) +
labs(x="", y="")
# dataframe des 200 termes les plus fréquents
matrix_top200 = GJ_matrix_wf[order(wf,decreasing=T),]
matrix_top200 = matrix_top200[1:200,-853]
# Elimine tous le colonnes avec que des zéros
matrix_top200 = matrix_top200[,colSums(matrix_top200)!=0]
#Nbr cluster?
ratio_ss <- data.frame(cluster = seq(from = 1, to = 9, by = 1))
for (k in 1:9) {
km_model <- kmeans(matrix_top200, k, nstart = 20)
ratio_ss$ratio[k] <- km_model$tot.withinss / km_model$totss
}
ggplot(ratio_ss, aes(cluster, ratio)) + ggtitle("Technique coude pour trouver le nombre de cluster k optimale")+
geom_line() +
geom_point()
# Analyse factorielle
result_ca = CA(matrix_top200, graph=FALSE)
# Découpage en 2 clusters
k = 2
# Kmeans
result_k_means = kmeans(result_ca$row$coord[,1:2], k)
# Obtenir clusters
clusters = result_k_means$cluster
# création data frame
rei_words_df = data.frame(
words = rownames(matrix_top200),
dimatrix_top200 = result_ca$row$coord[,1],
dim2 = result_ca$row$coord[,2],
freq = rowSums(matrix_top200),
cluster = as.factor(clusters))
# plot
ggplot(rei_words_df, aes(x=dimatrix_top200, y=dim2, label=words)) + ggtitle("Analyse factorielle des principaux mots de tweets des gilets jaunes")+
geom_text(aes(size=freq, colour=cluster), alpha=0.7) +
scale_size_continuous(breaks=seq(20,80,by=10), range=c(3,8)) +
scale_colour_manual(values=brewer.pal(8, "Dark2")) +
labs(x="", y="")
Résultat: Des groupes de mots apparaissent. Mais il est difficile de les étiquetter. Pour cela nous pouvons nous aider des principaux Hashtags liés aux tweets récupérés.
L’analyse de sentiments (Sentiment Analysis) est l’un des thèmes les plus fascinants dans l’analyse textuelle de tweets. Cette analyse permet de ressortir l’opinion et l’attitude d’une population concernant un sujet particulier, en effet Twitter permet d’acceder à un échantillon relativement significatif de la population sans pour autant avoir besoin de créer de sondages. Cependant la détéction d’émotions est une tâche très difficile notemment à cause de la nature complèxe de chaque langue, et du type de langages et des nuances présentes dans les tweets de chacun. L’analyse de sentiment peut avoir differentes fonction : une entreprise qui cherche à comprendre l’avis d’une population sur un produit ou un service peut notemment avoir recours à une analyse de sentiments.
Nous allons, dans cette partie proceder à une analyse des sentiments reflètent les Tweets récuperés à l’aide de la fonction “searchTwitter” du package “twitteR”. Pour ce faire, nous aurons besoin du package “syuzhet”.
Nous récuperons les Tweets en français, ces Tweets vont être mis en forme et prétraités pour pouvoir extraire l’information voulue, en l’occurence ici “les scores” des sentiments généraux derrière les Tweets #giletsjaunes.
tweets_gj <- searchTwitter("#giletsjaunes", n=1000,lang = "fr")
gj_tweets <- twListToDF(tweets_gj)
gj_text<- gj_tweets$text
Nous utilisons la fonction “get_nrc_sentiment” du package “syuzhet”, cette dernière permet d’attribuer à chaque Tweet une note sur chacun des sentiments.
Cette fonction procède en traitant chaque Tweet et en faisant appel au dictionnaire “NRC” et en évaluant la presence de mots qui reflètent l’un des 10 sentiments :
Ici nous avons effectué une analyse sur #giletsjaunes, c’est un mouvement qui évoque diverses émotions chez la populations, que ce soit de part leur affiliation politique, ou l’impact qu’a eu ce mouvement sur leurs quotidien. Il est alors fascinant d’avoir une idée sur l’opinion des personnes qui ont tweeté au moment de notre analyse.
ggplot(data=Sentimentscores_gj,aes(x=sentiment,y=Score))+geom_bar(aes(fill=sentiment),stat = "identity")+
theme(legend.position="none")+
xlab("Sentiments")+ylab("scores")+ggtitle("Sentiments des gens qui twittent #giletsjaunes")
Il peut aussi être intéressant de voir les differents “score” des sentiments du dictionnaire NRC dans differentes régions, ici nous avons éffectué l’étude dans la région parisienne, dans un rayon de 100km avec l’argument “geocode” de la fonction searchTwitter.
tweets_gj <- searchTwitter("#giletsjaunes", n=1000,lang = "fr", geocode = "48.8,2.3,100km")
gj_tweets <- twListToDF(tweets_gj)
gj_text<- gj_tweets$text
ggplot(data=Sentimentscores_gj,aes(x=sentiment,y=Score))+geom_bar(aes(fill=sentiment),stat = "identity")+
theme(legend.position="none")+
xlab("Sentiments")+ylab("scores")+ggtitle("Sentiments des gens qui twittent #giletsjaunes à Paris")
Nous effectuons l’analyse de sentiments cette fois dans un rayon de 100km autour de Bordeaux. Il est alors intéressant de voir les variations des scores des différents sentiments en fonction de la région.
tweets_gj <- searchTwitter("#giletsjaunes", n=1000,lang = "fr", geocode = "44.8637065,-0.6561808,100km")
gj_tweets <- twListToDF(tweets_gj)
gj_text<- gj_tweets$text
ggplot(data=Sentimentscores_gj,aes(x=sentiment,y=Score))+geom_bar(aes(fill=sentiment),stat = "identity")+
theme(legend.position="none")+
xlab("Sentiments")+ylab("scores")+ggtitle("Sentiments des gens qui twittent #giletsjaunes à Bordeaux")
Le nuage de comparaison des mots selont le sentiments, permet de visualiser les mots qui reviennent le plus dans les Tweets exprimant chaque émotion. En effet ceci peut nous permettre de voir quels sont les thèmes associés avec chaque sentiment.
# Create comparison word cloud data
wordcloud_tweet = c(
paste(gj_text[mysentiment_gj$anger > 0], collapse=" "),
paste(gj_text[mysentiment_gj$anticipation > 0], collapse=" "),
paste(gj_text[mysentiment_gj$disgust > 0], collapse=" "),
paste(gj_text[mysentiment_gj$fear > 0], collapse=" "),
paste(gj_text[mysentiment_gj$joy > 0], collapse=" "),
paste(gj_text[mysentiment_gj$sadness > 0], collapse=" "),
paste(gj_text[mysentiment_gj$surprise > 0], collapse=" "),
paste(gj_text[mysentiment_gj$trust > 0], collapse=" ")
)
# create corpus
corpus = Corpus(VectorSource(wordcloud_tweet))
corpus = tm_map(corpus, removeWords, stopwords("french"))
# create document term matrix
tdm = TermDocumentMatrix(corpus, control = list(tolower = TRUE, stopwords = "fr"))
# convert as matrix
tdm = as.matrix(tdm)
tdmnew <- tdm[nchar(rownames(tdm)) < 11,]
# column name binding
colnames(tdm) = c('anger', 'anticipation', 'disgust', 'fear', 'joy', 'sadness', 'surprise', 'trust')
colnames(tdmnew) <- colnames(tdm)
comparison.cloud(tdmnew, random.order=FALSE,
colors = c("#00B2FF", "red", "#FF0099", "#6600CC", "green", "orange", "blue", "brown"),
title.size=1, max.words=250, scale=c(2.5, 0.4),rot.per=0.4)
On peut voir, d’un côté, que la taille des mots reflète les différents scores des sentiments qu’on a vu plus haut, et d’un autre côté on peut voir quels mots apparaissent le plus dans les tweets qui correspondent à une émotion.
Par exemple, le mot “victimes” dans les tweets reflètant le dégoût ou encore le mot “cagoule” apparaissant dans les tweets reflètant la peur.
Nous pouvons effectuer la même étude pour le mouvement des foulards rouges.
On peut voir, que sur les tweets contenant #foulardsrouges, nous avons differents scores pour les sentiments analysés.
tweets_fr <- searchTwitter("#foulardsrouges", n=1000,lang = "fr")
fr_tweets <- twListToDF(tweets_fr)
fr_text<- fr_tweets$text
ggplot(data=Sentimentscores_fr,aes(x=sentiment,y=Score))+geom_bar(aes(fill=sentiment),stat = "identity")+
theme(legend.position="none")+
xlab("Sentiments")+ylab("scores")+ggtitle("Sentiments des gens qui twittent #foulardsrouges")
# Create comparison word cloud data
wordcloud_tweet = c(
paste(fr_text[mysentiment_fr$anger > 0], collapse=" "),
paste(fr_text[mysentiment_fr$anticipation > 0], collapse=" "),
paste(fr_text[mysentiment_fr$disgust > 0], collapse=" "),
paste(fr_text[mysentiment_fr$fear > 0], collapse=" "),
paste(fr_text[mysentiment_fr$joy > 0], collapse=" "),
paste(fr_text[mysentiment_fr$sadness > 0], collapse=" "),
paste(fr_text[mysentiment_fr$surprise > 0], collapse=" "),
paste(fr_text[mysentiment_fr$trust > 0], collapse=" ")
)
# create corpus
corpus = Corpus(VectorSource(wordcloud_tweet))
corpus = tm_map(corpus, removeWords, stopwords("french"))
# create document term matrix
tdm = TermDocumentMatrix(corpus, control = list(tolower = TRUE, stopwords = "fr"))
# convert as matrix
tdm = as.matrix(tdm)
tdmnew <- tdm[nchar(rownames(tdm)) < 11,]
# column name binding
colnames(tdm) = c('anger', 'anticipation', 'disgust', 'fear', 'joy', 'sadness', 'surprise', 'trust')
colnames(tdmnew) <- colnames(tdm)
comparison.cloud(tdmnew, random.order=FALSE,
colors = c("#00B2FF", "red", "#FF0099", "#6600CC", "green", "orange", "blue", "brown"),
title.size=1, max.words=250, scale=c(2.5, 0.4),rot.per=0.4)
La géolocalisation des tweets peut être un outil particulièrement efficace pour pouvoir situés les communauté qui sont les plus actives sur Twitter, en l’occurence les communauté qui s’exprime le plus sur un sujet particulier. Il était possible auparavant de récuperer les coordonées géographiques de l’émeteur du tweet, ce qui a naturèlment été considéré, entre autres, comme une atteinte à la vie privée des utilisateurs de Twitter.
Il éxiste cependant une méthode pour pouvoir géolocalisé les tweets, cette fois en utilisant la localisation indiquées par l’utilisateurs de Twitter sur son profil.
Cette méthode a nécéssité la familiarisation avec l’outil google
library(rtweet)
library(ggplot2)
library(ggmap)
library(data.table)
library(maps)
library(stringr)
source("https://raw.githubusercontent.com/dkahle/ggmap/897267760fb28550913f6a8435e18bb8c82c639e/R/helpers.R")
#Install / load "RJSONIO" package:
if (!require("RJSONIO")) {
install.packages("RJSONIO", repos="http://cran.rstudio.com/")
library("RJSONIO")
}
#Install key package helpers:
source("https://raw.githubusercontent.com/LucasPuente/geocoding/master/geocode_helpers.R")
#Install modified version of the geocode function
#(that now includes the api_key parameter):
source("https://raw.githubusercontent.com/LucasPuente/geocoding/master/modified_geocode.R")
ipak <- function(pkg){
new.pkg <- pkg[!(pkg %in% installed.packages()[, "Package"])]
if (length(new.pkg))
install.packages(new.pkg, dependencies = TRUE)
sapply(pkg, require, character.only = TRUE)
}
packages <- c("maps", "mapproj", "splancs")
ipak(packages)
## maps mapproj splancs
## TRUE TRUE TRUE
Depuis quelques années il n’est plus possible d’acceder au coordonnées géographiques de l’emetteur d’un tweet. Aisin nous allons proceder differemment dans la partie ci dessous :
Cette fois ci nous utilisons le package “rtweet”, qui permet de récuperer, entre autres, la localisation entrée par l’utilisateur de chaque compte twitter.
gj_tweets<- search_tweets(q = "#giletsjaunes", n = 10000, lang = "fr", include_rts = FALSE, geocode = "47.07,2.36,1000km")
# Nous filtrons les tweets dont l'emetteur n'a indiqué aucune localisation
gj_tweets<-subset(gj_tweets, location != "")
# Nous filtrons les tweets dont l'emetteur a indiquée une localisation qui n'est pas en France (ou un lieu imaginaire)
location_clean<-str_subset(gj_tweets$location, ", France")
location_index<-str_which(gj_tweets$location, ", France")
gj_tweets<-gj_tweets[location_index,]
Pour pouvoir convertir la localisation du tweet en coordonnées géographiques, nous avons besoin d’APIs Google.
On génère une fonction qui récupère les coordonnées de chaque ville à l’aide des APIs Google Geocoding :
geocode_apply<-function(x){
geocode(x, source = "google", output = "all", api_key="**********code API secret***********")
}
On applique la fonction :
geocode_results_final_gilets_jaunes<-sapply(gj_tweets$location, geocode_apply, simplify = F)
geocode_results <- geocode_results_final_gilets_jaunes
On nettoie les résultats du geocoding :
# On ne garde que les géolocalisation bien trouvées (status = "ok")
condition_a <- sapply(geocode_results, function(x) x["status"]=="OK")
geocode_results1<-geocode_results[condition_a]
# On ne garde que les géolocalistions auquelles ne correspond qu'un seul et unique résultat
condition_b <- lapply(geocode_results, lapply, length)
condition_b2<-sapply(condition_b, function(x) x["results"]=="1")
geocode_results<-geocode_results1[condition_b2]
Nous allons mettre en forme les géolocalisation afin de les illustrées sur une carte :
source("https://raw.githubusercontent.com/LucasPuente/geocoding/master/cleaning_geocoded_results.R")
# Conversion en tableaux :
results_b<-lapply(geocode_results, as.data.frame)
results_c<-lapply(results_b,function(x) subset(x, select=c("results.formatted_address", "results.geometry.location")))
# Formattage des data.frame :
results_d<-lapply(results_c,function(x) data.frame(Location=x[1,"results.formatted_address"],
lat=x[1,"results.geometry.location"],
lng=x[2,"results.geometry.location"]))
# Concaténation des tableaux :
results_e<-rbindlist(results_d)
# On rajoute les localisation entrées par les utilisateurs :
results_f<-results_e[,Original_Location:=names(results_d)]
# On ne garde que les résultats en France :
french_results_gj<-subset(results_f,
grepl(", France", results_f$Location)==TRUE)
Voici la forme du tableau final qui va nous servire à géolocaliser les utilisateurs de twitter en France :
Avec en dessous la forme originale des données entrées par les utilisateurs
head(french_results_gj)
## Location lat lng Original_Location
## 1: Paris, France 48.85661 2.352222 Paris, France
## 2: Paris, France 48.85661 2.352222 Paris, France
## 3: Paris, France 48.85661 2.352222 Paris, France
## 4: Paris, France 48.85661 2.352222 Paris, France
## 5: 60000 Beauvais, France 49.42954 2.080712 Beauvais, Picardie, France
## 6: Boulogne-Billancourt, France 48.83970 2.239912 Boulogne-Billancourt, France
Nous allons projeter sur une carte les utilisateurs de Twitter qui ont tweeté un #giletsjaunes, en effet toute la démarche ci dessus a non seulement permis de récuperer les lieux de provenance des tweets, de les filtrer, mais aussi de convertir ces lieux en coordonnées géographiques grâce à Google Cloud Platform et en l’occurence l’API Geocoding de ce service.
#Generate a blank map:
albers_proj<-map("france", proj="albers", param=c(20, 45), col="#999999", fill=TRUE, bg=NA, lwd=0.2, add=TRUE, resolution=1)
map(albers_proj)
albers_proj<-map("france", proj="albers", param=c(20, 45), col="#999999", fill=TRUE, bg=NA, lwd=0.2, add=TRUE, resolution=1)
#Add points to it:
points(mapproject(french_results_gj$lng, french_results_gj$lat), col="yellow", bg="#00000030", pch=21, cex=2.0)
#Add a title:
mtext("La géolocalisation des tweets #giletsjaunes", side = 3, line = -3.5, outer = T, cex=1.5, font=3)
Nous procédons de la même manière pour les tweets #foulardsrouges
Le mouvement des gilets jaunes occupe l’actualité depuis quelques mois, en effet le mouvement a commencé le 17 novembre 2018. Avec une grande quantité de posts et de tweets sur les réseaux sociaux qui traitent de ce thème, le contenu de ces posts peut paraître trop vaste pour pouvoir être assimilé. Nous avons ainsi vu grâce à différentes fonctions dans ce projet, qu’il est ainsi possible, non seulement de récupérer ces tweets et ces posts, mais aussi de les interpréter et faire ressortir les points d’accord et de désaccord entre les différents parties politiques et les gilets jaunes. Avec certains résultats très intéressant, comme par exemple une ressemblance dans le lexique utilisé par les partis d’extrêmes droites et gauches et les gilets jaunes. On a été confronté à certaines difficultés non seulement du fait de l’actualité du sujet mais aussi de l’évolution constante des termes et conditions d’utilisations de Twitter et des APIs permettant de récuperer les données disponibles. Cette étude peut être plus approfondie, et les outils utilisés peuvent être appliqués à différents thème et à des fins toute aussi diverses.
https://console.cloud.google.com/
https://towardsdatascience.com/access-data-from-twitter-api-using-r-and-or-python-b8ac342d3efe
https://sites.google.com/site/miningtwitter/questions/talking-about/wordclouds/comparison-cloud
http://lucaspuente.github.io/notes/2016/04/05/Mapping-Twitter-Followers
https://www.math.u-bordeaux.fr/~arichou/site/projets2017/Camara-Canal.html