1. Introdução

O avanço das tecnologias e o surgimento das redes sociais, smartphones e tablets, tornam os videogames ainda mais populares e acessíveis a diversos públicos. O número de jogadores em todo o mundo ainda está aumentando e ultrapassará três bilhões até 2023. O mercado global de videogames deve gerar cerca de $159,3 bilhões em 2020, com um aumento anual de +9,3% (NewZoo 2020).

Para ter sucesso neste mercado competitivo, um desenvolvedor de jogos precisa saber os motivos que levam os clientes a jogar um determinado jogo e entender como manter seus jogadores envolvidos. Com a popularização da internet obter essas informações se tornou mais fácil, jogadores podem expressar sua opinião de forma livre, fornecendo feedbacks e análises que podem ser acessados por meio de redes sociais e plataformas de jogos.

Os comentários sobre jogos são uma fonte rica de opiniões e sentimentos do usuário, dando a outros jogadores uma ideia sobre o que esperar do jogo e influenciando de forma significativa a decisão de compra (Livingston, Nacke, and Mandryk 2011). Conforme relatado na literatura, os sentimentos expressos pelos usuários sobre diferentes aspectos do jogo têm forte correlação com a aceitação do jogo pelo usuário (A. Vieira and Brandão 2019; Strååt and Verhagen 2017).

Neste contexto, abordagens de análise de sentimento foram recentemente adotadas para avaliar a aceitação de videogames pelos usuários (A. Vieira and Brandão 2019; A. de C. Vieira and Brandão 2019; Chaves 2019). A Análise de Sentimentos (AS) é um processo automatizado que usa Inteligência Artificial para analisar documentos textuais e identificar sentimentos e opiniões. Por meio de abordagens AS, é possível explorar os comentários de jogos automaticamente, identificando as taxas de aceitação do usuário contidas nessas análises. Quando um comentário expressa sentimentos e opiniões positivas, isso significa que o jogo é bem aceito. Os comentários negativos permitem que a equipe de desenvolvimento de jogos saiba quais são considerados, por alguns usuários, os pontos mais fracos de um jogo, orientando futuras decisões de design de jogos e melhorias.

Neste trabalho, comentários de jogos extraídos da Plataforma Steam são analisados usando técnicas de processamento de linguagem natural, buscando entender as características dos documentos que expressão sentimentos positivos e negativos e avaliar pontos significativos levantados pelos jogadores a respeito de diversos aspectos dos jogos.

2. Pacotes Requeridos

Para este projeto, alguns pacotes precisaram ser instalados e importados, esses pacotes e os motivos para sua utilização são listados abaixo.

  • dplyr - Manipulação avançada de estruturas de dados como Dataframes;
  • DT - Visualização de dados em tabelas de forma mais prática e dinâmica;
  • rmarkdown - Utilizado para geração do relatório;
  • syuzhet - Análise de sentimentos em documentos textuais;
  • textreuse e tm - Processamento de texto;
  • wordcloud - Geração de nuvens de palavras.

3. Preparação dos Dados

O conjunto de dados adotado neste trabalho é composto por cometnários de jogos extraídas da Steam usando sua API Web. Esses comentários, todos em em português do Brasil, expressam o que os jogadores pensam e como se sentem sobre os jogos. Além das análises, é extraída ainda a avaliação dada pelo jogador para o jogo. Essa avaliação é dada através de uma funcionalidade da plataforma que permite ao jogador recomendar ou não o jogo avaliado. Exemplos de comentários da Steam podem ser vistos nas imagens abaixo.

Recomendado Não Recomendado

O conjunto de dados está disponível em um repositório público, sem nenhum processamento para dar aos pesquisadores a liberdade de escolher quais etapas de pré-processamento são mais adequadas às suas pesquisas.

3.1 Carregando os dados

A base de dados está disposta em dois arquivos de texto: pos e neg, que contém os comentários com polaridade positiva e negativa, respectivamente. Nos arquivos, cada linha equivale a um comentário. Essas linhas são lidas através do comando readLines e carregadas em um dataframe. Através da função mutate do pacote dplyr, a polaridade do comentário é adicionado ao dataframe, (1 para comentários positivos e 0 para os negativos). Todo o código para leitura da base de dados pode ser visto abaixo.

set.seed(1234)
shuffle <- function(dataframe)
{
  rows <- sample(nrow(dataframe))
  if (ncol(dataframe)==1){
    dataframe <- data.frame(text=dataframe[rows, ])
    dataframe$text <- as.character(dataframe$text)
  }else{
    dataframe <- dataframe[rows, ]
  }
  return (dataframe)
}
p <- data.frame(text=readLines("/home/larissa/workspace/analise-dados-steam/pos"))
pos <- head(shuffle(p),10000)
pos <- mutate(pos, class=1)
pos <- mutate(pos, raw=pos$text)

