Artboard 2 copy 4

Análise de Sentimento e Nuvem de Palavras

Artigo feito em colaboração com Danielly Santos

A análise de sentimentos e a nuvem de palavras são duas ferramentas de análise textual que podem interagir por meio da bipolaridade dos comentários. Isto é, a distinção entre sentimentos positivos e negativos. Embora tenham abordagens diferentes, esses métodos compartilham alguns aspectos em comum.

A análise de sentimentos tem como objetivo identificar se um comentário transmite uma mensagem geral positiva ou negativa. A pergunta central é: “O sentimento predominante no comentário é positivo ou negativo?”. Essa técnica possui diversas utilidades, tais como identificar a satisfação dos clientes em relação a um determinado produto, resumir opiniões, avaliações ou recomendações, e analisar o contexto atual das mídias.

Portanto, tanto a análise de sentimentos quanto a nuvem de palavras são ferramentas valiosas para extrair informações e insights de textos. Enquanto a análise de sentimentos busca identificar o sentimento geral de um comentário, a nuvem de palavras permite visualizar as palavras mais frequentes em um texto, proporcionando uma compreensão rápida do conteúdo. Ambas as técnicas podem ser utilizadas em conjunto para uma análise textual mais completa e abrangente.

Etapas da análise

Para realizar a análise de sentimentos, podemos utilizar técnicas de Machine Learning. Uma abordagem comum é dividir a base de dados em duas partes: uma para treinamento (75% dos dados) e outra para avaliação dos resultados (25% dos dados). Assim, parte dos dados é usada para treinar o algoritmo classificador, enquanto a outra parte é utilizada para validar os resultados obtidos.

Para criar um classificador de sentimentos, podemos seguir alguns passos. Primeiramente, criamos uma lista com todas as palavras presentes no banco de dados. Suponhamos que as palavras contidas em todos os comentários sejam: “eu”, “admiro”, “e”, “amo”, “muito”, “a”, e “marca”. Sendo assim, uma lista com as frequências dessas palavras representa cada comentário.

Por exemplo, se tivermos o seguinte comentário: “eu amo a marca”, a lista de frequências será: (1, 0, 0, 1, 0, 1, 1). No exemplo, temos um grupo geral com 7 palavras, formando uma lista com 7 possíveis elementos onde cada palavra ocupa uma posição específica. No caso particular da frase “eu amo a marca”, cada palavra em comum presente na frase recebe o valor 1 na posição adequada da lista e 0, caso contrário. Se a frase fosse “eu admiro e amo muito a marca” a lista seria toda preenchida com 1.

Quando uma determinada apalavra aparece várias vezes, trocamos o 1 pela quantidade de ocorrências na frase.

A próxima etapa é calcular a pontuação de sentimentos positivos e negativos para cada palavra. Todas as palavras da base de dados receberão uma pontuação positiva e outra negativa. Aqui, podemos utilizar o algoritmo Naive Bayes, que utiliza essas pontuações internamente.

A pontuação (positiva ou negativa) de cada palavra em um comentário é multiplicada, assim como o total. Ao final, uma pontuação positiva e uma negativa serão comparadas para determinar o sentimento final. Por exemplo, se Pfinal (Pontuação Final) > 1, o sentimento predominante é positivo. Se Pfinal < 1, o sentimento predominante é negativo. Também podemos adotar um intervalo em que Pfinal = 1, indicando um sentimento predominantemente neutro.

Implementação no R

Para a implementação na linguagem R, alguns passos são importantes no processo. Os pacotes TM, DevTools e SentR são necessários para a realização da análise, além disso, usaremos dois vetores, um apenas com palavras positivas, e outro apenas com palavras negativas; as palavras possuem pontuações 1 (para positivas) ou -1 (para negativas). Por fim, é importante estar atento à língua na qual o comentário foi escrito. Isso se deve ao fato de que a língua inglesa é predominante nessa função.

require('tm')

