Chapitre 3 : compter les occurrences

tex2r image chap02

La première tache est de compter les mots, leurs occurrences et co-occurrences.

Dans cette première partie, nous allons nous intéresser uniquement aux mots, et non aux phrases ou aux n-grams. On peut utiliser les mêmes outils dans ces cas de figure.

1 Analyse des termes les plus fréquents du corpus

Dans cette première partie, nous allons nous intéresser uniquement aux mots, et non aux phrases ou aux n-grams.

Présentation du corpus : Le corpus est composé de 102 textes, issus du site airbnbcitizen.com et filtrés par le mot-clé “Paris”.

La répartition du corpus dans le temps montre la montée en puissance du discours corporate qui si depuis 2016 s’infléchit en nombre de texte, continue à a croître en nombre de mots:

 

chap2 01chap2 02

Examinons maintenant la répartition des termes dans les documents. Le pipe  met dans un fichier total_words le résultat du pipe a) grouper le tbl par document et année, b) compter et trier les mots, résumer le nombre total.

On représente immédiatement ce tableau avec ggplot (il est conseillé de s’y familiariser par exemple avec cette page), le puissant moteur graphique de tidyverse.

Les documents sont en abscisse, les couleurs distinguent les années, et le nombre de mot des textes est affiché en ordonnée. La médiane est de l’ordre de 200 mots, mais 4 textes retiendront notre attention par leur longueur exceptionnelle.

total_words <- tbl %>%
  group_by(doc,year)%>%
  count(word,sort=TRUE)%>%
    summarize(total = sum(n))

ggplot(total_words, aes(doc, total))+
  geom_col(aes(fill=year))+
  geom_hline(yintercept = mean(total_words$total), color="blue", show.legend = TRUE)+
  geom_hline(yintercept = median(total_words$total), color="red")+
  theme(axis.text.x = element_blank())+
  ylab("nombre de mots")

chap2 03

 

summary(total_words$total)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    38.0   145.0   195.5   251.9   269.2  1235.0
sd(total_words$total)
## [1] 207.0137

Le nombre de mots moyens par document est de 252, avec un écart-type de 207.

2.1 Termes les plus fréquents

Regardons maintenant plus en détails les mots qui forment ce corpus :

chap2 04

On peut représenter les même données sous la forme de nuage de mots.

tbl%>%
  count(word, sort = TRUE) %>%
  filter(n>35)%>%
  mutate(word = reorder(word, n)) %>%
  with(wordcloud(word, n, max.words = 200, rot.per=0.35, colors=brewer.pal(6, "Dark2")))

chap2 05

2.2 Stemming

Ces premières représentations des mots les plus fréquents du corpus nous montrent que parmi les mots les plus fréquents, certains sont présents sous leur forme singulière et plurielle. Il peut alors être intéressant de transformer ces mots en leur racine, afin de les comptabiliser sous une seule forme. Pour ce faire, nous utilisons le package hunspell :

library(hunspell)
tbl_stem<-tbl %>%
  mutate(stem = hunspell_stem(word, dict = "en_US")) %>%
  separate(stem, c("word1", "word2"), sep = " ")%>%
  filter(word1!=("character(0)"))%>%
  mutate(word2=str_split(word2,boundary("word")))%>%
  mutate(stem=word2)%>%
  mutate(word1=as.list(word1))%>%
  mutate(word=case_when(
    is.na(word2)~word1,
    word2!="NA"~word2))%>%
  select(year,doc,word)%>%
  mutate(word=as.character(word))
tbl_stem%>%
  count(word,sort=TRUE)
## # A tibble: 2,484 x 2
##    word          n
##    <chr>     <int>
##  1 home        653
##  2 host        634
##  3 share       627
##  4 city        516
##  5 community   412
##  6 guest       308
##  7 local       223
##  8 people      210
##  9 world       157
## 10 list        154
## # ... with 2,474 more rows

chap2 06

chap2 07

tbl_stem%>%
  count(word, year, sort=T)%>%
  mutate(word = factor(word, levels = rev(unique(word)))) %>% 
  group_by(year) %>% 
  top_n(15) %>% 
  ungroup %>%
  ggplot(aes(word, n, fill = year)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y = "n") +
  facet_wrap(~year, ncol = 2, scales = "free") +
  coord_flip()+
  theme(axis.text.y = element_text(size=7))
## Selecting by n

chap2 08

De cette manière, nous voyons les mots les plus fréquemment utilisés dans l’ensemble du corpus. Ces termes sont des représentations de l’univers véhiculé par Airbnb.

2.3 Termes associés aux termes fréquents

Le package widyr contient d’autres fonctions que count et cor. Je ne sais pas trop ce qu’il convient de choisir… Vaste débat! le choix des indices de similarités.