n <- data.frame(text=readLines("/home/larissa/workspace/analise-dados-steam/neg"))
neg <- head(shuffle(n), 10000)
neg <- mutate(neg, class=0)
neg <- mutate(neg, raw=neg$text)

data <- rbind(pos, neg)
data <- shuffle(data)


data_size <- nrow(data)
n_words <- length(unique(tokenize_words(paste(data$text, sep = " ", collapse = " "))))

3.2 Visualização dos Dados

A base de dados carregada é composta por mais de 70 mil comentário, entre comentários positivos e negativos onde 20000 comentários foram selecionados aleatoriamente para este trabalho. A base de dados pode ser vista na tabela abaixo. Para fins de melhor visualização, os comentários com mais de 200 caracteres foram filtrados.

datatable(filter(data, nchar(text) < 200), rownames = F)
## Warning in instance$preRenderHook(instance): It seems your data is too big
## for client-side DataTables. You may consider server-side processing: https://
## rstudio.github.io/DT/server.html

3.3 Preprocessamento

Uma das etapas mais fundamentais na classificação de textos é o pré-processamento dos documentos textuais, que inclui a limpeza dos dados textuais, que removerá informações irrelevantes e quaisquer outros ruídos que possam piorar o entendimento e análise dos documentos. Para ilustrar cada etapa do préprocessamento, um comentário significativo foi selecionado para demonstrar o efeito de cada um dos passos executados:

text class raw
5101 Muito bom e complexo, 12 hrs de jogo e ainda estou aprendendo as mecanicas basicasA Trilha Sonora, nem comento, perfeito!< 3 1 Muito bom e complexo, 12 hrs de jogo e ainda estou aprendendo as mecanicas basicasA Trilha Sonora, nem comento, perfeito!< 3

Neste trabalho, as seguintes etapas foram realizadas para o pré-processamento dos dados.

3.3.1 Conversão de Letras para Minúsculas

Uma abordagem básica no pré-processamento de textos e de grande impacto, foi adotada neste trabalho: a conversão de todas as letras maiúsculas dos documentos em minúsculas. Essa conversão elimina o problema de termos que, apesar de iguais, são considerados diferentes por algoritmos, como “jogo” e “Jogo”.

data$text <- sapply(data$text, tolower)
text class raw
5101 muito bom e complexo, 12 hrs de jogo e ainda estou aprendendo as mecanicas basicasa trilha sonora, nem comento, perfeito!< 3 1 Muito bom e complexo, 12 hrs de jogo e ainda estou aprendendo as mecanicas basicasA Trilha Sonora, nem comento, perfeito!< 3

3.3.2 Remoção de Caracteres Especiais

Todos os caracteres especiais dos documentos foram removidos, como pontuação, símbolos e dígitos. No nosso contexto, esses caracteres não são significativos e não indicam polaridade de sentimentos, sendo totalmente descartáveis. Além disso, espaços em brancos desnecessários também são removidos.

removeNumPunct <- function(x){
  x = gsub("[^[:alnum:]]", " ",x)
  return (stripWhitespace(x))
}
data$text <- sapply(data$text, removePunctuation)
data$text <- sapply(data$text, removeNumbers)
data$text <- sapply(data$text,  removeNumPunct)
text class raw
5101 muito bom e complexo hrs de jogo e ainda estou aprendendo as mecanicas basicasa trilha sonora nem comento perfeito 1 Muito bom e complexo, 12 hrs de jogo e ainda estou aprendendo as mecanicas basicasA Trilha Sonora, nem comento, perfeito!< 3

3.3.3 Remoção de Stop Words

Stop words são palavras consideradas pouco significativas, que ocorrem com alta frequência em um documento (Kaur and Buttar 2018), como conjunções, determinantes e preposições. A frequência desses termos podem dificultar a análise dos documentos, sendo, por isso, removidos.

data$text <- sapply(data$text,  removeWords, words=stopwords('portuguese'))
data$text <- sapply(data$text,  stripWhitespace)
text class raw
5101 bom complexo hrs jogo ainda aprendendo mecanicas basicasa trilha sonora comento perfeito 1 Muito bom e complexo, 12 hrs de jogo e ainda estou aprendendo as mecanicas basicasA Trilha Sonora, nem comento, perfeito!< 3