Loading required package: tm
Loading required package:| NLP
require('devtools')

Loading required package: devtools
Loading required package: usethis
#install github( 'mananshaho9/sentR')
require('sentR')

Loading required package: sentR

positive <- c('happy', 'loved', 'good', 'recommend', 'quality')
negative <- c('sad', 'bad', 'terrible', 'not', 'poor')

# Soma simples

classify.aggregate(c("I am happy", "I am sad”), positive, negative)
Loading required package: plyr
Loading required package: stringr
score text
1  1 I am happy
2 -1 I am sad

classify.naivebayes(c("I am happy", "I am sad”))

POS                       NEG                POS/NEG             SENT
[1,] "9.47547003995745" "0.445453222112551” “21.2715265477714"  “positive”
[2,] "1.03127774142571” "9.47547003995745" "0.108836578774127""negative”
classify.naivebayes(c("Eu estou feliz”, "Eu estou triste"))

POS                        NEG                POS/NEG             SENT
[1,] "1.03127774142571" "0.445453222112551” "2.31512017476245"positive”
[2,] "1.03127774142571” "0.445453222112551"2.31512017476245” "positive”

Pacote SentimentAnalysis

Contudo, quando a análise for feita com o pacote SentimentAnalysis, a mesma irá possuir algumas especificidades. As análises, quando feitas em português, tendem a ser mais positivas.

library(Sentimentanalysis)

Attaching package: 'Sentimentanalysis'
The following object is masked from 'package:base':

write

sentiment1 <- analyzeSentiment(c("I am happy", “I am sad"), language="english")

convertToBinaryResponse(sentiment1)$SentimentGI h

[1] positive negative
Levels: negative positive

convertToDirection(sentiment1$SentimentQDAP)

[1] positive negative
Levels: negative neutral positive

sentiment2 <- analyzeSentiment(c("Eu estou feliz”, “Eu estou triste”), language="portuguese")
convertToBinaryResponse(sentiment2)$SSentimentGI

[1] positive positive

Levels: negative positive

convertToDirection(sentiment2$SentimentQDAP)

[1] neutral neutral

Levels: negative neutral positive

Comparando Funções da análise

Vamos comparar as funções utilizadas para realizar a análise de sentimentos dos pacotes sentR e
SentimentAnalysis. Para isso, vamos carregar agora um arquivo com a polaridade (1/-1) e o sentimento de várias palavras em português.

A base de dados a seguir possui a polaridade e o sentimento de vários adjetivos em português e o sentimento que cada uma transmite. As palavras duplicadas foram removidas e foi feita a divisão entre positivas e negativas.

dfPolaridades <- readxl: :read xlsx("C:/Users/DataTalker/OneDrive/Área de Trabalho/dados seminario.xlsx"
sheet = 3)
#removendo palavras repetidas
polaridades pt <- dfPolaridades[!duplicated(dfPolaridades$word), ]
head(polaridades pt)

#A tibble: 6 x 4
word                         polaridade tipo              sentimento
<chr>                             <dbl> <chr>                       <chr>
1 aborrecente                                         -1 adjetivo negativo
2 anacrônico                                          -1 adjetivo negativo
3 besta                                               -1 adjetivo negativo
4 bizarro                                             -1 adjetivo negativo
5 bobo                                                -1 adjetivo negativo
6 burro                                               -1 adjetivo negativo

positivas <- polaridades pt %>%
dplyr: : filter(sentimento == 'positivo') %%
dplyr: : select(nord)
nrow(positivas)

[1] 228

negativas <- polaridades pt %>%
dplyr: : filter(sentimento -- 'negativo') %%
dplyr: : select(nmord)
nrow(negativas)

[1] 216 

Vamos, então, comparar o desempenho das funções:

sentR: : classify.naivebayes(positivas)[,4] %>% table()

positive
        228
sentR: :classify.naivebayes(negativas)[,4] %>% table()
negative positive
               3           213
               
sentimento1 <- analyzeSentiment(positivas$word, language="portuguese")
convertToBinaryResponse(sentimento1)$SentimentGI %% table()

negative positive 
                2           225
                
convertToDirection(sentimento1$SentimentQDAP) %>% table()

negative neutral positive
                 0        222               5
                 
sentimento2 <- analyzeSentiment (negativas$word, language="portuguese”)
convertToBinaryResponse(sentimento2)$SentimentGI %% table()

negative positive 
                1          215
                
convertToDirection(sentimento2$SentimentODAP) %% table()

negative neutral positive
                2        213                1

Na primeira classificação, ao analisar palavras positivas, todas foram classificadas corretamente; já as palavras negativas obtiveram algumas classificações incorretas. Na segunda análise, tudo ocorreu bem com as classificações positivas. Por outro lado, quando as palavras negativas apareciam, eram classificadas com polaridade neutra. Portanto, houve uma dificuldade em classificar comentários negativos na língua portuguesa utilizando esses pacotes.

Nuvem de palavras

Uma nuvem de palavras pode ser usada para analisar visualmente quais foram as palavras mais frequentes. Dentre as utilidades estão a visualização mais dinâmica dos comentários e a bipolarização destes (positivos/negativos). Os comentários no banco de dados geralmente precisam de um tratamento especial. Então vamos definir uma função para fazer a limpeza necessária dos comentários e outra para criar as nuvens de palavras.

limpar resposta <- function(respostas){
texto <- respostas %%
# Convertendo o texto para minúsculo
tolower() %5%
# Removendo as pontuações
removePunctuation()
# Removendo espaços no início do texto
texto <- gsub("** ",", texto)
# Removendo espaços no final do texto
texto <- gsub($", "", texto)
# Convertendo os textos em corpus
texto <- VCorpus(VectorSource(texto))
# Removendo numeros
texto <- tm map(texto, removeNumbers)
# Removendo stopwords
texto <- texto %% tm map(removewords, stopwords(“portuguese”))
texto <- texto %% tm map(removewords, c("gente", "então”, "inint", "acho”, “mim", "vai"))
# Removendo espaços extras h
texto <- texto %>% tm map(stripWhitespace)
aux1 <- TermDocumentMatrix(texto) %%
as.matrix()
aux1 <- sort(rowSums(aux1), decreasing=TRUE)
# Criando data frame de palavras e frequencias
nuvem <- data.frame(word = names(aux1), freg=aux1)
nuvemsuord <- as.character(nuvemsword)
return(nuvem)
}

plotar nuvem <- function(resposta limpa, min = 4,
max = 70, escala = c(3,.5)) {
# Lista de cores em hexadecimal
paleta <- brewer.pal(8, "Dark2")
mwordeloud: :wordeloud(words - resposta limpasuord,
freg = resposta limpasfreg,
min.freg = min,
max.words = max,
random.order = FALSE,
colors = paleta,
scale = escala)
}

Vamos, dessa forma, aplicar as funções:

require('wordcloud')
Loading required package: wordcloud
Loading required package: RColorBrewer
dados <- readxl::read_xlsx("C:/Users/DataTalker/OneDrive/Área de Trabalho/dados_seminario.xlsx")
head(dados)
#A tibble: 6x5
     ID Idade Sexo           "Qual sua opinião sobre nosso produto?”                   Sentimento

<dbl> <dbl> <chr> <chr> 
1   1      31     F — Adorei! Entrega rápida, ótima qualidade, super f~              positivo
2   2      34     F — Não, eu não recomendo esse produto ~                                    negativo
3   3      33     F — A lavadora não esquenta a água e o filtro é de p~                negativo
4   4      46     M — Amei estou apaixonada por minha nova AMIGA kkk ,~ positivo
5   5      43     M Ótimo produto. A entrega foi super rápida, recebv~            positivo
6   6      37     F linda, design maravilhoso, vidro temperado, vidr~                  positivo

texto <- limpar resposta(dados[,4])
head(texto)
           word        freq
produto   produto        8
entrega   entrega        6
super     super        6
fiapos    fiapos        5
linda     linda        5
bem       bem      4
texto$word <- texto$word X5%

removewords(c(“fica"))

set.seed(1537)
plotar_nuvem(texto, min = 2, max = 20)

Perceba que, nesse caso, a cor é a mesma para cada grupo de palavras com exatamente a mesma frequência de ocorrências. Outra função pode ser utilizada, veja abaixo:

require('wordcloud2')

Loading required package: wordcloud2

wordcloud2: :wordcloud2(data = texto, #data com palavras e frequencias
                                                            size = 1, #tamanho da fonte
                                                            color = “random-dark", #cor do texto
                                                            fontweight = 'bold', #cor de fundo
                                                            shape = 'circle', #forma da nuvem,
                                                            shuffle - FALSE

)

O resultado será uma nuvem de palavras interativa. Nesse caso, cores diferentes para um grupo de palavras com o mesmo número de citações é um padrão definido que não depende do uso desses pacotes específicos.

Uma forma de utilizar a função wordcloud2 seria definir o argumento “data” apenas com as palavras que desejamos visualizar, separando apenas as palavras mais citadas. Caso contrário, todas as palavras estarão na imagem e podem poluir a visualização. Vamos, então, ver o resultado da análise de sentimentos para esse texto:

sentimentos <- analyzeSentiment(dados$'Qual sua opinião sobre nosso produto?”,
language="portuguese”")
convertToBinaryResponse(sentimentos)$SentimentGI %>% table()

negative   positive
                1               14

table(dados$Sentimento)

negativo   positivo
               5                10

Possível Interação

Após realizar a análise de sentimentos podemos criar uma nuvem de palavras onde os sentimentos são definidos pela cor da palavra. Podemos, por exemplo, criar uma nuvem de palavras com cores frias para palavras que expressaram sentimento negativo e cores quentes para palavras que expressaram sentimento positivo.

dfPolaridades <- readxl::read_xlsx("C:/Users/DataTalker/OneDrive/Área de Trabalho/dados_seminario.xlsx",
sheet = 3)

# Removendo palavras repetidas

polaridades_pt <- dfPolaridades[!duplicated(dfPolaridades$word), ]

head(polaridades_pt)

# A tibble: 6x4

word                     polaridade tipo           sentimento
<chr>                  <dbl> <chr>                   <chr>

1 aborrecente      -1 adjetivo                 negativo

2 anacrônico         -1 adjetivo                  negativo

3 besta                     -1 adjetivo                   negativo

4 bizarro                 -1 adjetivo                   negativo

5 bobo                     -1 adjetivo                    negativo

6 burro                   -1 adjetivo                     negativo

sentJoin <- texto %>%
dplyr::inner join(polaridades pt, by='word')
library(reshape2)
Attaching package: 'reshape2'
The following object is masked from 'package:tidyr':

smiths

Llibrary(wordcloud)
sentJoin %>%

dplyr: : count(word, sentimento, sort = TRUE) %>%

acast(word - sentimento, value.var = "n", fill = 0) %>%

comparison.cloud(colors = c("#F8766D”, "#00BFC4”),

max.mords = 15)

Warning in comparison.cloud(., colors = c("#F8766D”, "#00BFC4”), max.words =
15): perfeito could not be fit on page. It will not be plotted.

Neste caso o resultado mostra palavras positivas na cor azul, e negativas na cor vermelha. Ainda restou alguma dúvida? Deixe seu comentário aqui embaixo e ficaremos felizes em responder! Até a próxima!

Share the Post:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Related Posts

Pesquisa Operacional

Artigo feito em colaboração com Valéria Nicéria A Pesquisa Operacional, ou PO, ganhou destaque durante a Segunda Guerra Mundial. Devido

Read More