TP4 - Análisis exploratorio de datos
En el siguiente TP vamos a utilizar las herramientas de análisis exploratorio de datos provistas en el curso para analizar un dataset que nunca antes vimos.
Los datos
Este dataset consta de todas las películas originales de Netflix estrenadas hasta del 1 de junio de 2021. Además, también incluye todos los documentales y especiales de Netflix. Los datos se bajaron de esta página de Kaggle. Los miembros de la comunidad votan las puntuaciones de IMDB, y la mayoría de las películas tienen más de 1.000 reseñas.
Las columnas del dataset son:
- Título de la película
Title
- Género de la película
Genre
- Fecha original de estreno
Premiere
- Duración en minutos
Runtime
- Puntaje en IMDB (al 01/06/21)
IMDB Score
- Idiomas disponibles (al 01/06/21)
Language
Para más detalles de los datos pueden consultar acá.
Los datos están en la carpeta Practicos/tp4-EDA/data del
repositorio. Los podemos bajar y cargar con read_csv()
o
cargar directo desde la url.
library(tidyverse)
library(here)
<- read_csv(here("docs/Practicos/tp4-EDA/data/NetflixOriginals.csv")) netflix
1 - Mirar los datos
Useamos las funciones summary()
, str()
y
glimpse()
para ver qué estructura y qué tipos de variables
tiene nuestro dataset
¿Tiene valores
NA
?¿Alguna de las variables no es del tipo que corresponde?
Ayuda: Para convertir una fecha de
chr
aDate
una de las formas más simples es usando el paquete {lubridate}. Por ejemplo, la funciónmdy("August 5, 2019")
nos da como resutado una fecha"2019-08-05"
. La ventaja de tener variablesDate
es que nos permite ordenarlas, realizar operaciones con ellas, etc.¿Hay algún valor sospechoso?
Una vez corregidos los probelmas del dataset imprimir un resumen
usando la función skim()
del paquete {skimr}.
# Solución
str(netflix)
#> spec_tbl_df [585 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
#> $ Title : chr [1:585] "Enter the Anime" "Dark Forces" "The App" "The Open House" ...
#> $ Genre : chr [1:585] "Documentary" "Thriller" "Science fiction/Drama" "Horror thriller" ...
#> $ Premiere : chr [1:585] "August 5, 2019" "August 21, 2020" "December 26, 2019" "January 19, 2018" ...
#> $ Runtime : num [1:585] 58 81 79 94 90 147 112 149 73 139 ...
#> $ IMDB Score: num [1:585] 2.5 2.6 2.6 3.2 3.4 3.5 3.7 3.7 3.9 4.1 ...
#> $ Language : chr [1:585] "English/Japanese" "Spanish" "Italian" "English" ...
#> - attr(*, "spec")=
#> .. cols(
#> .. Title = col_character(),
#> .. Genre = col_character(),
#> .. Premiere = col_character(),
#> .. Runtime = col_double(),
#> .. `IMDB Score` = col_double(),
#> .. Language = col_character()
#> .. )
#> - attr(*, "problems")=<externalptr>
summary(netflix)
#> Title Genre Premiere Runtime
#> Length:585 Length:585 Length:585 Min. : 4.00
#> Class :character Class :character Class :character 1st Qu.: 86.00
#> Mode :character Mode :character Mode :character Median : 97.00
#> Mean : 93.57
#> 3rd Qu.:108.00
#> Max. :209.00
#>
#> IMDB Score Language
#> Min. :2.500 Length:585
#> 1st Qu.:5.700 Class :character
#> Median :6.350 Mode :character
#> Mean :6.272
#> 3rd Qu.:7.000
#> Max. :9.000
#> NA's :1
# Hay un NA
# El mínimo de duración es raro (4 minutos)
# Los ratings están bien
glimpse(netflix)
#> Rows: 585
#> Columns: 6
#> $ Title <chr> "Enter the Anime", "Dark Forces", "The App", "The Open Ho…
#> $ Genre <chr> "Documentary", "Thriller", "Science fiction/Drama", "Horr…
#> $ Premiere <chr> "August 5, 2019", "August 21, 2020", "December 26, 2019",…
#> $ Runtime <dbl> 58, 81, 79, 94, 90, 147, 112, 149, 73, 139, 58, 112, 97, …
#> $ `IMDB Score` <dbl> 2.5, 2.6, 2.6, 3.2, 3.4, 3.5, 3.7, 3.7, 3.9, 4.1, 4.1, 4.…
#> $ Language <chr> "English/Japanese", "Spanish", "Italian", "English", "Hin…
# Premiere es un tipo de variable date y aparece como <chr>
# Arreglemos la fecha
library(lubridate)
<- netflix %>%
netflix mutate(Premiere = mdy(Premiere))
# Tiramos el NA
<- netflix %>% drop_na()
netflix
glimpse(netflix) # Ahora sí
#> Rows: 584
#> Columns: 6
#> $ Title <chr> "Enter the Anime", "Dark Forces", "The App", "The Open Ho…
#> $ Genre <chr> "Documentary", "Thriller", "Science fiction/Drama", "Horr…
#> $ Premiere <date> 2019-08-05, 2020-08-21, 2019-12-26, 2018-01-19, 2020-10-…
#> $ Runtime <dbl> 58, 81, 79, 94, 90, 147, 112, 149, 73, 139, 58, 112, 97, …
#> $ `IMDB Score` <dbl> 2.5, 2.6, 2.6, 3.2, 3.4, 3.5, 3.7, 3.7, 3.9, 4.1, 4.1, 4.…
#> $ Language <chr> "English/Japanese", "Spanish", "Italian", "English", "Hin…
library(skimr)
skim(netflix)
Name | netflix |
Number of rows | 584 |
Number of columns | 6 |
_______________________ | |
Column type frequency: | |
character | 3 |
Date | 1 |
numeric | 2 |
________________________ | |
Group variables | None |
Variable type: character
skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
---|---|---|---|---|---|---|---|
Title | 0 | 1 | 2 | 105 | 0 | 584 | 0 |
Genre | 0 | 1 | 3 | 36 | 0 | 115 | 0 |
Language | 0 | 1 | 4 | 26 | 0 | 38 | 0 |
Variable type: Date
skim_variable | n_missing | complete_rate | min | max | median | n_unique |
---|---|---|---|---|---|---|
Premiere | 0 | 1 | 2014-12-13 | 2021-05-27 | 2019-10-17 | 387 |
Variable type: numeric
skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
---|---|---|---|---|---|---|---|---|---|---|
Runtime | 0 | 1 | 93.58 | 27.76 | 4.0 | 86.0 | 97.00 | 108 | 209 | ▁▂▇▁▁ |
IMDB Score | 0 | 1 | 6.27 | 0.98 | 2.5 | 5.7 | 6.35 | 7 | 9 | ▁▂▇▇▁ |
# Podemos ver que el valor de duración que parecía raro tal vez no lo sea
¿Cuáles son los tres géneros con más estrenos?
%>%
netflix count(Genre) %>%
arrange(desc(n)) %>%
slice_head(n = 3)
#> # A tibble: 3 × 2
#> Genre n
#> <chr> <int>
#> 1 Documentary 159
#> 2 Drama 77
#> 3 Comedy 49
¿Y los tres idiomas con más estrenos?
%>%
netflix count(Language) %>%
arrange(desc(n)) %>%
slice_head(n = 3)
#> # A tibble: 3 × 2
#> Language n
#> <chr> <int>
#> 1 English 401
#> 2 Hindi 33
#> 3 Spanish 31
2 - Variación
Exploremos con un simple gráfico de barras la cantidad de películas de cada género.
- ¿Cuántos géneros hay?
- ¿Cómo es la distribución por género?
# Solución
%>%
netflix count(Genre, name = "Frecuencia") %>%
filter(Frecuencia > 5) %>%
ggplot(aes(x = reorder(Genre, Frecuencia), y = Frecuencia)) +
geom_col() +
coord_flip()
Ahora miremos la distribución de duraciones y de rating de IMDB ¿Qué podemos decir al respecto?
# Solución
%>%
netflix ggplot(aes(x = `IMDB Score`)) +
geom_histogram(fill = "#1380A1") +
labs(x = NULL,
y = NULL) +
labs(title = "IMDB Score") +
theme_minimal()
# La distribución es bastante normal pero con cola para abajo
%>%
netflix ggplot(aes(x = Runtime)) +
geom_histogram(fill = "#1380A1") +
labs(x = NULL,
y = NULL) +
labs(title = "Runtime") +
theme_minimal()
# pareciera haber un outlier de duración
Veamos cómo se distribuyen los ratings de IMDB para los géneros Drama y Comedy.
# Solución
<- c("Comedy", "Drama")
generos
%>%
netflix filter(Genre %in% generos) %>%
ggplot(aes(x = `IMDB Score`,
color = Genre)) +
geom_freqpoly() +
labs(x = NULL,
y = NULL) +
theme_minimal() +
theme(legend.position = "top")
Por último: ¿Qué pasa con las distribuciones de duraciones para Comedy y Documentary?
# Solución
<- c("Comedy", "Documentary")
generos
%>%
netflix filter(Genre %in% generos) %>%
ggplot(aes(x = Runtime,
color = Genre)) +
geom_freqpoly() +
labs(x = NULL,
y = NULL) +
theme_minimal() +
theme(legend.position = "top")
# Hay un montón de documentales cortos
3 - Covariación
Utilizando un boxplot veamos si hay alguna relación entre las películas de los géneros Comedy, Drama y Documentary y su rating de IMDB.
# Solución
<- c("Comedy", "Drama", "Documentary")
generos
%>%
netflix filter(Genre %in% generos) %>%
ggplot(aes(x = Genre,
y = `IMDB Score`,
color = Genre,
fill = Genre)) +
geom_boxplot(alpha = .5) +
theme_minimal()
Luego, usando la función geom_tile()
miremos la cantidad
de muestras para las combinaciones de los tres géneros y los tres
idiomas con más estrenos.
# Solución
<- c("Comedy", "Drama", "Documentary")
generos <- c("English", "Hindi", "Spanish")
idiomas
%>%
netflix filter(Genre %in% generos) %>%
filter(Language %in% idiomas) %>%
count(Genre, Language, name = "Frecuencia") %>%
ggplot(aes(x = Genre, y = Language, fill = Frecuencia)) +
geom_tile() +
geom_text(aes(label = Frecuencia), size = 3, color = "white") +
scale_fill_viridis_c() +
theme_minimal()
Ahora vamos a ver la covariación entre dos variables continuas. Vemos si existe alguna relación entre la fecha de estreno y el rating de IMDB.
# Solución
%>%
netflix ggplot(aes(x = Premiere,
y = `IMDB Score`)) +
geom_point() +
geom_smooth(method = lm) +
theme_minimal()
¿Y si nos quedamos con los tres géneros más populares y lo vemos por género?
# Solución
%>%
netflix filter(Genre %in% c("Comedy", "Drama", "Documentary")) %>%
ggplot(aes(x = Premiere,
y = `IMDB Score`,
color = Genre)) +
geom_point() +
geom_smooth(method = lm,
se = FALSE) +
theme_minimal() +
theme(legend.position = "top")
Por último, utilicemos la función ggpairs()
de
{GGally} para ver las distribuciones y correlaciones de todas
las variables numéricas de netflix
.
# Solución
library(GGally)
%>%
netflix select(all_of(c("Premiere", "Runtime", "IMDB Score"))) %>%
ggpairs() +
theme_minimal()
4 - Outliers
Usemos la librería {Routliers} para ver si tenemos
outliers univariados en las variables Runtime
(duración) y IMDB Score
(rating de IMDB)
# Solución
library(Routliers)
<- outliers_mad(x = netflix$Runtime)
outliers_runtime
outliers_runtime#> Call:
#> outliers_mad.default(x = netflix$Runtime)
#>
#> Median:
#> [1] 97
#>
#> MAD:
#> [1] 16.3086
#>
#> Limits of acceptable range of values:
#> [1] 48.0742 145.9258
#>
#> Number of detected outliers
#> extremely low extremely high total
#> 56 10 66
plot_outliers_mad(outliers_runtime,
x = netflix$Runtime)
%>%
netflix filter(Runtime < outliers_runtime$limits[1]) %>%
count(Genre) %>%
arrange(desc(n))
#> # A tibble: 14 × 2
#> Genre n
#> <chr> <int>
#> 1 Documentary 32
#> 2 Aftershow / Interview 6
#> 3 Animation / Short 4
#> 4 Animation 3
#> 5 Comedy 2
#> 6 Animation / Comedy 1
#> 7 Animation / Musicial 1
#> 8 Anime / Short 1
#> 9 Comedy / Musical 1
#> 10 Drama / Short 1
#> 11 Making-of 1
#> 12 Mockumentary 1
#> 13 Musical / Short 1
#> 14 Stop Motion 1
%>%
netflix filter(Runtime > outliers_runtime$limits[2])
#> # A tibble: 10 × 6
#> Title Genre Premiere Runtime `IMDB Score` Language
#> <chr> <chr> <date> <dbl> <dbl> <chr>
#> 1 Drive Acti… 2019-11-01 147 3.5 Hindi
#> 2 The Last Days of American Cri… Heis… 2020-06-05 149 3.7 English
#> 3 Army of the Dead Zomb… 2021-05-21 148 5.9 English
#> 4 Citation Drama 2020-11-06 151 6.2 English
#> 5 The Forest of Love Drama 2019-10-11 151 6.3 Japanese
#> 6 Da 5 Bloods War … 2020-06-12 155 6.5 English
#> 7 Raat Akeli Hai Thri… 2020-07-31 149 7.3 Hindi
#> 8 Ludo Anth… 2020-11-12 149 7.6 Hindi
#> 9 The Irishman Crim… 2019-11-27 209 7.8 English
#> 10 Springsteen on Broadway One-… 2018-12-16 153 8.5 English
# Solución
<- outliers_mad(x = netflix$`IMDB Score`)
outliers_IMDB
outliers_IMDB#> Call:
#> outliers_mad.default(x = netflix$`IMDB Score`)
#>
#> Median:
#> [1] 6.35
#>
#> MAD:
#> [1] 0.96369
#>
#> Limits of acceptable range of values:
#> [1] 3.45893 9.24107
#>
#> Number of detected outliers
#> extremely low extremely high total
#> 5 0 5
plot_outliers_mad(outliers_IMDB,
x = netflix$`IMDB Score`)
%>%
netflix filter(`IMDB Score` < outliers_IMDB$limits[1])
#> # A tibble: 5 × 6
#> Title Genre Premiere Runtime `IMDB Score` Language
#> <chr> <chr> <date> <dbl> <dbl> <chr>
#> 1 Enter the Anime Documentary 2019-08-05 58 2.5 English…
#> 2 Dark Forces Thriller 2020-08-21 81 2.6 Spanish
#> 3 The App Science fiction/Drama 2019-12-26 79 2.6 Italian
#> 4 The Open House Horror thriller 2018-01-19 94 3.2 English
#> 5 Kaali Khuhi Mystery 2020-10-30 90 3.4 Hindi
¿Qué podemos decir de los outliers de Runtime
?
¿Los podemos categorizar de alguna forma? ¿Nos pueden dar alguna
información sobre las producciones de Netflix?
¿Y sobre los outliers de rating de IMDB?
Por último, analicemos si hay outliers multivariados en ambas variables.
# Solución
<- outliers_mcd(x = cbind(netflix$Runtime,
outliers_multi $`IMDB Score`))
netflix
outliers_multi#> Call:
#> outliers_mcd.default(x = cbind(netflix$Runtime, netflix$`IMDB Score`))
#>
#> Limit distance of acceptable values from the centroid :
#> [1] 9.21034
#>
#> Number of detected outliers:
#> total
#> 95
plot_outliers_mcd(outliers_multi,
x = cbind(netflix$Runtime,
$`IMDB Score`)) netflix