Por fim, a base de dados já processada conta com um total de 20000 documentos e 46904 palavras únicas, contendo dados mais representativos, o que facilitará significativamente a análise dos mesmos.

4. Análise Exploratória dos Dados

Um ponto muito importante para análise de sentimentos é entender o que caracteriza os documentos que contém diferentes polaridades de sentimentos, conhecer essas características facilita a análise automática dos dados e trás informações importantes para as partes interessadas. Através de alguns dados quantitativos extraídos da base de dados bruta podemos entender o que distingue um comentário negativo de um positivo, e assim obter informações relevantes.

#Extraindo dados relavantes da base para análise  

classes = c("Positivo", "Negativo")

#Filtrando e selecionando os documentos textuais de cada classe
positive_texts_raw <- select(filter(data, class==1), raw)
negative_texts_raw <- select(filter(data, class==0), raw)
positive_texts_raw$raw <- as.character(positive_texts_raw$raw)
negative_texts_raw$raw <- as.character(negative_texts_raw$raw)
positive_texts <- select(filter(data, class==1), text)
negative_texts <- select(filter(data, class==0), text)


info <- c("Nº Documentos", "Nº Palavras", "Palavras/Comentário", "Sentenças/Comentário", "Comentários Curtos", "Comentários Longos")

#Calcula Quantidade de Palavras
pos_n_words <- length(unique(tokenize_words(paste(positive_texts_raw, sep = " ", collapse = " "))))
neg_n_words <- length(unique(tokenize_words(paste(negative_texts_raw, sep = " ", collapse = " "))))

#Calcula Média de Palavras por Documento
positive_texts_raw <- mutate(positive_texts_raw, w_tokens=lapply(positive_texts_raw$raw, FUN=tokenize_words))
negative_texts_raw <- mutate(negative_texts_raw, w_tokens=lapply(negative_texts_raw$raw, FUN=tokenize_words))

positive_texts_raw <- mutate(positive_texts_raw, nw_tokens =lapply(positive_texts_raw$w_tokens, FUN=length))
negative_texts_raw <- mutate(negative_texts_raw, nw_tokens =lapply(negative_texts_raw$w_tokens, FUN=length))

positive_texts_raw$nw_tokens <- as.numeric(positive_texts_raw$nw_tokens)
negative_texts_raw$nw_tokens <- as.numeric(negative_texts_raw$nw_tokens)

#Quantidade de Comentários Curtos e Longos
pos_curtos <- nrow(positive_texts_raw[positive_texts_raw$nw_tokens<5,])
pos_longos <- nrow(positive_texts_raw[positive_texts_raw$nw_tokens>20,])

neg_curtos <- nrow(negative_texts_raw[negative_texts_raw$nw_tokens<5,])
neg_longos <- nrow(negative_texts_raw[negative_texts_raw$nw_tokens>20,])  

#Calcula Média de Sentenças por Documento
positive_texts_raw <- mutate(positive_texts_raw, s_tokens=lapply(positive_texts_raw$raw, FUN=tokenize_sentences))
negative_texts_raw <- mutate(negative_texts_raw, s_tokens=lapply(negative_texts_raw$raw, FUN=tokenize_sentences))

positive_texts_raw <- mutate(positive_texts_raw, ns_tokens =lapply(positive_texts_raw$s_tokens, FUN=length))
negative_texts_raw <- mutate(negative_texts_raw, ns_tokens =lapply(negative_texts_raw$s_tokens, FUN=length))

positive_texts_raw$ns_tokens <- as.numeric(positive_texts_raw$ns_tokens)
negative_texts_raw$ns_tokens <- as.numeric(negative_texts_raw$ns_tokens)



#Constrói Matriz
pos_info <- as.integer(c(nrow(positive_texts_raw), pos_n_words, mean(positive_texts_raw$nw_tokens), mean(positive_texts_raw$ns_tokens), pos_curtos, pos_longos))
neg_info <- as.integer(c(nrow(negative_texts_raw), neg_n_words, mean(negative_texts_raw$nw_tokens), mean(negative_texts_raw$ns_tokens), neg_curtos, neg_longos))
Values <- matrix(c(pos_info, neg_info), nrow = 2, ncol = length(info), byrow = TRUE)

tb <- data.frame(Informação=info, Positivo=pos_info, Negativo=neg_info)

knitr::kable(tb, position="center")
Informação Positivo Negativo
Nº Documentos 10000 10000
Nº Palavras 36177 30079
Palavras/Comentário 84 65
Sentenças/Comentário 4 3
Comentários Curtos 617 630
Comentários Longos 6183 6155

