Projets informatiques M2 Parcours MSS

Table des matières

Introduction

  1. Wordclouds

    1.1. La primaire de la gauche

    1.2. L'image du PSG

    1.3. Le nuage de mots de Conway

  2. Analyse des hashtags

  3. Analyse des sentiments

  4. Clustering

  5. Géolocalisation

    5.1. Les Etats-Unis

    5.2. Le Monde

  6. Croisement avec d'autres Open Datas

Conclusion

Sources

Introduction


Twitter est un réseau social de microblogage créé en mars 2006 par J. Dorsey, E. Williams, B. Stone, N. Glass. Il permet à ses utilisateurs de publier des messages limités à 140 caractères appelés tweets. Twitter est aujourd'hui côté en bourse et compte 317 millions d'utilisateurs. En moyenne 500 millions de tweets sont échangés chaque jour sur ce réseau social. Des hommes politiques, des personnalités ou encore des entreprises utilisent ce réseau à des fins de communication.

Chaque jour des millions de données sont générées et un tweet contient 31 métadonnées (identifiant de l'auteur du tweet, localisation, nombre de followers, identifiants des followers, nombre de retweets, etc...).

Afin d'exploiter ces Open Data, deux chemins s'offrent à nous :

Pour effectuer ces analyses, nous utilisons un package R : twitteR et nous avons créé une API pour récupérer les données.
Il faut 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.

setup_twitter_oauth(api_key,api_secret,access_token,access_token_secret)

## [1] "Using direct authentication"

1. Wordclouds

1.1. La primaire de la gauche


Dans un premier temps, nous proposons de faire des comparaisons des mots les plus utilisés par les 7 candidats de la primaire de la gauche. Il s'agit ici d'analyser le contenu des tweets des 7 candidats à la primaire de la gauche, à savoir :

Le but étant d'afficher les mots qu'ils emploient le plus sur un même nuage. En effet, les nuages de mots sont appréciés en datavisualisation car ils permettent d'avoir une représentation graphique simple et lisible de l'information contenue dans du texte.
Pour cela, nous récupérons le texte des 1000 derniers tweets émis par les comptes des différents candidats et ceux des retweets (sur Twitter lorsqu'un tweet nous intéresse, nous pouvons le partager à nos abonnés à l'aide d'un retweet).

Prenons pour exemple Manuel Valls. Nous utilisons la fonction userTimeline() pour récupérer 1000 de ses tweets (et les métadonnées sous-jacentes). Nous convertissons la liste des tweets en dataframe, puis nous récupérons le texte des tweets.

Nous itérons la procédure pour les 6 autres candidats.

# Manuel Valls
manuel_tweet = userTimeline("manuelvalls", n=1000, includeRts = TRUE)
m_tweet = twListToDF(manuel_tweet)

manuel_text = m_tweet$text


Nous nettoyons le texte à l'aide de la fonction clean.text() qui permet d'enlever les caractères inutiles à notre analyse tels que les accents, les liens, les caractères spéciaux...

# Fonction de nettoyage du texte (plus d'accents, de liens et de divers caractères spécifiques)
clean.text = function(x_text, word = stopwords(), 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_trim(x_text)
  return(x_text)
}

# Nettoyage des tweets de Manuel Valls
m_text = clean.text(manuel_text)


Après avoir agrégé les textes de tous les candidats, nous convertissons cette liste en corpus qui contient 7 documents (un document par candidat), puis appliquons la fonction TermDocumentMatrix qui permet de compter les occurrences de chaque mots en prenant soin d'enlever les stopwords (les stopwords sont des mots très courants dans les phrases et vide de sens tels que les articles ou encore les prépositions). Nous convertissons cet objet en matrice puis il ne reste plus qu'à effectuer le nuage comparatif de mots.

# On convertit en corpus qui contient 7 documents
corpus = Corpus(VectorSource(primaire_text))

# On compte le nombre d'occurrences par mots pour chaque candidat (sans compter les stopwords et en mettant tous les caractères en minuscule)
tdm = TermDocumentMatrix(corpus, control = list(tolower = TRUE, stopwords = word))
tdm = as.matrix(tdm)

# On choisit la palette de couleurs à appliquer au nuage 
couleurs <- brewer.pal(7,"Dark2")

# On construit le nuage de comparaison des candidats (par fréquence de mots décroissante)
comparison.cloud(tdm, scale=c(.75,.5), random.order=FALSE, colors = couleurs, title.size=1, max.words=150)

Le nuage comparatif permet de visualiser les mots les plus tweetés par les 7 candidats (chaque candidat est représenté par une couleur différente). Nous observons que les mots chers à chaque candidat apparaissent clairement. Par exemple :

Rapidement, nous pouvons nous faire une idée des thèmes chers aux candidats.

Retour à la table des matières

1.2. L'image du PSG


Dans cette section, nous nous intéressons à la localisation des tweets sur un même sujet, ici le Paris Saint-Germain. Nous cherchons à observer l'existence de divergences sur les mots les plus utilisés dans les tweets citant le PSG autour de Paris et autour de Marseille. Nous utilisons cette fois la fonction searchTwitter() qui permet de récupérer des tweets contenant un mot ou un hashtag spécifique en précisant la langue des tweets et le géocode qui correspond à la latitude, la longitude ainsi que le rayon (définissant le périmètre autour d'un point géographique).

# Récupération des tweets en français contenant #psg dans un rayon de 100 kilomètres autour de Paris 
psg_tweets_paris = searchTwitter('#psg', n=100, lang='fr', geocode="48.8,2.3,100km")

# Récupération du texte des tweets
psg_txt_paris = sapply(psg_tweets_paris, function(x) x$getText())


De la même manière que dans la précédente section, nous nettoyons le texte des tweets et nous créons un Corpus afin d'appliquer la fonction TermDocumentMatrix(). Nous effectuons ensuite un nuage comparatif en utilisant la localisation des tweets.

# On construit le nuage de comparaison des deux villes (par fréquence de mots décroissante)
comparison.cloud(tdm, scale=c(2,.5), random.order=FALSE, colors = c("#6BAED6","#084594"), title.size=1.5, max.words=50, min.freq=3)

Nous pouvons ainsi visualiser dans deux villes différentes ce qui se dit sur un sujet.

Retour à la table des matières

1.3. Le nuage de mots de Conway


Les nuages de mots précédemment utilisés ont leur limite. En effet, ils se contentent de juxtaposer des mots et la taille de ces derniers dépend de leur fréquence d'utilisation.

Drew Conway, dans son article BUILDING A BETTER WORD CLOUD, introduit un nouveau type de nuage de mot qui permet de prendre en compte la fréquence d'apparition des mots mais également de comparer la fréquence d'utilisation de ces mots par deux individus.
Ici, nous récupérons 1500 tweets issus des comptes de deux présidents des Etats-Unis : Barack Obama et Donald Trump. Nous effectuons la même procédure que précédemment afin d'obtenir un TermDocumentMatrix.

Ensuite, nous enlevons les mots présents moins de 2 fois dans la colonne de chaque individu. La différence de fréquence d'utilisation des mots est la différence entre un mot tweeté par Barack Obama et un mot tweeté par Donald Trump.

Pour plus de lisibilité sur le nuage final, nous appliquons une fonction d'espacement des mots.

# Comparaison des mots employés par les deux utilisateurs
ggplot(obama_df, aes(x=freq.dif, y=Spacing)) +

  # Mots utilisateur 1
  geom_text(aes(size=obama.txt, label=row.names(obama_df),
                colour=freq.dif), alpha=0.7, family='Arial') +

  # Mots utilisateur 2
  geom_text(data=trump_df, aes(x=freq.dif, y=Spacing,
                                label=row.names(trump_df), size=trump.txt, color=freq.dif),
            alpha=0.7, family='Arial') +

  # Mots des deux utilisateurs (qui ont la même fréquence)
  geom_text(data=both_df, aes(x=freq.dif, y=Spacing,
                              label=row.names(both_df), size=obama.txt, color=freq.dif),
            alpha=0.7, family='Arial') +

  # Echelle des mots selon la fréquence
  scale_size(range=c(3,11)) +

  # Couleur selon qu'on s'écarte de 0 (plus la différence de fréquence des mots est grande)   
  scale_colour_gradient(low="red3", high="blue3", guide="none") +

  scale_x_continuous(breaks=c(min(trump_df$freq.dif), 0, max(obama_df$freq.dif)),
                     labels=c("Tweeté plus par Trump","Tweeté par les deux","Tweeté plus par Obama")) +
  scale_y_continuous(breaks=c(0), labels=c("")) +
  labs(x="", y="", size="Fréquence des mots") +
  theme_bw() +

  labs(#panel.grid.major = theme_blank(),
    #panel.grid.minor = theme_blank(),
    title="Nuage de mots de Conway, tweets Trump / Obama")



Le premier graphique est la première étape de construction du nuage de mots de Conway. Nous observons les mots employés par Barack Obama. En abscisse, la différence de fréquence des mots par rapport à Donald Trump est représentée. Par exemple, le mot "senat" a été prononcé par Obama plus de 60 fois par rapport à Donald Trump. En revanche, le mot "obama care" a été prononcé autant de fois par les deux individus. La taille du texte représente la fréquence dans les tweets de Barack Obama : "leaders" a été plus de fois tweeté que "team".

Voici le nuage de mots de Conway. Sur cette représentation, les mots tweetés par Barack Obama et Donald Trump apparaissent simultanément. Les mots situés au milieu du graphique sont autant employés par les deux individus. Plus on va vers la gauche, plus on observe les mots employés par Donald Trump et inversement, plus on va vers la droite, plus on observe les mots employés par Barack Obama.

Retour à la table des matières

1. Analyse des hashtags


*Un hashtag est un marqueur utilisé sur plusieurs réseaux sociaux qui permet d'étiqueter un contenu avec un mot clé ou plusieurs mots clés juxtaposés.* Ici, nous nous intéressons aux hashtags les plus utilisés par Donald Trump. Nous récupérons les 1000 derniers tweets du compte toujours avec la fonction userTimeline() puis nous récupérons le texte des tweets. Une fonction permet d'extraire les hashtags puis on sélectionne le nombre de hashtags désiré. # Fonction qui permet de récupérer les # utilisés extract.hashes = function(vec){ hash.pattern = "#[[:alpha:]]+" have.hash = grep(x = vec, pattern = hash.pattern) hash.matches = gregexpr(pattern = hash.pattern, text = vec[have.hash]) extracted.hash = regmatches(x = vec[have.hash], m = hash.matches) df = data.frame(table(tolower(unlist(extracted.hash)))) colnames(df) = c("hashtag","frequence") df = df[order(df$freq,decreasing = TRUE),] return(df) } # On récupère les 30 hashtags les plus fréquents dat = head(extract.hashes(texte),30) # On réordonne les hashtags par fréquence décroissante tag = transform(dat,hashtag = reorder(hashtag,frequence)) # On affiche le graphe des hashtags p = ggplot(tag, aes(x = hashtag, y = frequence)) + geom_bar(fill = "skyblue",stat="identity") p + coord_flip() + labs(title = "Fréquence des Hashtags de Donald Trump") ![](Camara-Canal_files/freq_hashtags.png) Nous observons un graphe avec la fréquence des hashtags les plus utilisés du plus fréquent au moins fréquent. L'hashtag le plus utilisé par Donald Trump est "drain the swamp" qui est une métaphore utilisée par certains politiques pour désigner le fait de vouloir débarrasser le gouvernement de ses mauvais éléments.

Retour à la table des matières

1. Analyse des sentiments


Les tweets permettent aux utilisateurs d'exprimer un avis ou une opinion. Il peut donc être intéressant pour une marque ou une entreprise de savoir ce que perçoivent les Twittos d'elle. Pour cela, nous avons effectué une analyse de sentiment qui permet de déterminer si un tweet a une connotation positive, négative ou neutre. Nous avons utilisé deux dictionnaires en anglais contenant une liste de mots positifs et de mots négatifs. Nous calculons un score qui est la différence entre le nombre de mots positifs et de mots négatifs puis nous utilisons la règle suivante : - un tweet est considéré positif si son score est supérieur ou égal à 1, - un tweet est considéré neutre si son score est égal à 0, - un tweet est considéré négatif si son score est inférieur ou égal à -1. Les scores sont généralement assez faibles car les utilisateurs expriment peu d'opinions différentes en 140 caractères. Nous nous intéressons aux avis sur deux films : Lalaland et Insidious. # Fonction qui calcule le score d'un tweet score.sentiment = function(sentences, pos.words, neg.words) { # Parameters # sentences: vector of text to score # pos.words: vector of words of postive sentiment # neg.words: vector of words of negative sentiment # create simple array of scores with laply scores = laply(sentences, function(sentence, pos.words, neg.words) { # remove punctuation sentence = gsub("[[:punct:]]", "", sentence) # remove control characters sentence = gsub("[[:cntrl:]]", "", sentence) # remove digits? sentence = gsub('\\d+', '', sentence) # define error handling function when trying tolower tryTolower = function(x) { # create missing value y = NA # tryCatch error try_error = tryCatch(tolower(x), error=function(e) e) # if not an error if (!inherits(try_error, "error")) y = tolower(x) # result return(y) } # use tryTolower with sapply sentence = sapply(sentence, tryTolower) # split sentence into words with str_split (stringr package) word.list = str_split(sentence, "\\s+") words = unlist(word.list) # compare words to the dictionaries of positive & negative terms pos.matches = match(words, pos.words) neg.matches = match(words, neg.words) # get the position of the matched term or NA # we just want a TRUE/FALSE pos.matches = !is.na(pos.matches) neg.matches = !is.na(neg.matches) # final score score = sum(pos.matches) - sum(neg.matches) return(score) }, pos.words, neg.words) # data frame with scores for each sentence scores.df = data.frame(text=sentences, score=scores) return(scores.df) } ![](Camara-Canal_files/Films-copie.png) Les tweets concernant le film Lalaland sont majoritairement neutres et positifs. C'est cohérent car cette comédie musicale a été encensée par la critique et a obtenu plusieurs distinctions aux Golden Globes. En revanche, les tweets concernant le film d'horreur Insidious sont majoritairement négatifs. Nous cherchons désormais à savoir comment ont évolué les avis sur le film Lalaland ces derniers jours. # Recherche effectuée et enregistrée dans le fichier lala.Rdata #la <- searchTwitter("lalaland", since='2017-02-8', until='2017-02-18', lang="en", n=50000) #lala <- twListToDF(la) # Chargement des tweets en anglais mentionnant lalaland entre le 08/02/2017 et le 18/02/2017 load("Camara-Canal_files/lala.RData") # Importation des deux dictionnaires pos = readLines("Camara-Canal_files/positive_words.txt") neg = readLines("Camara-Canal_files/negative_words.txt") # Récupération du texte lala_txt = lala$text # Application de la fonction score.sentiment scores = score.sentiment(lala_txt, pos, neg) # Ajout de la date des tweets scores_film = cbind(scores,lala$created) # Fonction qui affecte un sentiment selon le score sentiment_film <- c() for (i in 1:nrow(scores_film)) { if (scores_film[i,2] > 1 | scores_film[i,2] == 1) sentiment_film[i] <- "positif" if (scores_film[i,2] < -1 | scores_film[i,2] == -1) sentiment_film[i] <- "négatif" if (scores_film[i,2] == 0) sentiment_film[i] <- "neutre" } scores_final <- cbind(scores_film,sentiment_film) # On extrait les sentiments positifs, négatifs et neutres positif_film <- subset(scores_final, scores_final$sentiment_film=="positif") négatif_film <- subset(scores_final, scores_final$sentiment_film=="négatif") neutre_film <- subset(scores_final, scores_final$sentiment_film=="neutre") # Positifs positif_film$cpt <- rep(1, nrow(positif_film)) # On fait la somme par jour des tweets positifs ag = aggregate (positif_film$cpt, by=list(format(positif_film$`lala$created`, "%Y-%m-%d")), sum) ag$Date <- gsub(" ", "", gsub("-", "", ag$Group.1)) # Négatifs négatif_film$cpt <- rep(1, nrow(négatif_film)) # On fait la somme par jour des tweets négatifs ag2 = aggregate (négatif_film$cpt, by=list(format(négatif_film$`lala$created`, "%Y-%m-%d")), sum) ag2$Date <- gsub(" ", "", gsub("-", "", ag2$Group.1)) # Neutres neutre_film$cpt <- rep(1, nrow(neutre_film)) # On fait la somme par jour des tweets neutres ag3 = aggregate (neutre_film$cpt, by=list(format(neutre_film$`lala$created`, "%Y-%m-%d")), sum) ag3$Date <- gsub(" ", "", gsub("-", "", ag3$Group.1)) # Graphe plot(ag$Date, ag$x, xaxt="n",type="l",col="cornflowerblue",ylab="Nombre de tweets",xlab="Temps",ylim=c(0,max(ag3$x)),main="Evolution des avis sur le film Lalaland") lines(ag2$Date, ag2$x, xaxt="n",type="l",col="darkblue") lines(ag3$Date, ag3$x, xaxt="n",type="l",col="mediumturquoise") axis(1, at=ag$Date, labels=ag$Group.1) legend("topright",legend=c("positif","négatif","neutre"),lty=1,col=c("cornflowerblue","darkblue","mediumturquoise")) ![](Camara-Canal_files/lalaland.png) Nous observons que sur cet intervalle de temps, les tweets sont majoritairement positifs ou neutres. Il y a un pic de tweets positifs et neutres le 12 février 2017. Cette date correspond à la cérémonie de remise des récompenses britanniques du cinéma (BAFTA : British Academy of Film and Television Arts). Le film a reçu trois récompenses : BAFTA pour le meilleur film, la meilleure actrice et le meilleur réalisateur.

Retour à la table des matières

1. Clustering


Maintenant nous souhaitons calculer des scores d'association entre plusieurs mots et effectuer une Classification Ascendante Hiérarchique afin d'observer si nous pouvons faire des regroupements en classes. Pour ce faire, nous récupérons 1000 tweets mentionnant la marque Apple.
Nous pouvons afficher les termes qui reviennent le plus fréquemment dans ces 1000 tweets. Ici, nous avons choisis uniquement les termes qui ont une fréquence d'apparition au moins égale à 20. Puis nous affichons un histogramme des 10 mots les plus fréquemment rencontrés. # Quels sont les termes les plus fréquents (supérieur à 20 fois) dans le document matrix? b = findFreqTerms(tdm, lowfreq = 20) b ## [1] "amour" "android" "bande" "bientot" "chez" ## [6] "connecte" "critique" "deux" "dispo" "download" ## [11] "ecoute" "envoyer" "fait" "farmrewards" "full" ## [16] "gagner" "gagnons" "ios" "iphone" "itunes" ## [21] "jamais" "jeu" "jlui" "jouer" "lien" ## [26] "marche" "msg" "ouvre" "paradis" "petit" ## [31] "plus" "quand" "recompenses" "slt" "soeur" ## [36] "spotif" "store" "taptapfish" "tel" "touche" ## [41] "url" "venez" "venue" "video" "voir" # Ici, on cherche à avoir un data frame avec la fréquence associée à chaque mot m <- as.matrix(tdm) v <- sort(rowSums(m),decreasing=TRUE) d <- data.frame(word = names(v),freq=v) # Les 10 mots les plus fréquents barplot(d[1:10,]$freq, las = 2, names.arg = d[1:10,]$word, col ="lightblue", main ="Mots les plus fréquents",ylab = "Word frequencies") ![](Camara-Canal_files/figure-markdown_strict/unnamed-chunk-21-1.png)
Nous pouvons ensuite calculer des scores d'association entre deux mots tels que Apple et ios. Nous sélectionnons uniquement les mots ayant une corrélation minimale de 0.5.
# On peut chercher les associations entre mots, calculées par un score d'association a = findAssocs(tdm, "ios", corlimit = 0.5) a ## $ios ## vampire lol petit android jamais ## 0.76 0.74 0.64 0.62 0.62
Nous effectuons du clustering sur les tweets contenant le mot élection. Avant de réaliser une Classification Ascendante Hiérarchique, il est nécessaire d'enlever les termes rares. En effet, ces termes très peu présents augmentent considérablement la taille de la matrice. Nous fixons le coefficient de rareté à 97%. Ainsi, les mots ayant plus de 97% de 0 sur leurs lignes sont supprimés. Nous calculons la distance euclidienne entre les mots présents dans notre matrice, puis nous effectuons une CAH.
# Récupération de 1000 tweets contenant le mot "election" en français test = searchTwitter('election', n=1000, lang='fr')#,geocode="48.8,2.3,100km") # On récupère le texte des tweets et on applique la fonction nettoyage txt_test = sapply(test, function(x) x$getText()) txt_test = clean.text(txt_test) word = c(stopwords("french"),stopwords("english")) corpus = Corpus(VectorSource(txt_test)) tdm = TermDocumentMatrix(corpus, control = list(tolower = TRUE, stopwords = word)) # On enlève les termes rares selon un coéficient # de rareté, cette étape nous permet de réduire considérablement la taille de la matrice tdm2 <- removeSparseTerms(tdm, sparse = 0.97) # Une CAH sur les corpus les plus retouvés d <- dist(tdm2, method = "euclidean") fit <- hclust(d, method = "ward.D") plot(fit) groups <- cutree(fit, k = 5) rect.hclust(fit, k = 5, border = "mediumaquamarine") ![](Camara-Canal_files/Election.png)
Le dendrogramme suivant met en évidence un regroupement en k = 5 classes. Nous observons que ce regroupement est plutôt cohérent : - Les mots "american", "story", "horror", "americaine", "saison" apparaissent dans le même cluster. En effet, il a été révélé ces derniers jours que la saison 7 de la série américaine American Horror Story sera centrée sur l'élection américaine de 2016. - Les mots "election", "presidentielle" sont regroupés. - Les mots "macron", "contre", "colonisation", "crime" sont dans un groupe. En effet, le candidat à l'élection présidentielle Emmanuel Macron a qualifié la colonisation, lors d'une interview en Algérie le 14 février 2017, de crime contre l'humanité. - Les mots "fillon", "candidature", "lelection" apparaissent ensemble. En effet, François Fillon est un candidat à l'élection présidentielle de 2017. - Enfin, les mots "marine", "pen", "rapport", "accable", "parlement", "européen", "assistants", "parlementaires" sont regroupés. Effectivement, la candidate Marine Le Pen est mise en cause par le Parlement européen.

Retour à la table des matières

1. Géolocalisation

5.1. Les Etats-Unis


Enfin, nous allons utiliser les métadonnées pour tenter de faire une représentation géographique des tweets. En effet, sur certains tweets il est possible d'obtenir la géolocalisation (latitude, longitude).

Ici, nous utilisons la fonction filterStream() qui permet de récupérer en temps réel sur une durée déterminée en secondes (argument timeout) les tweets situés à un endroit précis (argument locations) ou sur un ou plusieurs sujets (argument track). Afin de récupérer dans un dataframe les tweets il faut utiliser la fonction parseTweets(). La localisation des Etats-Unis est "-125,25,-66,50" ("longitude_min,latitude_min,longitude_max,latitude_max").

load("Camara-Canal_files/my_oauth.Rdata")

# Exemple de récupération de tweets pendant 1 minute contenant le mot Obama
filterStream("tweets.json", track = "Obama", timeout = 60, oauth = my_oauth)

tweets.df <- parseTweets("tweets.json", simplify = TRUE)

# Exemple de récupération de tweets émis depuis les Etats-Unis pendant 1 minute 
filterStream("tweetsUS.json", locations = c(-125, 25, -66, 50), timeout = 60, oauth = my_oauth)

tweets.df <- parseTweets("tweetsUS.json", verbose = FALSE)

map.data <- map_data("state")
points <- data.frame(x = as.numeric(tweets.df$lon), y = as.numeric(tweets.df$lat))
points <- points[points$y > 25, ]
ggplot(map.data) + geom_map(aes(map_id = region), map = map.data, fill = "white", 
                            color = "grey20", size = 0.25) + expand_limits(x = map.data$long, y = map.data$lat) + 
  theme(axis.line = element_blank(), axis.text = element_blank(), axis.ticks = element_blank(), 
        axis.title = element_blank(), panel.background = element_blank(), panel.border = element_blank(), 
        panel.grid.major = element_blank(), plot.background = element_blank(), 
        plot.margin = unit(0 * c(-1.5, -1.5, -1.5, -1.5), "lines")) + geom_point(data = points, 
                                                                                 aes(x = x, y = y), size = 1, alpha = 1/5, color = "darkblue")


Nous observons que certains Etats émettent plus de tweets que d'autres. En effet, il semble y en avoir plus dans les grandes villes telles que Seattle, San Francisco, Los Angeles, Miami, Chicago, New-York.

Retour à la table des matières

5.2. Le monde


Nous procédons de manière identique pour récupérer les tweets émis dans le monde entier, en spécifiant locations = c(-180, -90, 180, 90).


La Triade ressort très clairement sur cette carte.

Retour à la table des matières

Croisement avec d'autres Open Datas


Ici, nous nous intéressons au sexe des utilisateurs. Nous souhaitons déterminer si un individu est un homme ou une femme. Pour ce faire, nous avons récupéré la liste de prénoms féminins et masculins sur Data.gouv pour analyser les noms d'utilisateurs.

Nous récupérons 1000 tweets en français contenant le mot "football" afin de déterminer le sexe des utilisateurs.

# Récupération des tweets et conversion en data.frame
test <- searchTwitter("football", n = 1000, lang = "fr")
test_2 <- twListToDF(test)

# Récupération des noms d'utilisateurs
utilisateur <- test_2$screenName
head(utilisateur)

# Chargement du fichier des prénoms
names <- read.csv("Camara-Canal_files/liste_des_prenoms_2004_a_2012.csv",sep=";")

tryTolower = function(x)
{
  # create missing value
  y = NA
  # tryCatch error
  try_error = tryCatch(tolower(x), error=function(e) e)
  # if not an error
  if (!inherits(try_error, "error"))
    y = tolower(x)
  # result
  return(y)
}

# On enlève la ponctuation, les caractères de contrôle
sentence = gsub("[[:punct:]]", "", utilisateur)
sentence = gsub("[[:cntrl:]]", "", utilisateur)
sentence = gsub('\\d+', '', utilisateur)

# On crée une liste contenant les noms d'utilisateurs
sentence = sapply(utilisateur, tryTolower)
word.list = str_split(sentence, "\\s+")
words = unlist(word.list)

# Création d'un fichier féminin d'un fichier masculin
feminin <- subset(names,names$sexe=="F")
masculin <- subset(names,names$sexe=="M")

# On conserve uniquement les prénoms qui ont plus de trois lettres
feminin_2 <- subset(feminin,nchar(as.character(feminin$prenoms)) > 3)
masculin_2 <- subset(masculin,nchar(as.character(masculin$prenoms)) > 3)

prenoms_feminins <- sapply(feminin_2$prenoms,tryTolower)
prenoms_masculins <- sapply(masculin_2$prenoms,tryTolower)

# On identifie les prénoms féminins
genre_1 <- rep(NA,1000)
for (i in 1:length(prenoms_feminins))
{
  for (j in 1:length(words))
  {
    if (grepl(prenoms_feminins[i],words[j]) == TRUE) genre_1[j] <- "Feminin"
  }
}

# On identifie les prénoms masculins
genre_2 <- rep(NA,1000)
for (i in 1:length(prenoms_masculins))
{
  for (j in 1:length(words))
  {
    if (grepl(prenoms_masculins[i],words[j]) == TRUE) genre_2[j] <- "Masculin"
  }
}

sexe <- genre_1

# Condition pour gérer les prénoms mixtes et les pseudos qui ont à la fois juxtaposés un prénom masculin et féminin
cond <- genre_1=="Feminin" & genre_2=="Masculin"

# Construction d'une seule liste
masc <- which(genre_2=="Masculin")
sexe[masc] <- "Masculin"

commun <- which(cond==TRUE) 
sexe[commun] <- "Mixte"

# Affichage des 100 premiers éléments de la liste
head(sexe,100)

# Récupération du nombre dans chaque catégorie
nb_masc <- count(sexe=="Masculin")[2,2]
nb_fem <- count(sexe=="Feminin")[2,2]
nb_mixte <- count(sexe=="Mixte")[2,2]
nb_NA <- count(sexe=="NA")[2,2]

# Paramètres du graphique
x <- c(nb_masc,nb_fem,nb_mixte,nb_NA)
pourcentage <- round(x/sum(x)*100)
noms <- c("Masculin","Feminin","Mixte","NA")
noms <- paste(noms,pourcentage)
noms <- paste(noms,"%",sep="")

# Graphique
pie(x,labels=noms,col=brewer.pal(7,"Blues"),main="Répartition des utilisateurs par sexe")

On obtient la liste du sexe des utilisateurs. Les individus qui ont pour valeur NA sont soit des utilisateurs qui n'ont pas mis de prénom dans leur nom d'utilisateur soit ce sont des entreprises. Pour ceux qui ont pour valeur NA et les prénoms mixtes, il faut pousser plus loin l'analyse en exploitant le contenu de la description de leur compte Twitter.

Afin de détecter les entreprises, il faut disposer d'un fichier contenant les entreprises que l'on peut récupérer sur le site de l'INSEE.

Retour à la table des matières

Conclusion


Dans ce post, nous avons passé en revue différentes techniques permettant d'analyser les données ouvertes de Twitter : nuages de mots, cartes, clustering... Ce sont des outils qui peuvent être intéressants pour des entreprises afin de connaître leur image auprès des Twittos. En effet, Twitter agit comme du bouche à oreille. Si un utilisateur a aimé un film ou un produit, il va en parler et en informer ses followers.

Nous proposons une application Shiny permettant d'effectuer ces différentes analyses en choisissant vous-même les paramètres et ainsi produire les analyses que vous jugerez pertinentes. Cliquez ici pour y accéder.

Sources


http://drewconway.com/zia/2013/3/26/building-a-better-word-cloud

https://sites.google.com/site/miningtwitter/

http://www2.rdatamining.com/uploads/5/7/1/3/57136767/rdatamining-slides-text-mining.pdf

https://www.r-bloggers.com/r-et-twitter/

https://www.r-bloggers.com/search/twitter/