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!