class: center, middle, inverse, title-slide # Introdução à mineração de texto com R: Análise de Sentimentos ### Prof. Carlos Trucíos
ctruciosm.github.io
carlos.trucios@facc.ufrj.br
### Faculdade de Administração e Ciências Contábeis, Universidade Federal do Rio de Janeiro ### Última atualização: 2021-11-28 --- layout: true <a class="footer-link" href="http://ctruciosm.github.io">ctruciosm.github.io — Carlos Trucíos (FACC/UFRJ)</a> ---
### Análise de sentimentos: pacotes necessários ```r library(dplyr) library(tidytext) library(textdata) library(stringr) library(lexiconPT) library(tm) library(rtweet) library(wordcloud) library(reshape2) ``` --- ### Análise de sentimentos > Quando lemos um texto utilizamos nosso conhecimento das palavras para entender se, por exemplo, o texto tem um sentido positivo ou negativo. -- O computador não consegue entender o texto assim como nós o entendemos, mas existem métodos que nos ajudam a `entender` o sentimento contido no texto. Estes métodos utilizam dicionários (léxicos) para atribuir um sentimento a cada palavra. -- Em inglês existem vários dicionários! .panelset[ .panel[.panel-name[AFINN] ```r head(get_sentiments("afinn")) ``` ``` ## # A tibble: 6 × 2 ## word value ## <chr> <dbl> ## 1 abandon -2 ## 2 abandoned -2 ## 3 abandons -2 ## 4 abducted -2 ## 5 abduction -2 ## 6 abductions -2 ``` ] .panel[.panel-name[BING] ```r head(get_sentiments("bing")) ``` ``` ## # A tibble: 6 × 2 ## word sentiment ## <chr> <chr> ## 1 2-faces negative ## 2 abnormal negative ## 3 abolish negative ## 4 abominable negative ## 5 abominably negative ## 6 abominate negative ``` ] .panel[.panel-name[NCR] ```r head(get_sentiments("nrc")) ``` ``` ## # A tibble: 6 × 2 ## word sentiment ## <chr> <chr> ## 1 abacus trust ## 2 abandon fear ## 3 abandon negative ## 4 abandon sadness ## 5 abandoned anger ## 6 abandoned fear ``` ] ] --- ### Análise de sentimentos Infelizmente, esses dicionários só funcionam em inglês 😭 e não podem ser utilizados no nosso contexto. Uma alternativa é utilizar o pacote `lexiconPT` que contêm três dicionários (mas alguns ajustes devem ser feitos para podermos utilizar o pacote.) -- ```r library(lexiconPT) get_word_sentiment("ruim") ``` ``` ## $oplexicon_v2.1 ## term type polarity ## 26960 ruim adj 1 ## ## $oplexicon_v3.0 ## term type polarity polarity_revision ## 28325 ruim adj -1 M ## ## $sentilex ## term grammar_category polarity polarity_target polarity_classification ## 6147 ruim Adj -1 N0 MAN ``` -- A palavra terá algum sentimento (positivo/negativo). -- > Pode acontecer de não encontrarmos alguma palavra? Pode sim! --- ### Análise de sentimentos Se quisermos ver o que tem em cada um dos três dicionários, basta fazer: ```r oplexicon_v2.1 oplexicon_v3.0 sentiLex_lem_PT02 ``` Por exemplo: ```r head(oplexicon_v3.0) ``` ``` ## term type polarity polarity_revision ## 1 =[ emot -1 A ## 2 =@ emot -1 A ## 3 =p emot -1 A ## 4 =P emot -1 A ## 5 =x emot -1 A ## 6 =d emot 1 A ``` --- ### Análise de sentimentos Antes de continuar, precisamos carregar as funções feitas na reunião anterior. -- ```r stop_words_pt <- stopwords("portuguese") %>% union(c("é", "pra", "pro", "vc", "vcs")) %>% as_tibble() %>% unnest_tokens(word, value) ``` ```r clean_tweets <- function(x) { x %>% str_remove_all(" ?(f|ht)(tp)(s?)(://)(.*)[.|/](.*)") %>% str_remove_all("@[[:alnum:]_]{4,}") %>% str_remove_all("#[[:alnum:]_]+") %>% str_replace_all("&", "e") %>% str_remove_all("^RT:? ") %>% str_replace_all("\\\n", " ") %>% str_trim("both") } ``` --- ### Análise de sentimentos Criaremos a função `get_polaridade_vec()` para pegar a polaridade (pontuação que nos indica se um _token_ é positivo ou negativo) utilizando os dicionários `oplexicon_v3.0` e `sentilex`. O código da função está no seguinte slide. -- ```r exemplo <- data.frame(textos = c("Carlos é bom jogador", "Carlos é um jogador ruim")) exemplo ``` ``` ## textos ## 1 Carlos é bom jogador ## 2 Carlos é um jogador ruim ``` ```r exemplo_tokens <- exemplo %>% unnest_tokens(word, textos) %>% anti_join(stop_words_pt, by = "word") exemplo_tokens %>% mutate(polaridade = get_polaridade_vec(word)) ``` ``` ## word polaridade ## 1 carlos 0 ## 2 bom 1 ## 3 jogador 0 ## 4 carlos 0 ## 5 jogador 0 ## 6 ruim -1 ``` --- ### Análise de sentimentos ```r get_polaridade <- function(x) { sentimento <- get_word_sentiment(x) texto <- "Word not present in dataset" polaridades <- c(ifelse(sentimento$oplexicon_v3.0 == texto, 0, sentimento$oplexicon_v3.0$polarity), ifelse(sentimento$sentilex == texto, 0, sentimento$sentilex$polarity)) return(sign(sum(polaridades))) } get_polaridade_vec <- Vectorize(get_polaridade, SIMPLIFY = FALSE) ``` --- ### Análise de sentimentos .panelset[ .panel[.panel-name[Tweets] ```r twitter <- search_tweets("CPI", n = 200, lang = "pt", include_rts = FALSE) ``` ] .panel[.panel-name[Limpeza] ```r twitter <- twitter %>% mutate(text = clean_tweets(text)) ``` ] .panel[.panel-name[Tokens] ```r twitter_token <- twitter %>% dplyr::select(text) %>% unnest_tokens(word, text) ``` ] .panel[.panel-name[Stop Words] ```r twitter_token <- twitter %>% dplyr::select(text) %>% unnest_tokens(word, text) %>% anti_join(stop_words_pt, by = "word") ``` ] .panel[.panel-name[Sentimentos] ```r token_sentiments <- twitter_token %>% mutate(polaridade = unlist(get_polaridade_vec(twitter_token$word))) %>% mutate(sentimento = factor(polaridade, levels = c(-1,0,1), labels = c("Negativo", "Neutro", "Positivo"))) token_sentiments_counts <- token_sentiments %>% count(word, polaridade, sentimento, sort = TRUE) token_sentiments_counts ``` ``` ## # A tibble: 1,480 × 4 ## word polaridade sentimento n ## <chr> <dbl> <fct> <int> ## 1 cpi 0 Neutro 181 ## 2 covid 0 Neutro 27 ## 3 q 0 Neutro 21 ## 4 circo 0 Neutro 19 ## 5 vai 0 Neutro 19 ## 6 senadora 0 Neutro 16 ## 7 bolsonaro 0 Neutro 15 ## 8 ser 1 Positivo 15 ## 9 tá 0 Neutro 12 ## 10 agora 0 Neutro 11 ## # … with 1,470 more rows ``` ] ] --- ### Análise de sentimentos ```r library(reshape2) token_sentiments_counts %>% filter(polaridade != 0) %>% acast(word ~ sentimento, value.var = "n", fill = 0) %>% comparison.cloud(colors = c("red4", "green4"),max.words = 100) ``` --- ### Análise de sentimentos <img src="IMTR3_files/figure-html/unnamed-chunk-19-1.png" width="100%" /> --- ### Análise de sentimentos Análise de sentimentos feito palavra a palavra é útil. Contudo, às vezes pode ser vantajoso utilizar algoritmos que sejam capazes de interpretar frases tais como: - Eu não sou um bom jogador - Eu não gostei do atendimento - Pessoal nada atencioso Como frases negativas. -- .blue[Pacotes como `coreNLP`, `cleanNLP` e `sentimentr` ajudam nesse trabalho. Infelizmente ainda ha muito a ser feito na lingua portuguesa (pesquisas em processamento da linguagem natural e em linguística que caminham nessa direção são importantes para nós).] -- Uma alternativa simples é somar a polaridade de cada frase. Se a soma for positiva então o sentimento é positivo, se a soma for negativa então o sentimento é negativo. --- ### Análise de sentimentos Análise completa: -- ```r twitter <- search_tweets("CPI", n = 200, lang = "pt", include_rts = FALSE) %>% mutate(text = clean_tweets(text)) twitter_token <- twitter %>% dplyr::select(status_id, text) %>% unnest_tokens(word, text) %>% anti_join(stop_words_pt, by = "word") %>% mutate(polaridade = unlist(get_polaridade_vec(word))) sent_by_tweet <- twitter_token %>% group_by(status_id) %>% summarise(sentimento = sum(polaridade)) twitter_sent <- twitter %>% left_join(sent_by_tweet, by = "status_id") twitter_sent <- twitter_sent %>% mutate(sentimento = ifelse(sentimento > 0, "Positivo", ifelse(sentimento < 0, "Negativo", "Neutro"))) ``` -- .red[Inspecione o que temos no objeto `twitter_sent`.] --- ### Análise de sentimentos - Em inglês existem dicionários específicos por área de pesquisa, mas em portugues ainda precisamos de mais pesquisa no assunto. -- - Para nossos fins, podemos utilizar os dicionários contidos no pacote `lexiconPT` e incluir algumas palavras/jargões específicos da área de pesquisa em questão. -- - O dicionário [ReLi](https://www.linguateca.pt/Repositorio/ReLi/) apresenta mais palavras com conotações positivas e negativas, mas essas palavras precisam ser importadas ao R de forma _manual_. [Esse post aqui faz isso](https://rpubs.com/giuice/analisesentimentos1). -- - Embora na internet possam existir vários dicionários (tome cuidado, qualquer um poderia fazer um dicionários), eu prefiro dicionários que são fruto de algum trabalho de pesquisa (pelo menos podemos citar que usamos os lexicos propostos por determinador autores). --- ### Data-Tips: .pull-left[ <img src="https://octodex.github.com/images/minertocat.png" width="70%" /> ] .pull-right[ O livro [Text Mining with R](https://www.tidytextmining.com) contém bastante informação para fazermos mineração de texto. Nas próximas reuniões veremos os pontos mais importantes. O livro [Supervised Machine Learning for Text Analysis in R](https://smltar.com) presenta vários tópicos de modelagem de dados com dados textuais. O livro [Textual Data Science with R](https://www.routledge.com/Textual-Data-Science-with-R/Becue-Bertaut/p/book/9781032093659) discute vários conceitos teóricos e pode ser um ótimo complemento. ]