Através da base de dados podemos ver como usuários se comportam ao escreverem comentários positivos e negativos. Podemos observar como o volume de palavras encontradas nos comentários positivos é expressivamente maior. Comentários curtos são muito mais frequentes na classe negativa, enquanto na classe positiva os longos são mais frequentes. A média da quantidade de comentários e sentenças nos comentários positivo também são significativamente maiores, indicando como os usuários tendem a detalhar mais sua experiência positiva. Isso pode indicar que os positiva podem ser uma fonte mais ricas de informações.

A plataforma Steam permite apenas duas formas de avaliação pelo usuário (positivo e negativo), forçando os usuários a escolherem uma das duas possibilidades, mesmo que o comentários não se encaixe em nenhuma das duas. Para entender como esse comportamento pode afetar a análise dos documentos, vamos analisar como os comentários neutros se comportam na base de dados. Através da biblioteca syuzhet é possível realizar a classificação automática de documentos textuais de acordo com os sentimentos expressos no mesmo. A biblioteca classifica os sentimentos contidos em cada sentença do texto, através do número de sentenças positivas e negativas do texto, calcularemos os documentos neutros.

s <- get_nrc_sentiment(data$text, language="portuguese")
## Warning: `filter_()` is deprecated as of dplyr 0.7.0.
## Please use `filter()` instead.
## See vignette('programming') for more help
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
## Warning: `group_by_()` is deprecated as of dplyr 0.7.0.
## Please use `group_by()` instead.
## See vignette('programming') for more help
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
## Warning: `data_frame()` is deprecated as of tibble 1.1.0.
## Please use `tibble()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
sentiments <- data.frame(sent_positivo=s$positive, sent_negativo=s$negative)
sentiments <- mutate(sentiments, positivo=as.numeric(sent_positivo>sent_negativo))
sentiments <- mutate(sentiments, negativo=as.numeric(sent_positivo<sent_negativo))
sentiments <- mutate(sentiments, neutro=as.numeric(sent_positivo==sent_negativo))
sentiments$sent_negativo <- NULL
sentiments$sent_positivo <- NULL

barplot(colSums(sentiments), col=c("black", "white","gray"), ylim=c(0,10000), main="Comportamento dos comentários neutros na base de dados")#alterar 

No gráfico podemos observar como o volume de comentários neutros é expressivo, indicando que não necessariamente os comentários da base de dados são corretamente classificados. Podemos observar também que comentários neutros tendem a ser classificados como positivos pelos usuários. Para entender melhor o que os usuários expressão nesses comentários, que como acabamos de observar não são só comentários negativos e positivos, iremos usar novamente o pacote syuzhet para analisar a proporção dos sentimentos contidos nas classes da base de dados.

sentimentos_labels <- c("Raiva", "Antecipação", "Nojo", "Medo", "Alegria", "Tristeza", "Surpresa","Confiança", "Negativo", "Positivo")


pos_s <- get_nrc_sentiment(positive_texts$text, language="portuguese")
pos_s <- as.integer((colSums(pos_s)/sum(pos_s))*100)
plot = barplot(pos_s,las=2,col=rainbow(10), name=sentimentos_labels,
        main="Sentimentos Expressos nos Comentários Positivos",
        ylab="Número de Sentenças")
text(plot,pos_s-2,pos_s)

neg_s <- get_nrc_sentiment(negative_texts$text, language="portuguese")
neg_s <- as.integer((colSums(neg_s)/sum(neg_s))*100)
plot=barplot(neg_s,las=2,col=rainbow(10), name=sentimentos_labels,
        main="Sentimentos Expressos nos Comentários Negativos",
        ylab="Número de Sentenças")

text(plot,neg_s-2,neg_s)

Através dos gráficos é possível observar como os sentimentos expressos nos comentários estão de acordo com as classes da base de dados: comentários com polaridade negativa obtiveram um número maior de sentenças com sentimentos considerados negativos, como raiva, nojo, medo e tristeza, enquanto os comentários de polaridade positiva tiveram uma maior proporção de comentários positivos como alegria a confiança. Apesar disso, em ambas as classes, a quantidade de sentenças que expressão sentimentos contrários a classe ao qual são classificadas é grande, onde o segundo sentimento mais expresso na classe positiva, é o sentimento negativo. O mesmo ocorre na classe negativa, onde o segundo sentimento mais expresso é da classe positiva. Isso indica o quanto os comentários de ambas as classes podem ser úteis para obter diversas informações. É possível obter críticas e pontos de melhorias dos jogos não só nos comentários negativos, mas também nos positivos, assim como também é possível analisar pontos admirados pelos jogadores também nos comentários negativos.