2.3.1 Co-occurences

Nous nous intéressons aux co-occurrences de termes dans le corpus, c’est-à-dire aux termes qui apparaissent dans les mêmes documents, peu importe leur place dans le document :

word_pairs <- tbl_stem %>%
  pairwise_count(word, doc,upper=FALSE, sort = TRUE)
word_pairs
## # A tibble: 604,140 x 3
##    item1     item2     n
##    <chr>     <chr> <dbl>
##  1 host      home     86
##  2 home      share    86
##  3 community host     81
##  4 community home     80
##  5 home      city     79
##  6 host      share    79
##  7 city      share    76
##  8 community share    75
##  9 host      city     74
## 10 community city     73
## # ... with 604,130 more rows

Nous pouvons voir que ce fichier contient énormément de paires de mots, dont certains n’apparaissent qu’une seule fois. Nous allons donc retraiter ce tableau pour ne garder que les paires de mots qui apparaissent plus de 40 fois :

word_pairs<-word_pairs%>%
  filter(n>40)

Nous pouvons représenter les mots et leurs co-occurents les plus fréquents du corpus :

ggplot(word_pairs,aes(item1,n))+
  geom_text(aes(label=item2),hjust=0, vjust=0,size=2.5,check_overlap = TRUE)+
  coord_flip()+
  theme_light()

chap2 09

Nous constatons que les mots les plus fréquents du corpus dominent largement, ce qui est tout à fait logique. Nous pouvons également représenter les mots en fonction de leurs relations :

set.seed(2016)

word_pairs %>%
  graph_from_data_frame() %>%
  ggraph(layout = "auto") +
  geom_edge_link(aes(edge_alpha = n, color="grey"), show.legend = FALSE) +
  geom_node_text(aes(label = name), repel = TRUE, color="darkred")+
  geom_node_point(color = "lightblue", size = 4) +
  theme_void()
## Using `nicely` as default layout

chap2 10

Sur cette représentation, nous voyons bien que les mots les plus fréquents sont très liés entre eux. Voyons ce qu’il en est si l’on garde plus de paires de mots :

## Using `nicely` as default layout

chap2 11

Nous constatons donc que certains mots (community, guest, home, etc.) sont très présents dans le corpus. Les termes qui sont associés à ce groupe de mots dénotent plusieurs thématiques : les taxes et la régulation, les gains financiers, le tourisme et la découverte.

Nous pouvons nous intéresser aux mots co-occurents des 6 mots choisis :

word_pairs %>%
  filter(item1 %in% c("travel", "share", "city", "tax","hotel","economic")) %>%
  group_by(item1) %>%
  top_n(10) %>%
  ungroup() %>%
  mutate(item2 = reorder(item2, n)) %>%
  ggplot(aes(item2, n)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ item1, scales = "free") +
  coord_flip()+
  theme(axis.title.y = element_blank())
## Selecting by n

chap2 12

Nous remarquons que les mots les plus fréquents du corpus se retrouvent avec les mots choisis, mais nous pouvons tout de même remarquer que certains mots sont associés plus spéciquement avec certains termes.

Voyons ce qu’il en est lorsque l’on s’intéresse aux corrélations entres les termes.

2.3.2 Corrélations

Le calcul de corrélation doit prendre en compte les co-occurences des termes dans les documents, mais aussi leurs absences simultanées.

word_cors <- tbl_stem %>%
  group_by(doc) %>%
  filter(n() >= 20) %>%
  pairwise_cor(word, doc, upper=FALSE,sort = TRUE)
word_cors
## # A tibble: 3,083,886 x 3
##    item1     item2     correlation
##    <chr>     <chr>           <dbl>
##  1 memorable doubt              1.
##  2 professor ken                1.
##  3 blame     media              1.
##  4 meal      thank              1.
##  5 avenue    knowledge          1.
##  6 medium    written            1.
##  7 kingdom   fought             1.
##  8 swimming  pool               1.
##  9 tiny      fraction           1.
## 10 finance   offset             1.
## # ... with 3,083,876 more rows

Regardons les mots qui sont très fortement corrélés entre eux :

word_cors%>%
  filter(correlation==1)
## # A tibble: 20,645 x 3
##    item1       item2       correlation
##    <chr>       <chr>             <dbl>
##  1 four        prosperity            1
##  2 anecdote    observation           1
##  3 anecdote    quantify              1
##  4 observation quantify              1
##  5 anecdote    abundant              1
##  6 observation abundant              1
##  7 quantify    abundant              1
##  8 argue       theory                1
##  9 argue       evidence              1
## 10 theory      evidence              1
## # ... with 20,635 more rows
tbl_stem%>%
  filter(word=="anecdote")%>%
  count(doc)
