Esse case trata-se de um desafio proposto pela RankMyApp, de criar um Relatório de Análise de Dados de um aplicativo do setor financeiro de um banco fictício chamado B4 Bank, cujo objetivo é avaliar a performance de seu app. Foram fornecidas duas base de dados (Retained_Bank e Reviews_Bank). Ambas as bases de dados podem ser encontradas no repositório do github: https://github.com/isaccarvalho/Case_RankMyApp.
library(tidyverse)
library(data.table)
library(tm)
library(SnowballC)
library(tidytext)
library(stringr)
library(stringi)
library(rvest)
library(readr)
library(gridExtra)
Primeiramente vamos carregar nossa base de dados. Lembrando que minha base já se encontra em meu diretório.
Retained_Bank <- read.csv("Retained_Bank.csv", header=TRUE, sep=";")
Reviews_Bank <- read.csv("Reviews_Bank.csv", header=TRUE, sep=";")
Primeiramente vamos trabalhar com a base Retained_Bank. A base de dados Retained_Bank traz os dados de instalações, visitas e canal de aquisição do usuário no aplicativo. Neste case foi dito que o KPI dever ser baseado nas métricas do Canal Orgânico. Portanto, irei extrair somente o que nos interessa antes de começar.
*Observação: Na base Retained_Bank havia uma data “31/09/2019”, o que evidentemente não existe. Acredito que o mês 10 foi trocado pelo mês 9, pois no mês 10 não tem a data “31/10/2019”. Não sei se a troca foi proposital, mas de qualquer forma a data mencionada ficará fora da análise.
###Analisando base Retained_Bank
#Extraindo Canal Orgânico e excluindo data inexistente
Retained_Bank_Org = Retained_Bank %>%
filter(Acquisition.Channel == "Organic") %>%
filter(Date != "31/09/2019") %>%
mutate(Date = as.Date(Date, format = "%d/%m/%Y"))
#Criando coluna mês
Retained_Bank_Org = Retained_Bank_Org %>%
mutate(MES = month(Date))
#Separando indicadores entre tipo quantidade e taxa
#Quantidade
Retained_Bank_Org_Qtd = Retained_Bank_Org %>%
select(Date,MES,Package.Name:Installers,Installers.retained.for.1.day,Installers.retained.for.7.days,Installers.retained.for.15.days,Installers.retained.for.30.days)
#Taxa
Retained_Bank_Org_Tx = Retained_Bank_Org %>%
select(Date,MES,Package.Name,Acquisition.Channel,Visitor.to.Installer.conversion.rate,Installer.to.1.day.retention.rate,Installer.to.7.days.retention.rate,Installer.to.15.days.retention.rate,Installer.to.30.days.retention.rate)
#Quantidade total de Instalações Retidas por mês
Retained_Bank_Org_Qtd_Mes = Retained_Bank_Org_Qtd %>%
group_by(MES) %>%
summarise(Visitas = sum(Store.Listing.Visitors),
Instalacoes = sum(Installers),
Retidas_1dia = sum(Installers.retained.for.1.day),
Retidas_7dias = sum(Installers.retained.for.7.days),
Retidas_15dias = sum(Installers.retained.for.15.days),
Retidas_30dias = sum(Installers.retained.for.30.days))
#Exibindo base sumarizada - Quantidade total de Instalações Retidas por mês
print.data.frame(Retained_Bank_Org_Qtd_Mes)
## MES Visitas Instalacoes Retidas_1dia Retidas_7dias Retidas_15dias
## 1 8 45180 9387 8255 7887 7699
## 2 9 65011 12378 11040 10587 10334
## 3 10 73384 11941 10619 10115 9824
## Retidas_30dias
## 1 7490
## 2 10065
## 3 9539
Tabela 1 - Quantidade total de Instalações Retidas por mês
#Taxa média de Instalações Retidas por mês
Retained_Bank_Org_Tx_Mes = Retained_Bank_Org_Tx %>%
group_by(MES) %>%
summarise(Visitas_por_Intalacoes = round(mean(Visitor.to.Installer.conversion.rate),digits = 2),
taxa_Retidas_1dia = round(mean(Installer.to.1.day.retention.rate),digits = 2),
taxa_Retidas_7dias = round(mean(Installer.to.7.days.retention.rate),digits = 2),
taxa_Retidas_15dias = round(mean(Installer.to.15.days.retention.rate),digits = 2),
taxa_Retidas_30dia = round(mean(Installer.to.30.days.retention.rate),digits = 2))
#Exibindo base sumarizada - Taxa média de Instalações Retidas por mês
print.data.frame(Retained_Bank_Org_Tx_Mes)
## MES Visitas_por_Intalacoes taxa_Retidas_1dia taxa_Retidas_7dias
## 1 8 0.22 0.88 0.84
## 2 9 0.21 0.89 0.85
## 3 10 0.17 0.89 0.84
## taxa_Retidas_15dias taxa_Retidas_30dia
## 1 0.82 0.79
## 2 0.83 0.81
## 3 0.82 0.80
Tabela 2 - Taxa média de Instalações Retidas por mês
Analisando os resultados dos indicadores da Tabela 1, a coluna ‘Visitas’ obteve aumento siginificativo nestes últimos 3 meses, porém a quantidade de ‘Instalações’ não acompanhou esse aumento. Na Tabela 2 confirma essa hipótese ao verificar que a conversão de Visitas por Instalações vem diminuindo nos últimos 3 meses. Os indicadores de Instalações Retidas permaneceram dentro da normalidade, o que significa que nesses últimos 3 meses não ouveram grandes mudanças nas retenções. O que tudo indica, os usuários estão visitando mais, porém não estão instalando o aplicativo.
A base de dados Reviews_Bank traz os dados das avaliações do aplicativo, como classificação de estrelas, comentários e sentimentos do usuário.
Na variável ‘Sentiment’ possui 3 categorias (Positive, Negative e Neutral), porém ela está relacionada com a variável ‘Star Rating’ (Classificação por estrelas). Se o usuário classificar o aplicativo com 1 ou 2 estrelas, teremos a variável ‘Sentiment’ com a categoria ‘Negative’, mas se o usuário classificar o aplicativo com 4 ou 5 Estrelas, teremos a variável ‘Sentiment’ com a categoria ‘Positive’ e claro que se o usuário classificar o aplicativo com 3 estrelas, teremos a variável ‘Sentiment’ com a categoria ‘Neutral’.
O problema é que a categoria ‘Neutral’ oculta informações para análise, não conseguimos saber se essa fatia de usuários tiveram uma experiência Positiva ou Negativa no aplicativo, mas podemos a partir dos comentários dos próprios usuários (na variável ‘Review Text’), utilizar o algoritmo de Análise de Sentimentos para descobrir.
###Analisando base Reviews_Bank
#Relacionando Classificação de Estrelas e Sentimentos
Reviews_Bank_Stars.vs.Sentiment = Reviews_Bank %>%
group_by(Star.Rating,Sentiment) %>%
summarise(Quantidade = n())
print.data.frame(Reviews_Bank_Stars.vs.Sentiment)
## Star.Rating Sentiment Quantidade
## 1 1 282
## 2 1 Negative 90
## 3 2 124
## 4 2 Negative 39
## 5 3 198
## 6 3 Neutral 63
## 7 4 686
## 8 4 Positive 231
## 9 5 2961
## 10 5 Positive 1005
Podemos ver na tabela acima, que na variável ‘Sentiment’ possui campos vazios, vamos completá-los usando o mesmo critério da base.
Quem for 1 ou 2 estrelas: ‘Negative’.
Quem for 3 estrelas: ‘Neutral’.
Quem for 4 ou 5 estrelas: ‘Positive’.
#Preenchendo o vazio com sentimentos
Reviews_Bank = Reviews_Bank %>%
mutate(Sentiment = ifelse(Star.Rating <= 2,
"Negative",
ifelse(Star.Rating == 3,
"Neutral",
"Positive")))
#Relacionando Classificação de Estrelas e Sentimentos (novamente)
Reviews_Bank_Stars.vs.Sentiment = Reviews_Bank %>%
group_by(Star.Rating,Sentiment) %>%
summarise(Quantidade = n())
print.data.frame(Reviews_Bank_Stars.vs.Sentiment)
## Star.Rating Sentiment Quantidade
## 1 1 Negative 372
## 2 2 Negative 163
## 3 3 Neutral 261
## 4 4 Positive 917
## 5 5 Positive 3966
Já que sabemos quantas avaliações temos em classificação por estrelas, vamos analisar as avaliações de posição neutra. Usaremos o algoritmo de Machine Learning (NLP), do pacote TM(Text Mining) para realizar uma Análise de Sentimentos.
#filtrando sentimento Neutro e avaliações comentadas
Reviews_Bank_Neutro = Reviews_Bank %>%
filter(Sentiment == "Neutral") %>%
filter(Review.Text !="")
#Limpando texto
Reviews_Bank_Neutro$Review.Text = chartr("áéíóúÁÉÍÓÚýÝàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛãõÃÕñÑäëïöüÄËÏÖÜÿçÇ",
"aeiouaeiouyyaeiouaeiouaeiouaeiouaoaonnaeiouaeiouycc",Reviews_Bank_Neutro$Review.Text)
#Criando Corpus
Corpus = Corpus(VectorSource(Reviews_Bank_Neutro$Review.Text))
#Minerando Texto
Corpus = tm_map(Corpus, content_transformer(tolower))
Corpus = tm_map(Corpus, removePunctuation)
Corpus = tm_map(Corpus, removeNumbers)
Corpus = tm_map(Corpus, removeWords, stopwords("portuguese"))
Corpus = tm_map(Corpus, stripWhitespace)
Reviews_Bank_Neutro_Corpus = TermDocumentMatrix(Corpus)
Reviews_Bank_Neutro_Corpus = as.matrix(Reviews_Bank_Neutro_Corpus)
#Lematização do texto
lemma_dicio = read.delim(file = "https://raw.githubusercontent.com/michmech/lemmatization-lists/master/lemmatization-pt.txt", header = FALSE, stringsAsFactors = FALSE)
names(lemma_dicio) = c("stem", "term")
#Condicional Lematização - Tratando dados
Palavras = row.names(Reviews_Bank_Neutro_Corpus)
for (j in 1:length(Palavras)){
comparacao = Palavras[j] == lemma_dicio$term
if (sum(comparacao) == 1){
Palavras[j] = as.character(lemma_dicio$stem[comparacao])
} else {
Palavras[j] = Palavras[j]
}
}
Palavras_Corpus = Palavras
Reviews_Bank_Neutro_Corpus_df = as.data.frame(Reviews_Bank_Neutro_Corpus)
row.names(Reviews_Bank_Neutro_Corpus_df) = NULL
Reviews_Bank_Neutro_Corpus_df$Palavras = Palavras_Corpus
#Somando ocorrências
Reviews_Bank_Neutro_Corpus_df = Reviews_Bank_Neutro_Corpus_df %>%
mutate(Ocorrencias = apply(Reviews_Bank_Neutro_Corpus_df[, 2:174],1, sum)) %>%
select(Palavras,Ocorrencias)
#Exibindo as 10 palavras mais mencionadas (exceto app e aplicativo)
Palavras.mais.mencionadas.Neutro = Reviews_Bank_Neutro_Corpus_df %>%
filter(Ocorrencias >= 13) %>%
filter(Palavras != "app") %>%
filter(Palavras != "aplicativo")
print.data.frame(arrange(Palavras.mais.mencionadas.Neutro,desc(Ocorrencias)))
## Palavras Ocorrencias
## 1 nao 75
## 2 bom 21
## 3 lentar 18
## 4 consigo 16
## 5 fazer 16
## 6 vezes 15
## 7 ser 14
## 8 pra 14
## 9 cartao 13
## 10 depositar 13
Tabela 3 - 10 Palavras mais mencionadas - Sentimento Neutro(Avaliação de 3 Estrelas)
Agora vamos fazer o mesmo com Sentimento Positivo e Sentimento Negativo, para saber quais os pontos positivos do aplicativo e os pontos a melhorar.
#filtrando sentimento Positivo e avaliações comentadas
Reviews_Bank_Positivo = Reviews_Bank %>%
filter(Sentiment == "Positive") %>%
filter(Review.Text !="")
#Limpando texto
Reviews_Bank_Positivo$Review.Text = chartr("áéíóúÁÉÍÓÚýÝàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛãõÃÕñÑäëïöüÄËÏÖÜÿçÇ",
"aeiouaeiouyyaeiouaeiouaeiouaeiouaoaonnaeiouaeiouycc",Reviews_Bank_Positivo$Review.Text)
#Criando Corpus
Corpus = Corpus(VectorSource(Reviews_Bank_Positivo$Review.Text))
#Minerando Texto
Corpus = tm_map(Corpus, content_transformer(tolower))
Corpus = tm_map(Corpus, removePunctuation)
Corpus = tm_map(Corpus, removeNumbers)
Corpus = tm_map(Corpus, removeWords, stopwords("portuguese"))
Corpus = tm_map(Corpus, stripWhitespace)
Reviews_Bank_Positivo_Corpus = TermDocumentMatrix(Corpus)
Reviews_Bank_Positivo_Corpus = as.matrix(Reviews_Bank_Positivo_Corpus)
#Condicional Lematização - Tratando dados
Palavras = row.names(Reviews_Bank_Positivo_Corpus)
for (j in 1:length(Palavras)){
comparacao = Palavras[j] == lemma_dicio$term
if (sum(comparacao) == 1){
Palavras[j] = as.character(lemma_dicio$stem[comparacao])
} else {
Palavras[j] = Palavras[j]
}
}
Palavras_Corpus = Palavras
Reviews_Bank_Positivo_Corpus_df = as.data.frame(Reviews_Bank_Positivo_Corpus)
row.names(Reviews_Bank_Positivo_Corpus_df) = NULL
Reviews_Bank_Positivo_Corpus_df$Palavras = Palavras_Corpus
#Somando ocorrências
Reviews_Bank_Positivo_Corpus_df = Reviews_Bank_Positivo_Corpus_df %>%
mutate(Ocorrencias = apply(Reviews_Bank_Positivo_Corpus_df[, 1:2144],1, sum)) %>%
select(Palavras,Ocorrencias)
#Exibindo as 10 palavras mais mencionadas (exceto app e aplicativo)
Palavras.mais.mencionadas.Positivo = Reviews_Bank_Positivo_Corpus_df %>%
filter(Ocorrencias >= 84) %>%
filter(Palavras != "app") %>%
filter(Palavras != "aplicativo")
print.data.frame(arrange(Palavras.mais.mencionadas.Positivo,desc(Ocorrencias)))
## Palavras Ocorrencias
## 1 bom 407
## 2 praticar 259
## 3 facil 225
## 4 otimo 203
## 5 excelente 191
## 6 nao 174
## 7 bom 125
## 8 rapido 120
## 9 usar 94
## 10 bem 84
Tabela 4 - 10 Palavras mais mencionadas - Sentimento Positivo(Avaliação de 4 e 5 Estrelas)
#filtrando sentimento Negativo e avaliações comentadas
Reviews_Bank_Negativo = Reviews_Bank %>%
filter(Sentiment == "Negative") %>%
filter(Review.Text !="")
#Limpando texto
Reviews_Bank_Negativo$Review.Text = chartr("áéíóúÁÉÍÓÚýÝàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛãõÃÕñÑäëïöüÄËÏÖÜÿçÇ",
"aeiouaeiouyyaeiouaeiouaeiouaeiouaoaonnaeiouaeiouycc",Reviews_Bank_Negativo$Review.Text)
#Criando Corpus
Corpus = Corpus(VectorSource(Reviews_Bank_Negativo$Review.Text))
#Minerando Texto
Corpus = tm_map(Corpus, content_transformer(tolower))
Corpus = tm_map(Corpus, removePunctuation)
Corpus = tm_map(Corpus, removeNumbers)
Corpus = tm_map(Corpus, removeWords, stopwords("portuguese"))
Corpus = tm_map(Corpus, stripWhitespace)
Reviews_Bank_Negativo_Corpus = TermDocumentMatrix(Corpus)
Reviews_Bank_Negativo_Corpus = as.matrix(Reviews_Bank_Negativo_Corpus)
#Condicional Lematização - Tratando dados
Palavras = row.names(Reviews_Bank_Negativo_Corpus)
for (j in 1:length(Palavras)){
comparacao = Palavras[j] == lemma_dicio$term
if (sum(comparacao) == 1){
Palavras[j] = as.character(lemma_dicio$stem[comparacao])
} else {
Palavras[j] = Palavras[j]
}
}
Palavras_Corpus = Palavras
Reviews_Bank_Negativo_Corpus_df = as.data.frame(Reviews_Bank_Negativo_Corpus)
row.names(Reviews_Bank_Negativo_Corpus_df) = NULL
Reviews_Bank_Negativo_Corpus_df$Palavras = Palavras_Corpus
#Somando ocorrências
Reviews_Bank_Negativo_Corpus_df = Reviews_Bank_Negativo_Corpus_df %>%
mutate(Ocorrencias = apply(Reviews_Bank_Negativo_Corpus_df[, 1:438],1, sum)) %>%
select(Palavras,Ocorrencias)
#Exibindo as 10 palavras mais mencionadas (exceto app e aplicativo)
Palavras.mais.mencionadas.Negativo = Reviews_Bank_Negativo_Corpus_df %>%
filter(Ocorrencias >= 37) %>%
filter(Palavras != "app") %>%
filter(Palavras != "aplicativo")
print.data.frame(arrange(Palavras.mais.mencionadas.Negativo,desc(Ocorrencias)))
## Palavras Ocorrencias
## 1 nao 292
## 2 consigo 56
## 3 lentar 56
## 4 atualizacao 55
## 5 cartao 52
## 6 bank 48
## 7 vezes 45
## 8 contar 42
## 9 fazer 41
## 10 funcionar 37
Tabela 5 - 10 Palavras mais mencionadas - Sentimento Negativo(Avaliação de 1 e 2 Estrelas)
Analisando os resultados da Tabela 3, podemos observar que as avaliações de posição Neutra possuem a palavra “não” como a que mais se destaca, mencionada quase 4 vezes mais do que a segunda palavra “bom”. A terceira palavra de maior impacto “lento” vem acompanhada de outras palavras de ação como “consigo” e “fazer”. O que tudo indica, os usuários de posição Neutra não estão satisfeitos com aplicativo, que apesar do aplicativo ser bom, não conseguem fazer o que necessitam.
Nos resultados da Tabela 4, observamos que as avaliações de posição Positiva tem destaque para as palavras como, “prático”, “fácil” e “rápido”, porém a 6ª palavra mais mencionada é “não”, o que liga um alerta para esses usuários, que apesar de gostar muito do aplicativo, podem estar com algum tipo de problema.
Os resultados da Tabela 5, tem comportamento muito parecido com as avaliações de posição Neutra. As avaliações de posição Negativa tem a palavra “não” como a mais mencionada, acompanhada de outras palavras de ação como “consigo”, “fazer” e “funciona”. Outras palavras destacadas também são “lento” e “atualização”, com quase a mesma quantidade, trazendo indícios que essas palavras estão relacionadas.
É evidente que o aplicativo B4 Bank é muito bem avaliado, com uma nota média de 4,4 em mais de 5 mil avaliações. Mas os indicadores de Visualizações e Instalações mostram que o aplicativo vem perdendo potenciais clientes neste período de 3 meses. Além de que as avaliações dos usuários mostram que o aplicativo tem dificuldades operacionais com alguns usuários, até mesmo com usuários que avaliaram muito bem o aplicativo. Sugiro verificar as atualizações do aplicativo que podem ter causado lentidão e entre outros problemas em alguns usuários.
As visualizações dos resultados obtidos neste projeto podem ser encontradas no Github: https://github.com/isaccarvalho/Case_RankMyApp.