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)

Parte 2 - Eu e um problema pequeno

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.

Análise Descritiva

# 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
  • movieId : Identificador do filme
  • title : Título da produção (junto com o ano)
  • genres : Os gêneros aos quais o filme pertence
  • UserId : Identificador do usuário que fez uma avaliação naquele filme
  • rating : Avaliação de um determinado usuário (userId) naquele filme
  • timestamp : Momento no qual a avaliação foi realizada
  • GenreQnt : Campo derivado de ‘genres’. Quantidade de gêneros daquele filme

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.


Perguntas

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:

  1. Considerar cada valor da Quantidade de Gêneros como um grupo, assim teremos 10 grupos

  2. Calcular o intervalo de confiança da média para cada um dos grupos (os que tiverem observações bastantes)

  3. 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:

  1. Descobrir os 10 gêneros mais citados no dataset

  2. 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.