## # A tibble: 1 x 2
##   doc              n
##   <chr>        <int>
## 1 20131206.txt     1
word_cors%>%
  filter(correlation<0.999)%>%
  filter(correlation>0.99)
## # A tibble: 345 x 3
##    item1    item2        correlation
##    <chr>    <chr>              <dbl>
##  1 football championship       0.997
##  2 meetup   success            0.997
##  3 meetup   craze              0.997
##  4 meetup   upgrade            0.997
##  5 meetup   echos              0.997
##  6 meetup   encounter          0.997
##  7 meetup   allover            0.997
##  8 meetup   forty              0.997
##  9 meetup   convivial          0.997
## 10 meetup   warm               0.997
## # ... with 335 more rows
tbl_stem%>%
  filter(word=="meetup")%>%
  count(doc)
## # A tibble: 2 x 2
##   doc              n
##   <chr>        <int>
## 1 20162707.txt     1
## 2 20170206.txt    12

On peut constater que les mots très corrélés entre eux sont ceux qui n’apparaissent que peu de fois dans le corpus (dans moins de 3% des documents). Regardons,maintenant, les mots les plus présents du corpus et leurs corrélations :

word_cors %>%
  filter(item1 %in% c("travel", "share", "city", "tax","hotel","economic")) %>%
  group_by(item1) %>%
  top_n(6) %>%
  ungroup() %>%
  mutate(item2 = reorder(item2, correlation)) %>%
  ggplot(aes(item2, correlation)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ item1, scales = "free") +
  coord_flip()+
  theme(axis.text.x = element_text(angle= 45, size=7))
## Selecting by correlation

chap2 13

Nous pouvons regarder les liens entre les mots choisis :

set.seed(2016)

word_cors_reseau<-word_cors %>%
  filter(item1%in%c("travel", "share", "city", "tax","hotel","economic","guest","benefit","community","pay","tourist","host","home"))%>%
  group_by(item1)%>%
  top_n(15)
## Selecting by correlation
g<-word_cors_reseau%>%
  graph_from_data_frame() %>%
  ggraph(layout = "auto") +
  geom_edge_link(aes(edge_alpha = correlation, color="grey"), show.legend = FALSE) +
  geom_node_point(color = "lightblue", size = 4)+
  theme_void()
## Using `nicely` as default layout
g

chap2 14

g+geom_node_text(aes(label=name),repel = TRUE, color="darkred" )

chap2 15

Nous pouvons voir que certains mots sont plus ou moins liés entre eux, ce qui laissent présager que différents sujets sont abordés dans le corpus. Avant de nous intéresser à déterminer ces sujets, nous allons d’abord observer ce qui ressort de l’analyse du corpus lorsque nous pondérons la fréquence d’apparition des termes par l’inverse de leur fréquence d’apparition dans les documents.

 

2.4 Tf-idf

Cette pondération permet de donner un poids plus importants aux termes les moins fréquents, qui permettent de dégager plus de sens qu’en s’intéressant seulement aux mots très présents dans le corpus.

2.4.1 Préparer le corpus

tbl_stem_tfidf<-tbl_stem %>% 
  group_by(doc,year) %>%
  count(word,sort=TRUE)
total_words <- tbl_stem_tfidf %>%
  summarize(total = sum(n))
tbl_stem_tfidf <- left_join(tbl_stem_tfidf, total_words)%>%
  bind_tf_idf(word, doc, n)%>%
  select(year,doc,word,n,tf_idf)%>%
  filter(tf_idf>0.001)%>%
  arrange(desc(tf_idf))
## Joining, by = c("doc", "year")
tbl_stem_tfidf
## # A tibble: 11,667 x 5
## # Groups:   doc, year [102]
##    year  doc            word             n tf_idf
##    <chr> <chr>          <chr>        <int>  <dbl>
##  1 2017  20170803.txt   women           19  0.322
##  2 2015  20151112.txt   solar           11  0.260
##  3 2016  20162902.txt   aster           10  0.256
##  4 2016  20161006.txt   championship    14  0.244
##  5 2017  20170407.txt   club            12  0.244
##  6 2016  20161006.txt   football        13  0.226
##  7 2016  20162906-1.txt parliament       3  0.224
##  8 2016  20160108.txt   ca              10  0.207
##  9 2016  20160706.txt   china           12  0.193
## 10 2017  20170112-2.txt village          6  0.189
## # ... with 11,657 more rows

2.4.2 Mots représentatifs

Regardons les mots qui ont le plus gros tf-idf : ils représentent les sujets abordés ponctuellement et qui dominent certains documents.

## Selecting by tf_idf

chap2 16

On peut aussi représenter les mots peu fréquents mais qui donnent du sens dans l’ensemble du corpus :

