9. Bigramas

No artigo de hoje vamos ter uma abordagem um pouco diferente para analisar textos. Esta abordagem consiste em considerar que palavras em conjunto podem trazer mais contexto do que palavras em separado. Esta metodologia é chamada de n-gramas e consiste em considerar a sequência das palavras em uma frase. Um exemplo:

n-gramas é uma metodologia de mineração de textos

Nesta afirmação, considerando bigramas, temos a seguinte combinação:

Ainda expandindo este conceito, considerando trigramas, temos a seguinte combinação:

Aplicando esta metodologia e algum pré-processamento adicional, podemos utilizar o que já aprendemos nos artigos anteriores e tirar melhores significados dos textos. Vamos aos códigos!

Dados do Twitter

Na aplicação de hoje, vamos escolher um assunto dos trending topics e capturar 200 tweets sobre esse determinado assunto. Clique aqui se você não sabe como usar o R para extrair dados da API to Twitter.

1. Autenticando na API

library(twitteR)
consumer_key <- "sua consumer_key"
consumer_secret <- "seu consumer_secret"
access_token <- "seu access_token"
access_secret <- "seu access_secret"
setup_twitter_oauth(consumer_key, consumer_secret, access_token, access_secret)
## [1] "Using direct authentication"

2. Listando os Trending Topics no Brasil.

trends <- getTrends(23424768) # este número se refere ao Brasil.
head(trends$name, 10) # retorna as 10 primeiras hashtags.
##  [1] "#EliminaçãoPowerCouple" "Borja"                 
##  [3] "#QuartaDetremuraSDV"    "Britto"                
##  [5] "#CorreVerdevaldo"       "Godoy Cruz"            
##  [7] "Com 15"                 "#SouDoTempoQue"        
##  [9] "DIA DE INTER"           "HOJE TEM FLAMENGO"

3. Buscando a hashtag #EliminaçãoPowerCouple

library(dplyr)

tweets <- searchTwitter('#EliminaçãoPowerCouple', n = 200, resultType="recent", lang="pt-br") %>%
  twListToDF() %>% # Converte o formato lista para data frame
  as_tibble() %>% # Converte no formato tidy
  select(id, text) # Seleciona apenas a coluna que contém o conteúdo de texto e o id

head(tweets, 5)
## # A tibble: 5 x 2
##   id               text                                                    
##   <chr>            <chr>                                                   
## 1 115389868172630… RT @JonasMattos_: Quem fez parte desses 57% da RT aqui …
## 2 115389867418501… RT @powercouple: Agora é com a Nicole e o Bimbi. Quem é…
## 3 115389856492753… "RT @eduucomenta: Eu não apareci aqui ! O site tava uma…
## 4 115389856184477… RT @JonasMattos_: Quem fez parte desses 57% da RT aqui …
## 5 115389851754871… "RT @GuiHenri190: #EliminaçãoPowerCouple  Nicole e Bimb…

4. Pré-processamento, conforme já abordado no artigo anterior sobre dados do Twitter.

library(tidytext)
library(tm)
library(ptstem)
library(ggplot2)

# Pré-processamento
tweets$text <- tweets$text %>%
  gsub(pattern = "RT ",replacement = "") %>% # Remove RT simples
  gsub(pattern = "http\\S+\\s*",replacement = "") %>% # Remove URLs
  gsub(pattern = "@\\w+: ",replacement = "") %>% # Remove @mentions
  gsub(pattern = "#\\w+ ",replacement = "") %>% # Remove #hashtags
  ptstem() # Retorno ao radical

# Formato tidy
tidytweets <- tibble(id = tweets$id, tweet = tweets$text)
head(tidytweets, 5)
## # A tibble: 5 x 2
##   id               tweet                                                   
##   <chr>            <chr>                                                   
## 1 115389868172630… "Quem fez parte desses 57% da aqui amores "             
## 2 115389867418501… "Agora é com a Nicole e o Bimbi. Quem é #TimeNicelo? "  
## 3 115389856492753… "Eu não apareci aqui ! O site tava uma merda, mas votei…
## 4 115389856184477… "Quem fez parte desses 57% da aqui amores "             
## 5 115389851754871… " Nicole e Bimbi retornam e Brasil vai a loucura/ O Bra…

5. Tokenização e bigramas

Nesta etapa temos algo diferente e para ilustrar, farei a análise de dados tradicional, com unigramas (palavras únicas) e com bigramas:

tweets_unigramas <- tweets %>%
  unnest_tokens(unigrama, text) %>%
  filter(!unigrama %in% stopwords(kind = "pt")) %>%
  filter(!unigrama == "eliminaçãopowercouple") %>% # Para remover a #hashtag de busca
  count(unigrama, sort = TRUE)

tweets_unigramas
## # A tibble: 471 x 2
##    unigrama     n
##    <chr>    <int>
##  1 nicole      57
##  2 é           44
##  3 final       41
##  4 bimbi       40
##  5 votei       31
##  6 nicelo      28
##  7 casal       22
##  8 vamos       20
##  9 vai         19
## 10 tudo        18
## # … with 461 more rows

Até aqui, não há muita novidade além de pré-processamento de dados e análise de frequência. A seguir, como calcular as bigramas:

tweets %>%
  unnest_tokens(bigrama, text, token = "ngrams", n = 2) %>%
  count(bigrama, sort = TRUE)
## # A tibble: 1,186 x 2
##    bigrama             n
##    <chr>           <int>
##  1 nicole e           41
##  2 e bimbi            27
##  3 na final           26
##  4 nicelo na          16
##  5 com tudo           13
##  6 ju e               11
##  7 57 nicelo          10
##  8 aaaaaaa 57         10
##  9 fez parte          10
## 10 final famíliaaa    10
## # … with 1,176 more rows

Note que o papel das stopwords aqui continua grande e por conta disso, precisamos de algumas etapas adicionais para filtrar as stopwords que apareçam no primeiro ou no segundo termo do bigrama. Note que usamos a função separate() do pacote tidyr que quebra a bigrama no espaço em branco entre as duas palavras, transformando a bigrama em duas colunas. Desta forma, podemos filtrar mais facilmente:

library(tidyr)

tweets_bigramas <- tweets %>%
  unnest_tokens(bigrama, text, token = "ngrams", n = 2) %>%
  separate(bigrama, c("palavra1","palavra2"), sep = " ") %>%
  filter(!palavra1 %in% stopwords(kind = "pt")) %>%
  filter(!palavra2 %in% stopwords(kind = "pt")) %>%
  filter(!palavra1 == "eliminaçãopowercouple") %>%
  filter(!palavra2 == "eliminaçãopowercouple") %>%
  count(palavra1, palavra2, sort = TRUE)

tweets_bigramas
## # A tibble: 398 x 3
##    palavra1 palavra2       n
##    <chr>    <chr>      <int>
##  1 57       nicelo        10
##  2 aaaaaaa  57            10
##  3 fez      parte         10
##  4 final    famíliaaa     10
##  5 agora    é              7
##  6 casal    elimina        6
##  7 casal    final          6
##  8 contra   todas          6
##  9 é        timenicelo     6
## 10 final    elimi          6
## # … with 388 more rows

Este procedimento pode ser repetido para trigramas também de forma análoga. E você pode prosseguir com a elaboração de gráficos usando ggplot(), conforme já demonstrado exaustivamente nos artigos anteriores.

Rede de Bigramas

A metodologia anterior é interessante para visualizar as bigramas mais frequentes, mas seria interessante poder ter uma visão mais ampla destas relações entre palavras. A seguir, mostro como podemos ter essa visão geral usando uma rede de palavras com os pacotes igraph e ggraph.

library(igraph)
library(ggraph)

tweets_bigramas %>%
  filter(n > 2) %>% # Filtrando bigramas que aparecem mais do que 2 vezes, pelo menos.
  graph_from_data_frame() %>% # Gera as relações direcionais entre palavras.
  ggraph(layout = "fr") +
  geom_edge_link(alpha = 0.5) + # Alpha para adicionar transparência as retas.
  geom_node_point(color = "lightblue", 
                  size = 3, alpha = 0.5) + # Definindo cor e tamanho dos pontos.
  geom_node_text(aes(label = name), 
                 vjust = 1, hjust = 1) + # vjust, hjust para deslocar os textos.
  theme_void() # Remover eixos e cor do fundo.

Esta rede mostra algumas relações bem interessantes até sobre a estrutura de como os textos são escritos e enviados no Twitter. Mensagens de apoio, como por exemplo, “pessoal foquem somente”, ou reclamação como “terceiros típica participacão mediocre”. Note que estamos praticamente construindo a frase a partir de bigramas.

No entanto, ainda tem algo faltando neste gráfico: a direção das palavras (Palavra 1 –> Palavra 2). Para isso, precisamos de um código um pouquinho mais complexo:

set.seed(1234)
a <- grid::arrow(type = "closed", length = unit(.10, "inches"))

tweets_bigramas %>%
  filter(n > 2) %>% # Filtrando bigramas que aparecem mais do que 2 vezes, pelo menos.
  graph_from_data_frame() %>% # Gera as relações direcionais entre palavras.
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = n), show.legend = FALSE, 
                 arrow = a, end_cap = circle(0.07,"inches")) + 
  geom_node_point(color = "lightblue", 
                  size = 3, alpha = 0.5) + # Definindo cor e tamanho dos pontos.
  geom_node_text(aes(label = name), 
                 vjust = 1, hjust = 1) + # vjust, hjust para deslocar os textos.
  theme_void() # Remover eixos e cor do fundo.

Neste gráfico acima temos 2 elementos novos:

  1. As setas indicam a direção das bigramas: Palavra 1 –> Palavra 2.
  2. As setas com cores mais escuras indicam bigramas mais frequentes. Note que: casal_eliminado, casal_final e fez_parte são as bigramas mais frequentes na tabela de frequência e aqui também possuem as setas mais escuras.

Considerações Finais

Neste artigos vimos o importante papel das bigramas em adicionar significado a um conjunto de textos, indo além da simples análise de frequência de palavras. Essa metodologia é útil até mesmo em conjunto de textos curtos, como do Twitter. Também vimos que é possível visualizar essas relações em formato de rede, demonstrando expressões mais usuais dentro deste domínio.