Para analisar mais detalhadamente esses pontos positivos e negativos levantantados pelos usuários, os bigramas mais frequentes em cada classe são exibidos na Figura abaixo.

#Unindo documentos textuais
data_pos_string <- paste(positive_texts, sep = " ", collapse = " ")
data_neg_string <- paste(negative_texts, sep = " ", collapse = " ")

#Tokenizando 
BigramTokenizer <- function(x){
return(unlist(lapply(ngrams(words(x), 2), paste, collapse = " "), use.names = FALSE))
}

#Criando Matriz de Termos do Documento
corpus_data <- removeNumPunct(c(data_pos_string, data_neg_string))
corpus <- VCorpus(VectorSource(corpus_data))
tdm <- TermDocumentMatrix(corpus, control = list(tokenize = BigramTokenizer))
tdm_clean <- as.matrix(removeSparseTerms(tdm, 0.8))
colnames(tdm_clean) <- classes

#Gerando nuvem de palavras
comparison.cloud(tdm_clean,max.words=300,random.order=FALSE,scale=c(2,.3), title.size=1.4)

Através na nuvem de palavras gerada podemos observar pontos pertinentes analisados pelos jogadores, entre pontos aprovados pelos jogadores podemos notar algumas características dos jogos como trilha sonora (“trilha sonora”, “sonora é”), jogabilidade (“ótima jogabilidade”, “boa jogabilidade”, “jogabilidade boa”) e história (“boa história”, . Além disso podemos notar alguns elogios como “jogo fantástico”, “ótimo jogo”, “vale pena”. Algumas críticas, que podem ser úteis para os desenvolvedores descobrirem que pontos precisam ser melhorados, também são expressados pelos jogadores, como a mal otimização de recursos do jogo (“mal otimizado”), bugs (“muitos bugs”, “cheio bugs”, “jogo bugado”), jogo com problemas de execução (“jogo fecha”, “tela preta”, “jogo trava”). Além disso, outras críticas são feitas, como “jogo repetitivo”, “impossível jogar” e “deixa desejar”.

4. Conclusões

Nesse trabalho, uma base de dados de comentários da Steam foi analisada no intuito de entender os sentimentos expressos pelos jogadores.

Através da análise foi possível avaliar o compotarmento dos comentários em relação a diferentes sentimentos. Foi possível compreender como comentários classificados como positivos e negativos, expressão muito além que essas duas polaridades, podendo conter diferentes sentimentos como medo, alegria, raiva, entre outros.

Comentários de ambas as polaridades demonstraram ser ricas fontes de informações para o desenvolvimento do jogo Através dos comentários feitos por jogadores, fomos capazes de obter informações relevantes que apontam pontos de melhorias, como a correção de bugs e otimizações, e pontos positivos que indicam aos desenvolvedores o que está agradando seus usuários, como a trilha sonora, história e jogabilidade dos jogos.

Como trabalhos futuros, pretende-se expandir os experimentos com o uso de toda base de dados, além da realização da análise para os comentários categorizados como neutros.

5. Referências

Chaves, Francisco Miguel Duarte Pinheiro Ramos. 2019. “Video Game Success and Consumer Acceptance: A Sentiment Classification of Gamer Reviews.” PhD thesis.

Kaur, Jashanjot, and P Kaur Buttar. 2018. “A Systematic Review on Stopword Removal Algorithms.” Int. J. Futur. Revolut. Comput. Sci. Commun. Eng 4 (4).

Livingston, Ian, Lennart Nacke, and Regan Mandryk. 2011. “The Impact of Negative Game Reviews and User Comments on Player Experience.” Proceedings - Sandbox 2011: ACM SIGGRAPH Video Game, August. https://doi.org/10.1145/2037692.2037697.

Strååt, Björn, and Harko Verhagen. 2017. “Using User Created Game Reviews for Sentiment Analysis: A Method for Researching User Attitudes.” In GHITALY@ Chitaly.

Vieira, Augusto, and Wladmir Brandão. 2019. “Evaluating Acceptance of Video Games Using Convolutional Neural Networks for Sentiment Analysis of User Reviews.” In Proceedings of the 30th Acm Conference on Hypertext and Social Media, 273–74.

Vieira, Augusto de Castro, and Wladmir Cardoso Brandão. 2019. “GA-Eval: A Neural Network Based Approach to Evaluate Video Games Acceptance.” In Proceedings of the 18th Brazilian Symposium on Computer Games and Digital Entertainment, 595–98. SBGAMES’19. Rio de Janeiro, Brazil.