tbl_stem_tfidf%>%
  ungroup()%>%
  select(word, n, tf_idf)%>%
  group_by(word)%>%
  summarise(sum(n), sum(tf_idf))%>%
  rename(n='sum(n)', tf_idf='sum(tf_idf)')%>%
  ungroup()%>%
  select(word,tf_idf)%>%
  arrange(desc(tf_idf))%>%
  mutate(tf_idf=100*(tf_idf/max(tbl_stem_tfidf$tf_idf)))%>%
  mutate(word = reorder(word, tf_idf)) %>%
  with(wordcloud(word, tf_idf, max.words = 200, colors=brewer.pal(6, "Dark2")))

chap2 17

2.4.3 Evolution dans le temps

Regardons tout de suite quels sont les mots les plus significatifs au cours du temps, qui nous renseignerons sur l’évolution des thématiques abordées :

tbl_stem_tfidf%>%
  ungroup()%>%
  select(year, word, n, tf_idf)%>%
  group_by(year,word)%>%
  summarise(sum(n), sum(tf_idf))%>%
  rename(n='sum(n)', tf_idf='sum(tf_idf)')%>%
  ungroup()%>%
  group_by(year)%>%
  top_n(15) %>%
  ggplot(aes(word, tf_idf, fill = year)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y = "tf-idf") +
  facet_wrap(~year, ncol = 2, scales = "free") +
  coord_flip()+
  theme(axis.text.y = element_text(size=8))
## Selecting by tf_idf

chap2 19

On peut constater une évolution dans les sujets abordés au cours des années. Il semble intéressant de regarder la répartition des mots en fonction de leur fréquence d’apparition dans le corpus et en fonction de la fréquence normalisée par le poids des documents :

tbl_stem_tfidf%>%
  ungroup()%>%
  select(year, word, n, tf_idf)%>%
  group_by(year,word)%>%
  summarise(sum(n), sum(tf_idf))%>%
  rename(n='sum(n)', tf_idf='sum(tf_idf)')%>%
  arrange(desc(n))%>%
  ggplot(aes(n,tf_idf))+
  geom_point(aes(color=year))+
  geom_text(aes(label=word, color=year),hjust=0, vjust=0,size=2.5,check_overlap = TRUE,nudge_y = 0.002, show.legend = F)+
  geom_smooth()
## `geom_smooth()` using method = 'gam'

chap2 20

2.4.4 Termes associés

Bon, on peut refaire comme vu précédemment. Il suffit de rajouter une option pour définir la valeur des termes. Mais j’ai d’abord des questions sur quelle mesure de relation choisir (voir les fonctions du package widyr). Allez, voyons les corrélations :

word_cors_tfidf <- tbl_stem_tfidf %>%
  group_by(doc) %>%
  pairwise_cor(word, doc,tf_idf, upper=FALSE,sort = TRUE)
word_cors_tfidf
## # A tibble: 3,083,886 x 3
##    item1     item2      correlation
##    <chr>     <chr>            <dbl>
##  1 comb      stuck               1.
##  2 bottle    champagne           1.
##  3 bottle    fridge              1.
##  4 champagne fridge              1.
##  5 bottle    specialist          1.
##  6 champagne specialist          1.
##  7 fridge    specialist          1.
##  8 bottle    subscribe           1.
##  9 champagne subscribe           1.
## 10 fridge    subscribe           1.
## # ... with 3,083,876 more rows
word_cors_tfidf %>%
  filter(item1 %in% c("women", "emergency", "registration", "attorney","lie","helpful")) %>%
  group_by(item1) %>%
  top_n(6) %>%
  ungroup() %>%
  mutate(item2 = reorder(item2, correlation)) %>%
  ggplot(aes(item2, correlation)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ item1, scales = "free") +
  coord_flip()+
  theme(axis.text.x = element_text(angle = 45, size=6), axis.text.y = element_text(size = 8))
## Selecting by correlation

chap2 21

set.seed(2016)

word_cors_tfidf_reseau<-word_cors_tfidf %>%
  filter(item1%in%c("women", "emergency", "registration", "attorney","lie","helpful","championship","football","google"))%>%
  group_by(item1)%>%
  top_n(15)
## Selecting by correlation
g<-word_cors_tfidf_reseau%>%
  graph_from_data_frame() %>%
  ggraph(layout = "auto") +
  geom_edge_link(aes(edge_alpha = correlation, color="grey"), show.legend = FALSE) +
  geom_node_point(color = "lightblue", size = 4)+
  theme_void()
## Using `nicely` as default layout
g
g+geom_node_text(aes(label=name),repel = TRUE, color="darkred" )

chap2 22.png

 

Chapitre suivant : Passons maintenant à la recherche et à l’analyse de sujets (topic analysis).

Publicités

Laisser un commentaire

Choisissez une méthode de connexion pour poster votre commentaire:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s