knitr::opts_chunk$set(echo=TRUE, warning=FALSE, message=FALSE, fig.width=12, fig.height=6)
library(dplyr)
library(ggplot2)
library(reshape2)
library(gridExtra)
library(resample)
library(splitstackshape)
Para essa parte do problema usaremos dados de um grupo de pesquisa já antigo da área de sistemas de recomendação, o grouplens. Usaremos esses dados sobre avaliações de filmes: http://grouplens.org/datasets/movielens/latest/. Para essa parte do problema, usaremos o dataset small. Para a parte onde você escolhe a pergunta e conduz a investigação você escolhe qual dos datasets usar.
Independente do dado que você usar, lembre que temos aqui apenas uma amostra das opiniões de todas as pessoas pessoas. Este problema trata com estimarmos a opinião que a audiência de um filme tem a partir dessa amostra.
Observação: Alguns códigos estão comentados pois se referem a processamentos demorados e/ou que ocorreram mais de uma vez. Gerei uma vez, salvei em arquivos os dataframes resultado apenas 1 vez para acelerar o processo.
O que fazer?
Temos duas perguntas de pesquisa. Responda ambas em um relatório que seguirá os moldes do que você viu de melhor em estilo e forma do CP 1. Lembre de escrever uma frase com o vocabulário do domínio do problema explicando seu achado.
1. Normalmente os filmes têm vários gêneros. Existe uma relação entre em quantos gêneros os filmes se encaixam e a avaliação média que os filmes recebem? Mais especificamente: se consideramos a média dos filmes com 1, 2, 3 ... gêneros, existe alguma quantidade de gêneros num mesmo filme que em média recebe avaliações melhores? Caso exista, estime a diferença nas médias entre essa combinação e filmes com apenas um gênero.
2. Entre os 10 gêneros que têm mais filmes, quais possuem maior variação nas notas atribuídas a seus filmes?
Antes de responder qualquer pergunta faremos uma pequena análise descritiva sobre os dados, afim de conhecê-los e ter certeza que estes são consistentes.
# Carregando os dados
#movies <- read.csv("movies.csv", header=TRUE, encoding = "UTF-8")
#ratings <- read.csv("ratings.csv", header=TRUE, encoding = "UTF-8")
#tags <- read.csv("tags.csv", header=TRUE, encoding = "UTF-8")
# Unificando o dataset
#avaliacoes <- merge(movies, ratings, by="movieId")
# Determinando a quantidade de gêneros de cada filme
#avaliacoes <- avaliacoes %>%
# rowwise() %>%
# mutate(GenreQnt = length(strsplit(as.character(genres), "\\|")[[1]]))
#write.csv(avaliacoes, file = "avaliacoes.csv", row.names = FALSE)
# Carregando o dataset completo
avaliacoes <- read.csv("avaliacoes.csv", header=TRUE, encoding = "UTF-8")
Os dados estavam divididos em 3 arquivos: movies, tags e ratings, no pré-processamento acima unificamos esses dados em um único dataset que possui a seguinte estrutura:
head(avaliacoes, n=5, row.names = FALSE)
## movieId title genres
## 1 1 Toy Story (1995) Adventure|Animation|Children|Comedy|Fantasy
## 2 1 Toy Story (1995) Adventure|Animation|Children|Comedy|Fantasy
## 3 1 Toy Story (1995) Adventure|Animation|Children|Comedy|Fantasy
## 4 1 Toy Story (1995) Adventure|Animation|Children|Comedy|Fantasy
## 5 1 Toy Story (1995) Adventure|Animation|Children|Comedy|Fantasy
## userId rating timestamp GenreQnt
## 1 220 4 1111497936 5
## 2 419 4 1447594754 5
## 3 187 5 863122452 5
## 4 54 3 1146936004 5
## 5 409 5 1292610547 5
O atributo movieId varia de 1 até o número total de filmes distintos avaliados. No nosso caso o range é:
range(avaliacoes$movieId)
## [1] 1 149532
Para o atributo título podemos verificar se é NA, NULL em alguma das observações.
# Verificação se há NA's no dataframe inteiro
any(is.na(avaliacoes))
## [1] FALSE
# Verificação se há valores nulos no dataframe inteiro
any(is.null(avaliacoes))
## [1] FALSE
No caso do atributo gênero temos as categorias separadas por “-”. Neste caso há uma peculiaridade: quando fizemos a contagem dos gêneros no processamento acima, as observações que possuem “(no genres listed)” foram classificadas como tendo 1 gênero, é necessário corrigir para 0 esse valor.
avaliacoes$GenreQnt[avaliacoes$genres == "(no genres listed)"] <- 0
ng <- subset(avaliacoes, avaliacoes$genres == "(no genres listed)")
ng
## movieId title genres
## 105064 126929 Li'l Quinquin (no genres listed)
## 105240 135460 Pablo (2012) (no genres listed)
## 105279 138863 The Big Broadcast of 1936 (1935) (no genres listed)
## 105307 141305 Round Trip to Heaven (1992) (no genres listed)
## 105309 141472 The 50 Year Argument (2014) (no genres listed)
## 105324 143709 The Take (2009) (no genres listed)
## 105339 149532 Marco Polo: One Hundred Eyes (2015) (no genres listed)
## userId rating timestamp GenreQnt
## 105064 668 2.0 1436668178 0
## 105240 475 3.5 1441965768 0
## 105279 668 2.0 1438052429 0
## 105307 164 4.0 1440814069 0
## 105309 668 2.5 1442679119 0
## 105324 475 3.5 1450606399 0
## 105339 475 4.0 1451223429 0
rm(ng)
O atributo GenreQnt foi gerado a partir do atributo genres. Dividimos a string completa em um array utilizando o separador ‘-’, e contamos a quantidade de gêneros ali existentes. O tratamento para o caso de não existir nenhum gênero também foi realizado acima.
summary(avaliacoes$GenreQnt)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 2.000 3.000 2.676 3.000 10.000
ggplot(avaliacoes, aes(GenreQnt)) + geom_bar(fill="blue") +
ggtitle("Quant Gêneros - Filmes") + xlab("Quant Gêneros") + ylab("Quantidade - Filmes") + coord_flip()
O atributo rating pode variar de 0.5 até 5, que são as notas possíveis atribuídas a um filme. Uma possível inconsistência seriam valores fora desse range.
summary(avaliacoes$rating)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.500 3.000 3.500 3.517 4.000 5.000
ggplot(avaliacoes, aes(rating)) + geom_bar(fill="blue") +
ggtitle("Avaliações - Filmes") + xlab("Avaliação") + ylab("Quantidade - Avalia")
ggplot(avaliacoes, aes(factor(GenreQnt), rating)) + geom_boxplot() +
xlab("Quantidade de Gêneros") + ylab("Avaliação") + ggtitle("Distribuição - Avaliações")
Observa-se que a grande maioria das avaliações está entre 2 e 4, e que filmes com 7 gêneros possuem uma variação grande nas notas recebidas.
Visto que para cada filme existem diversas avaliações (uma avaliação de cada usuário), podemos calcular uma estatística que sumarize essas opiniões. Podemos utilizar a média ou a mediana das avaliações. Neste caso optaremos por utilizar a média.
movRating <- avaliacoes %>%
group_by(movieId) %>%
summarise(Media = mean(rating), Mediana = median(rating), numAvaliacoes = n())
head(movRating, n=5)
## Source: local data frame [5 x 4]
##
## movieId Media Mediana numAvaliacoes
## (int) (dbl) (dbl) (int)
## 1 1 3.907328 4 232
## 2 2 3.353261 3 92
## 3 3 3.189655 3 58
## 4 4 2.818182 3 11
## 5 5 3.250000 3 62
a <- ggplot(movRating, aes("-", Mediana)) + geom_boxplot() + ggtitle("Avaliações")
b <- ggplot(movRating, aes("-", numAvaliacoes)) + geom_boxplot() + ggtitle("Numero de Avaliacoes")
grid.arrange(a, b, ncol = 2)
Calculamos a média das avaliações para filmes com a mesma quantidade de gêneros.
movRatingQ <- avaliacoes %>%
group_by(GenreQnt) %>%
summarise(Media = mean(rating), Mediana = median(rating), numAvaliacoes = n())
movRatingQ
## Source: local data frame [10 x 4]
##
## GenreQnt Media Mediana numAvaliacoes
## (dbl) (dbl) (dbl) (int)
## 1 0 3.071429 3.50 7
## 2 1 3.445802 3.50 17473
## 3 2 3.506113 3.50 30998
## 4 3 3.550022 4.00 33485
## 5 4 3.521803 3.50 16718
## 6 5 3.576595 4.00 5157
## 7 6 3.524167 3.50 1200
## 8 7 3.755068 4.00 296
## 9 8 3.166667 3.00 3
## 10 10 2.250000 2.25 2
No boxplot “Avaliações” acima vemos que 50% das avaliações feitas pelo usuário estão entre 2.5 e 4.
Feito um breve resumo sobre os dados, voltaremos nossa atenção para as perguntas a serem respondidas.
1. Normalmente os filmes têm vários gêneros. Existe uma relação entre em quantos gêneros os filmes se encaixam e a avaliação média que os filmes recebem? Mais especificamente: se consideramos a média dos filmes com 1, 2, 3 … gêneros, existe alguma quantidade de gêneros num mesmo filme que em média recebe avaliações melhores? Caso exista, estime a diferença nas médias entre essa combinação e filmes com apenas um gênero.
Para responder a questão acima vamos:
Considerar cada valor da Quantidade de Gêneros como um grupo, assim teremos 10 grupos
Calcular o intervalo de confiança da média para cada um dos grupos (os que tiverem observações bastantes)
Comparar dos demais intervalos de confiança com o grupo que representa os filmes classificados em 1 gênero (comparar os outros grupos com o grupo 1)
# IntervaloConf <- function(baseDedados, generos) {
# filterByGender <- filter (baseDedados, GenreQnt == generos)
# qtAvaliacoes <- nrow(filterByGender)
# bt <- bootstrap(filterByGender$rating, mean)
# media <- mean (filterByGender$rating)
# cix <- CI.percentile(bt, probs = c(.05, .95))
# lower <-cix[1]
# upper <-cix[2]
# quantidadeDeGeneros <- generos
# final.data <- data.frame(quantidadeDeGeneros, qtAvaliacoes, media, lower, upper)
# return(final.data)
# }
# data <- IntervaloConf (avaliacoes, 1)
# for(i in 2:10) {
# data <- rbind(data, IntervaloConf(avaliacoes, i))
# }
#write.csv(data, file = "intervalos.csv", row.names = FALSE)
data <- read.csv("intervalos.csv", header = TRUE)
ggplot(data, aes(x=quantidadeDeGeneros, y=media, color=factor(quantidadeDeGeneros))) +
geom_errorbar(aes(ymin=intervalo01, ymax=intervalo02),
width=.2, position=position_dodge(.9)) +
scale_x_continuous(breaks=seq(1:7)) +
geom_point(shape=21, size=2, fill="white") +
ggtitle("Intervalos de Confiança - Avaliações") +
xlab("Quant. Gêneros") + ylab("Média Avaliações") +
theme(legend.position="none")
Para a análise deixaremos de fora grupos com 0, 8, 9 e 10 gêneros, pois não houveram avaliações suficientes nestes grupos.
Observando os intervalos de confiança, podemos perceber que ao passo que a quantidade de gêneros cresce, em geral, a média das avaliações de cada grupo também cresce. Por não haver sobreposição entre os intervalos dos grupos de 2 a 7 com o intervalo do grupo 1, podemos afirmar com 95% de confiança que há diferença estatística significativa entre o grupo 1 e os demais grupos.
A impressão que a análise me passa é: quanto mais simples o filme, ou seja, quanto menos características de gêneros distintos ele possui, menores as avaliações. E vice-versa.
2. Entre os 10 gêneros que têm mais filmes, quais possuem maior variação nas notas atribuídas a seus filmes?
Para responder a questão acima, iremos:
Descobrir os 10 gêneros mais citados no dataset
Para cada gênero do top 10 calcular a variância e o intervalo de confiança da variância
qn <- avaliacoes %>%
group_by(movieId, title, genres, GenreQnt) %>%
summarise(NumeroDeAvaliacoes = n(), Variancia = var(rating))
contagemGenero <- qn %>%
group_by(movieId, genres) %>%
summarise(NumeroDeVotos = n())
contagemGenero <- cSplit(qn, "genres", "|", direction="long")
resultGenre <- contagemGenero %>%
group_by(genres) %>%
summarise(NumeroTotalAparicoes = n())
resultGenre <- resultGenre[order(-NumeroTotalAparicoes),]
resultGenre <- slice(resultGenre, 1:10)
resultGenre
## Source: local data table [10 x 2]
##
## genres NumeroTotalAparicoes
## (fctr) (int)
## 1 Drama 5218
## 2 Comedy 3513
## 3 Thriller 2187
## 4 Romance 1788
## 5 Action 1737
## 6 Crime 1440
## 7 Adventure 1164
## 8 Horror 1001
## 9 Sci-Fi 859
## 10 Mystery 675
Podemos conferir acima os 10 gêneros mais frequentes nos filmes avaliados.
Agora para cada gênero do top 10 vamos calcular o intervalo de confiança da variância para as avaliações dos filmes daquele gênero.
IntervaloConfVar <- function(baseDedados, generoDoFilme) {
filterByGender <- filter (baseDedados, genres== generoDoFilme)
bt <- bootstrap(filterByGender$rating, var)
cix <- CI.percentile(bt, probs = c(.05, .95))
lower <- cix[1]
upper <-cix[2]
genero <- generoDoFilme
variancia <- var(filterByGender$rating)
final.data <- data.frame(genero, variancia, upper, lower)
return(final.data)
}
#avv <- cSplit(avaliacoes, "genres", "|", direction="long")
# intervalosVar <- IntervaloConfVar(avv, "Drama")
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Comedy"))
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Thriller"))
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Romance"))
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Action"))
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Crime"))
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Adventure"))
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Horror"))
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Sci-Fi"))
# intervalosVar <- rbind(intervalosVar, IntervaloConfVar (contagemGenero, "Mystery"))
#write.csv(intervalosVar, file="intervalosVar.csv", row.names = FALSE)
intervalosVar <- read.csv("intervalosVar.csv", header = TRUE)
ggplot(intervalosVar, aes(x=genero, y=variancia, color=factor(genero))) +
geom_errorbar(aes(ymin=lower, ymax=upper),
width=.2, position=position_dodge(.9)) +
geom_point(shape=21, size=2, fill="white") +
ggtitle("Intervalos de Confiança - Avaliações") +
xlab("Gêneros") + ylab("Variância Avaliações") +
theme(legend.position="none")
A partir do gráfico acima podemos inferir que os gêneros com maior variância de avaliações são Horror e Mistery.