Integrantes: Alan Acevedo, Diego Kauer, Camila Labarca, Franco Miranda, Julia Paredes
Grupo: 4
Profesores: Andrés Abeliuk, Hernán Sarmiento
Auxiliares: Alison Fernández, Cinthia Sánchez
Fecha de entrega: 28 de septiembre de 2021
En la última década, el mercado de las películas y series ha crecido enormemente gracias a los avances tecnológicos y a plataformas online como Netflix, Hulu, Amazon, Disney+, entre otras. Este tema puede ser bastante interesante de analizar ya que prácticamente todas las personas están familiarizadas con éste ámbito.
El objetivo de este proyecto es aplicar distintas técnicas de minería de datos para realizar un estudio sobre un dataset obtenido de la Internet Movie Database que contiene información sobre películas. Los resultados le podrían ser de bastante utilidad a aspirantes en el mundo del cine, pues les daría una amplia imagen de la industria cinematográfica, y junto con ello entender su comportamiento.
Se trabajó con el dataset de IMDB, el cual está distribuido en 7 tablas en formato TSV, de las cuales en este hito se ocuparán dos.
La tabla title_basics contiene varios atributos de interés sobre títulos:
titleType: tipo/formato del título (película, cortometraje, documental, etc.)
primaryTitle: el nombre más popular del título
startYear: año año de lanzamiento original
runtimeMinutes: duración en minutos
genres: arreglo de hasta tres géneros asociados al título
La tabla title_ratings contiene dos atributos que también son interesantes:
averageRating: promedio de todas las valoraciones de los usuarios de IMDB que ha recibido el título
numVotes: número de valoraciones que el título ha recibido
En primer lugar se cargan las librerías a usar y luego las tablas.
library(tidyr)
library(tidyverse)
library(ggplot2)
library(corrplot)
title_basics <- read.delim2("./datasets/title_basics.tsv", encoding="UTF-8")
title_ratings <- read.delim2("./datasets/title_ratings.tsv", encoding="UTF-8")
Luego, los atributos que correspondan se cambian a categóricos o a númericos.
title_basics$runtimeMinutes = as.numeric(title_basics$runtimeMinutes)
title_basics$genres = as.factor(title_basics$genres)
title_basics$startYear = as.numeric(title_basics$startYear)
title_basics$titleType = as.factor(title_basics$titleType)
title_basics$isAdult = as.factor(title_basics$isAdult)
title_ratings$averageRating = as.numeric(title_ratings$averageRating)
nrow(title_basics)
## [1] 8246400
title_basics tiene 8.246.400 filas.
Para ver cuántos elementos sin valores asignados hay en las tablas se usa la siguiente línea:
sapply(title_basics, function(x) sum(is.na(x)))
## tconst titleType primaryTitle originalTitle isAdult
## 0 0 4 4 0
## startYear endYear runtimeMinutes genres
## 972161 0 5920733 0
La columna runtimeMinutes tiene 5.920.733 valores no asignados, lo que corresponde a un gran porcentaje del total de filas. También hay 972.161 valores nulos en startYear. Estos atributos son de interés y podrían ser usados en el estudio, por lo que se hará una limpieza del dataset.
Se realizará un estudio de los datos relacionados a películas, por lo que se filtrará para quitar los títulos de tipo documental o cortometraje, y además solo se considerarán las películas con géneros válidos.
only_movies <- title_basics[title_basics$genres != '\\N' & title_basics$titleType == 'movie', ]
nrow(only_movies)
## [1] 516492
El nuevo dataframe tiene 516.492 filas.
Ahora se puede volver a ver cuantos elementos tienen valores no asignados.
sapply(only_movies, function(x) sum(is.na(x)))
## tconst titleType primaryTitle originalTitle isAdult
## 0 0 0 0 0
## startYear endYear runtimeMinutes genres
## 70672 0 170153 0
La cantidad de valores no asignados en runtimeMinutes y startYear disminuyó considerablemente por lo que ahora podemos hacer una limpieza de estos valores.
only_movies <- na.omit(only_movies)
nrow(only_movies)
## [1] 340916
Después de la limpieza de valores nulos, title_basics tiene 340.916 filas. Sigue siendo un número importante de datos que pueden ser estudiados por lo que no es necesario considerar los valores nulos.
Como ya se filtró según la columna titleType, ésta se elimina del dataframe. Además, endYear es siempre \N para las películas, por lo que no entregan información y se eliminan del dataframe. Por último se elimina la columna originalTitle ya que no es de interés (es el nombre de la película en el idioma original).
only_movies$endYear <- NULL
only_movies$titleType <- NULL
only_movies$originalTitle <- NULL
Se hace un join de las tablas only_movies y title_ratings para que cada película tenga su valoración promedio y cantidad de valoraciones, obteniendo la tabla con la que se trabajará.
movies <- merge(only_movies, title_ratings, by='tconst')
En la tabla movies hay una columna “genres” que lista hasta 3 géneros de una misma película, éstos se pueden separar para hacer más sencillo el estudio. Se considerará el primer género como el principal.
movies_alt <- separate(movies, "genres", paste("Genero", 1:3, sep=""), sep=",")
Se puede ver la cantidad de NA’s en las columnas.
sapply(movies, function(x) sum(is.na(x)))
## tconst primaryTitle isAdult startYear runtimeMinutes
## 0 0 0 0 0
## genres averageRating numVotes
## 0 0 0
No hay nulos en ninguna columna, por lo que el dataframe está completamente limpio.
Luego de haber limpiado el dataset, se puede hacer un summary para analizar si hay algún dato inconsistente,
summary(movies)
## tconst primaryTitle isAdult startYear
## Length:235209 Length:235209 0 :231633 Min. :1896
## Class :character Class :character 1 : 3576 1st Qu.:1977
## Mode :character Mode :character \\N : 0 Median :2004
## 1964 : 0 Mean :1993
## 1965 : 0 3rd Qu.:2014
## 1972 : 0 Max. :2021
## (Other): 0
## runtimeMinutes genres averageRating numVotes
## Min. : 1.00 Drama : 41062 Min. : 1.000 Min. : 5
## 1st Qu.: 81.00 Documentary : 23770 1st Qu.: 5.400 1st Qu.: 22
## Median : 91.00 Comedy : 20651 Median : 6.300 Median : 75
## Mean : 94.34 Comedy,Drama : 8170 Mean : 6.141 Mean : 3890
## 3rd Qu.: 103.00 Drama,Romance: 7488 3rd Qu.: 7.000 3rd Qu.: 376
## Max. :51420.00 Horror : 5279 Max. :10.000 Max. :2455855
## (Other) :128789
En isAdult solo se entrega 0 o 1 dependiendo de si existe contenido explícito, por lo que esta columna está correcta. En startYear se muestran años desde 1896 hasta el año actual, no existen películas en años futuros por lo que está correcta esta columna. En runtimeMinutes existen películas con 1 minuto de duración y con más de 50 mil minutos de duración, estos valores posiblemente sean outliers y entonces se pueden trabajar sin problemas. En genres solo existen géneros válidos. Los valores de la columna averageRating se mueven entre 1 y 10 lo que es consistente. Por último, la columna numVotes se mueve entre valores positivos y entonces es consistente.
Ahora se analizarán las películas exitosas y películas muy malas. Para ello, se mostrarán los deciles que corresponden a rating y número de votos. Con ello, se definirá una película exitosa la que esté sobre el decil 9 en ambas categorías, y una película muy mala será aquella bajo el decil 1 para rating.
Deciles para rating.
quantile(movies$averageRating, probs = seq(0, 1, by = .1))
## 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
## 1.0 4.4 5.1 5.6 6.0 6.3 6.6 6.9 7.2 7.7 10.0
Deciles para numVotes.
quantile(movies$numVotes, probs = seq(0, 1, by = .1))
## 0% 10% 20% 30% 40% 50% 60% 70% 80% 90%
## 5 11 17 27 45 75 133 255 585 2097
## 100%
## 2455855
Vistos los deciles, se define una película exitosa como toda película que tiene un rating promedio de al menos 7.7 y una cantidad de votos mayor o igual a 2097.
best_movies <- movies[movies$averageRating >= 7.7 & movies$numVotes >= 2097, ][2:ncol(movies)]
De la misma forma, se define una película muy mala como toda película que tenga un rating inferior o igual a 4.4.
worst_movies <- movies[movies$averageRating <= 4.4, ][2:ncol(movies)]
A partir de esto, se puede ver qué porcentaje de películas del total puede considerarse exitosa.
round((nrow(best_movies) / nrow(movies)) * 100, 2)
## [1] 1.12
Solo el 1.12% de películas puede considerarse exitosa, que corresponde a un porcentaje muy bajo, lo que puede hablar de que es muy difícil crear una película y que ésta sea exitosa.
También, se puede ver qué porcentaje del total de películas corresponde a películas muy malas.
round((nrow(worst_movies) / nrow(movies)) * 100, 2)
## [1] 10.81
Un 10.81% de las películas es muy mala, un porcentaje bastante mayor al de películas exitosas, lo que habla de que es mucho más sencillo crear una película muy mala a una que tenga éxito.
Se puede analizar cuantas películas exitosas y cuantas muy malas tienen contenido explícito.
nrow(best_movies[best_movies$isAdult == 1, ])
## [1] 0
Ninguna película exitosa tiene contenido explícito, lo que puede puede dar luces de que una película para un amplio público puede ser bien calificada.
nrow(worst_movies[worst_movies$isAdult == 1, ])
## [1] 561
Hay muchas películas malas en comparación a las exitosas que tienen contenido explícito, lo que puede significar que las películas con contenido explícito tienen mayor probabilidad de no tener éxito. Esto puede deberse a la misma razón mencionada anteriormente, es decir, que estas películas están hechas para un público más pequeño y específico, por lo que no serán bien recibidas por todos y todas.
Luego de realizar el análisis anterior, se puede consultar sobre un top 10 de películas mejor y peor calificadas a partir de 1970, esto último para solo considerar películas contemporáneas.
best_movies1970 <- best_movies[best_movies$startYear >= 1970, ]
best_movies1970[order(-best_movies1970$averageRating), ][1:10, ]
## primaryTitle isAdult startYear runtimeMinutes
## 151110 Methagu 0 2021 100
## 57392 The Shawshank Redemption 0 1994 142
## 88853 The Chaos Class 0 1975 87
## 34182 The Godfather 0 1972 175
## 136088 Chal Mera Putt 2 0 2020 124
## 107667 Aguner Poroshmoni 0 1994 123
## 126843 Soorarai Pottru 0 2020 153
## 129132 #Home 0 2021 158
## 178514 CM101MMXI Fundamentals 0 2013 139
## 206913 Mirror Game 0 2016 147
## genres averageRating numVotes
## 151110 Biography,History 9.6 8416
## 57392 Drama 9.3 2455855
## 88853 Comedy,Drama 9.3 38992
## 34182 Crime,Drama 9.2 1699389
## 136088 Drama 9.2 2617
## 107667 Drama,War 9.1 2991
## 126843 Drama 9.1 89377
## 129132 Drama 9.1 7949
## 178514 Comedy,Documentary 9.1 44645
## 206913 Crime,Mystery,Thriller 9.1 24835
worst_movies1970 <- worst_movies[worst_movies$startYear >= 1970, ]
worst_movies1970[order(worst_movies1970$averageRating), ][1:10, ]
## primaryTitle isAdult startYear runtimeMinutes
## 73779 Steckler Interviews 0 1994 60
## 127426 Konjaku monogatari: The new edition 0 2007 88
## 141317 Princess Europe 0 2020 108
## 143627 Hearts Are Trump 0 2020 112
## 144439 Bootleg Death Tape III 0 2020 45
## 145070 321 Action 0 2020 100
## 153006 Play in the Gray 0 2009 85
## 153200 Macbeth 0 2009 56
## 155255 Hito no sabaku 0 2010 121
## 156186 Stand Up Face the Fear 0 2008 65
## genres averageRating numVotes
## 73779 Documentary 1 19
## 127426 Comedy,Drama 1 500
## 141317 Documentary 1 584
## 143627 Drama 1 21
## 144439 Horror 1 8
## 145070 Drama 1 9341
## 153006 Biography,Comedy,Documentary 1 132
## 153200 Drama 1 11
## 155255 Drama 1 475
## 156186 Comedy,Documentary 1 35
Se puede ver que tanto en las mejores como en las peores hay muchas películas de los últimos años. Esto podría deberse a que en los últimos años se han votado las películas que han salido y esos datos se han guardado en el dataset de forma correcta, lo que puede no haber sido así para películas más antiguas.
Se definen funciones que pueden ser útiles para el análisis de los datos.
# str -> double
# Entrega el porcentaje de películas del género que pertenecen a las mejores películas.
best_movies_por_genero <- function(genre) {
genero <- best_movies[grepl(genre, best_movies$genres), ]
round((nrow(genero) / nrow(best_movies)) * 100, 2)
}
# str -> double
# Entrega el porcentaje de películas del género que pertenecen a las peores películas
worst_movies_por_genero <- function(genre) {
genero <- worst_movies[grepl(genre, worst_movies$genres), ]
round((nrow(genero) / nrow(worst_movies)) * 100, 2)
}
# str -> boxplot
# Crea un boxplot de los rating de un género
boxplot_rating_por_genero <- function(genre) {
ggplot(movies[grepl(genre, movies$genres), ], aes(x=isAdult, y=averageRating)) +
geom_boxplot() +
xlab("Es para adultos? (0=No, 1=Sí)") +
ylab("Rating") +
ggtitle(paste("Boxplot de ratings para género", genre, sep=" ")) +
theme(plot.title = element_text(hjust = 0.5, size = 10))
}
# str -> boxplot
# Crea un boxplot con la distribución de ratings para una película con género principal genre
# Género principal es el primer género en la lista de géneros
boxplot_rating_genero_principal <- function(main_genre) {
ggplot(movies_alt[movies_alt$Genero1 == main_genre, ], aes(x=isAdult, y=averageRating)) +
geom_boxplot() +
xlab("Es para adultos? (0=No, 1=Sí)") +
ylab("Rating") +
ggtitle(paste("Boxplot de ratings para género principal", main_genre, sep=" ")) +
theme(plot.title = element_text(hjust = 0.5, size = 10))
}
# str -> ggplot
# Crea un gráfico de dispersión que muestra la relación entre rating y duración para películas de un género
dispersion_rating_duracion <- function(genre) {
ggplot(movies[grepl(genre, movies$genres), ], aes(x=runtimeMinutes, y=averageRating)) +
geom_point() +
ggtitle(paste("Dispersión entre rating y duración para el género", genre, sep=" ")) +
xlab("Duración") +
ylab("Rating") +
theme(plot.title = element_text(hjust = 0.5, size = 9))
}
# str -> ggplot
# Crea un gráfico de dispersión que muestra la relación entre rating y cantidad de votos para películas de un género
dispersion_rating_votos <- function(genre) {
ggplot(movies[grepl(genre, movies$genres), ], aes(x=numVotes, y=averageRating)) +
geom_point() +
ggtitle(paste("Dispersión entre rating y votos para género", genre, sep=" ")) +
xlab("Número de votos") +
ylab("Rating") +
theme(plot.title = element_text(hjust = 0.5, size = 9))
}
Luego de haber definido las funciones, se pueden realizar algunas consultas interesantes, como ver qué porcentaje de películas exitosas o muy malas corresponde a cierto género.
Porcentaje de películas de un género que son exitosas.
best_movies_por_genero("Action")
## [1] 14.25
best_movies_por_genero("Drama")
## [1] 67.32
best_movies_por_genero("Crime")
## [1] 16.11
best_movies_por_genero("Comedy")
## [1] 24.83
best_movies_por_genero("Romance")
## [1] 15.73
best_movies_por_genero("Mystery")
## [1] 6.03
best_movies_por_genero("Family")
## [1] 3.94
best_movies_por_genero("Adult")
## [1] 0
best_movies_por_genero("Sport")
## [1] 2.81
best_movies_por_genero("Sci-Fi")
## [1] 2.81
Porcentaje de películas de un género que son muy malas.
worst_movies_por_genero("Action")
## [1] 17.47
worst_movies_por_genero("Drama")
## [1] 30.61
worst_movies_por_genero("Crime")
## [1] 8.44
worst_movies_por_genero("Comedy")
## [1] 29.91
worst_movies_por_genero("Romance")
## [1] 8.47
worst_movies_por_genero("Mystery")
## [1] 4.79
worst_movies_por_genero("Family")
## [1] 3.22
worst_movies_por_genero("Adult")
## [1] 2.03
worst_movies_por_genero("Sport")
## [1] 0.68
worst_movies_por_genero("Sci-Fi")
## [1] 6.78
Comparando ambos resultados, lo primero que se destaca es que no hay películas para adultos exitosas y sí lo hay en películas muy malas, lo que confirma lo dicho en un punto anterior. También, en ambos casos el género “Drama” es el que más aparece, seguido por “Comedy” y “Action”, esto puede dar a entender que se realizan muchas películas de estos géneros porque son géneros populares, y por ende hay muchas que son tanto exitosas como muy malas. Se puede notar que hay un alto porcentaje de películas del género “Romance” que son exitosas, sin embargo este número disminuye bastante en las películas muy malas, lo mismo ocurre con “Crime”.
Se realizará un gráfico de densidad que mostrará cómo se distribuyen los rating para todas las películas.
ggplot(movies) +
geom_density(aes(x = averageRating), fill = "steelblue") +
xlab("Rating") +
ylab("Frecuencia") +
ggtitle("Densidad de rating para todas las películas") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, size = 10))
mean(movies$averageRating)
## [1] 6.140733
sd(movies$averageRating)
## [1] 1.312389
Analizando el gráfico junto con el promedio y desviación estándar para el rating, se puede notar que el gráfico se asemeja bastante a una distribución normal con media 6.14 y desviación estándar 1.31. El valor de la densidad aumenta levemente hasta llegar a su máximo, para luego caer rápidamente, esto significa que hay muy poca cantidad de películas con rating sobre el promedio en comparación a películas con rating bajo el promedio.
Se crearán boxplots de ratings para géneros utilizando dos métodos. Primero, se creará un boxplot si es que la película es de un único género, y segundo, en caso de que el título esté asociado a más de un género, se creará un boxplot solo considerando el primer género de la lista, esto para ver si hay mayores diferencias a cuando el género es único o no. Se analizará para los géneros “Action”, “Drama” y “Comedy” y se diferenciará por contenido explícito.
Boxplots para “Action”
boxplot_rating_por_genero("Action")
boxplot_rating_genero_principal("Action")
En este caso, no se ven cambios respecto a cuando “Action” es el género principal de una película o no.
Boxplots para “Drama”
boxplot_rating_por_genero("Drama")
boxplot_rating_genero_principal("Drama")
En este caso, si la película no es para adultos no se ven cambios, pero de serlo, la distribución cambia, ésta se traslada un poco hacia abajo, lo que significa que el primer, segundo y tercer cuartil de ratings son más bajos.
Boxplots para “Comedy”
boxplot_rating_por_genero("Comedy")
boxplot_rating_genero_principal("Comedy")
En este último caso se ven cambios tanto si la película es para adultos como si no. Cuando no es para adultos, el tercer cuartil se desplaza un poco para abajo y hay más outliers superiores, mientras que cuando es para adultos, el boxplot se desplaza bastante hacia abajo, y se puede concluir lo mismo que para el género anterior.
Se pueden realizar gráficos de dispersión para analizar la relación entre ciertas variables. En particular, se analizará la relación entre rating y duración, y rating y cantidad de votos para los géneros “Action”, “Drama”, “Comedy”.
Gráficos de dispersión entre rating y duración.
dispersion_rating_duracion("Action")
dispersion_rating_duracion("Drama")
dispersion_rating_duracion("Comedy")
Luego de realizar el gráfico de dispersión entre rating y duración de las películas para los 3 géneros, se puede notar que hay 2 bastante similares, “Action” y “Comedy”, es decir, su duración fluctúa en un rango amplio sin importar el rating, mientras que para “Drama” la duración es notoriamente menor y tampoco parece importar el rating.
Gráficos de duración entre rating y cantidad de votos.
dispersion_rating_votos("Action")
dispersion_rating_votos("Drama")
dispersion_rating_votos("Comedy")
Con los gráficos de dispersión entre rating y cantidad de votos se puede ver una clara tendencia, a partir del rating 5.0 aproximadamente comienza a haber un mayor número de votos, lo cual hace sentido, pues si una película es bien calificada más gente se verá interesada en seguir votándola.
Con los valores numéricos del dataset se puede crear una matriz de correlación. Para ver si el título de la película tiene alguna correlación con otra variable se usará el largo del título.
movies_cor <- cbind(movies[1], lapply(movies[2], FUN=nchar), movies[3:8])
cor <- cor(movies_cor %>% select(primaryTitle, startYear, runtimeMinutes, averageRating, numVotes))
colnames(cor) <- c("titleLength", "startYear", "runtimeMinutes", "averageRating", "numVotes")
corrplot(cor, title = "Correlaciones para la tabla 'movies'", mar = c(0, 0, 1, 0), method = "shade", tl.pos = 'n', addCoef.col = "black")
La mayor correlación que existe es entre el rating y el número de votos, lo que es consistente con lo dicho anteriormente, pero aun así sigue siendo un valor extremadamente pequeño (ni siquiera 0.1). Entonces, se puede decir que todas las variables son prácticamente independientes entre sí.
Luego de realizar el analisis exploratorio de los datos se definieron las siguientes preguntas y problemas a responder:
¿Cuáles son los factores principales que afectan en la clasificación de una película?
¿Existe alguna relación entre el largo del título de una película y su popularidad?
¿Existe alguna relación entre los géneros asociados a una película y el largo de su título?
¿Existe alguna relación entre los géneros asociados a una película y su valoración?
¿Existe alguna relación entre la duración de una película y su valoración?
¿Está relacionado el rating con el número de votos que recibe una película?
Alan Acevedo: Obtención y limpieza de datos. Presentación.
Diego Kauer: Gráficos de dispersión, matriz de correlación. Presentación.
Camila Labarca: Gráficos de dispersión, largo de titulo para matriz de correlación, formulación de preguntas y problemas. Presentación.
Franco Miranda: Análisis de mejores y peores películas . Funciones para gráficos. Preguntas y problemas. Presentación.
Julia Paredes: Análisis y exploración de datos, formulación de preguntas y problemas. Presentación.