Load the weekly Data
Download the weekly data and make available in the tt object.
library(lubridate)
## Warning: package 'lubridate' was built under R version 4.0.4
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
tt <- tt_load("2021-04-20")
## --- Compiling #TidyTuesday Information for 2021-04-20 ----
## --- There is 1 file available ---
## --- Starting Download ---
##
## Downloading file 1 of 1: `netflix_titles.csv`
## --- Download complete ---
netflix_titles <- tt$netflix_titles %>%
separate(duration, c("duration", "duration_units"), sep = " ", convert = TRUE) %>%
mutate(date_added = mdy(date_added),
year_added = year(date_added)) %>%
mutate(mature = rating %in% c("TV-MA", "R", "NC-17"))
netflix_titles %>%
ggplot(aes(release_year, fill = type)) +
geom_histogram(binwidth = 5) +
facet_wrap(~ type, ncol = 1, scales = "free_y")

netflix_titles %>%
#count(year = 2 * (release_year %/% 2), type) %>%
count(year = release_year, type) %>%
group_by(type) %>%
mutate(percent = n / sum(n)) %>%
ggplot(aes(year, percent, color = type)) +
geom_line()

netflix_titles %>%
filter(type == "Movie") %>%
mutate(decade = 10 * (release_year %/% 10)) %>%
ggplot(aes(decade, duration, group = decade)) +
geom_boxplot()

summarize_titles <- function(tbl) {
tbl %>%
summarize(n = n(),
median_duration = median(duration),
median_year = median(release_year)) %>%
arrange(desc(n))
}
netflix_titles %>%
count(rating, sort = TRUE)
## # A tibble: 15 x 2
## rating n
## <chr> <int>
## 1 TV-MA 2863
## 2 TV-14 1931
## 3 TV-PG 806
## 4 R 665
## 5 PG-13 386
## 6 TV-Y 280
## 7 TV-Y7 271
## 8 PG 247
## 9 TV-G 194
## 10 NR 84
## 11 G 39
## 12 <NA> 7
## 13 TV-Y7-FV 6
## 14 UR 5
## 15 NC-17 3
netflix_titles %>%
separate_rows(listed_in, sep = ", ") %>%
group_by(type, genre = listed_in) %>%
summarize_titles() %>%
filter(type == "Movie") %>%
filter(genre != "Movies") %>%
mutate(genre = fct_reorder(genre, median_duration)) %>%
ggplot(aes(median_duration, genre)) +
geom_col()
## `summarise()` has grouped output by 'type'. You can override using the `.groups` argument.

head(netflix_titles$date_added)
## [1] "2020-08-14" "2016-12-23" "2018-12-20" "2017-11-16" "2020-01-01"
## [6] "2017-07-01"
Date added
netflix_titles %>%
filter(!is.na(date_added)) %>%
arrange(date_added) %>%
select(type, title, date_added)
## # A tibble: 7,777 x 3
## type title date_added
## <chr> <chr> <date>
## 1 Movie To and From New York 2008-01-01
## 2 TV Show Dinner for Five 2008-02-04
## 3 Movie Just Another Love Story 2009-05-05
## 4 Movie Splatter 2009-11-18
## 5 Movie Mad Ron's Prevues from Hell 2010-11-01
## 6 Movie Even the Rain 2011-05-17
## 7 Movie Joseph: King of Dreams 2011-09-27
## 8 Movie A Stoning in Fulham County 2011-10-01
## 9 Movie Adam: His Song Continues 2011-10-01
## 10 Movie Hard Lessons 2011-10-01
## # ... with 7,767 more rows
netflix_titles %>%
filter(!is.na(date_added)) %>%
count(year_added, type) %>%
ggplot(aes(year_added, n, fill = type)) +
geom_area()

netflix_titles %>%
filter(year_added >= 2015) %>%
filter(!is.na(date_added), !is.na(rating)) %>%
mutate(rating = fct_lump(rating, 5)) %>%
ungroup() %>%
count(type, year_added, rating) %>%
group_by(type, year_added) %>%
mutate(percent = n / sum(n)) %>%
ggplot(aes(year_added, percent, fill = rating)) +
geom_area() +
facet_wrap(~ type)

netflix_titles %>%
filter(!is.na(country)) %>%
count(country = fct_lump(country, 20),
type,
sort = TRUE) %>%
mutate(country = fct_reorder(country, n)) %>%
ggplot(aes(n, country, fill = type)) +
geom_col()

netflix_titles %>%
filter(!is.na(country)) %>%
filter(type == "Movie") %>%
group_by(country) %>%
summarize_titles()
## # A tibble: 590 x 4
## country n median_duration median_year
## <chr> <int> <dbl> <dbl>
## 1 United States 1850 91 2016
## 2 India 852 127 2014.
## 3 United Kingdom 193 90 2016
## 4 Canada 118 89 2017
## 5 Egypt 89 101 2012
## 6 Spain 89 99 2018
## 7 Turkey 73 104 2015
## 8 Philippines 70 112. 2016
## 9 France 69 95 2017
## 10 Japan 69 98 2016
## # ... with 580 more rows
netflix_titles %>%
filter(rating %in% c("R", "TV-MA")) %>%
count(country, sort = TRUE)
## # A tibble: 399 x 2
## country n
## <chr> <int>
## 1 United States 1244
## 2 India 230
## 3 United Kingdom 199
## 4 <NA> 161
## 5 Spain 111
## 6 South Korea 83
## 7 Japan 82
## 8 France 77
## 9 Canada 76
## 10 Mexico 73
## # ... with 389 more rows
netflix_titles %>%
filter(!is.na(rating), !is.na(country)) %>%
group_by(type, country = fct_lump(country, 9)) %>%
summarize(n_mature = sum(rating %in% c("R", "TV-MA", "NC-17")),
n = n(),
.groups = "drop") %>%
mutate(pct_mature = n_mature / n,
conf_low = qbeta(.025, n_mature + .5, n - n_mature + .5),
conf_high = qbeta(.975, n_mature + .5, n - n_mature + .5)) %>%
ggplot(aes(pct_mature, country, color = type)) +
geom_point(aes(size = n)) +
geom_errorbar(aes(xmin = conf_low, xmax = conf_high), width = .1) +
scale_x_continuous(labels = percent) +
expand_limits(x = 0) +
labs(x = "% of titles that are R/TV-MA")

library(tidytext)
## Warning: package 'tidytext' was built under R version 4.0.5
library(snakecase)
## Warning: package 'snakecase' was built under R version 4.0.3
library(tidylo)
## Warning: package 'tidylo' was built under R version 4.0.5
words_unnested <- netflix_titles %>%
unnest_tokens(word, description) %>%
anti_join(stop_words, by = "word")
words_unnested %>%
count(type, word, sort = TRUE) %>%
mutate(type = to_snake_case(type)) %>%
spread(type, n, fill = 0) %>%
mutate(total = movie + tv_show) %>%
arrange(desc(total)) %>%
head(100) %>%
ggplot(aes(movie, tv_show)) +
geom_point() +
geom_text(aes(label = word) , vjust = 1, hjust = 1) +
scale_x_log10() +
scale_y_log10()

words_unnested %>%
count(type, word) %>%
bind_log_odds(type, word, n) %>%
arrange(desc(log_odds_weighted)) %>%
group_by(type) %>%
top_n(10, log_odds_weighted) %>%
ungroup() %>%
mutate(word = fct_reorder(word, log_odds_weighted)) %>%
ggplot(aes(log_odds_weighted, word)) +
geom_col() +
facet_wrap(~ type, scales = "free_y")

library(widyr)
library(tidygraph)
## Warning: package 'tidygraph' was built under R version 4.0.5
##
## Attaching package: 'tidygraph'
## The following object is masked from 'package:stats':
##
## filter
library(ggraph)
## Warning: package 'ggraph' was built under R version 4.0.5
set.seed(2021)
words_unnested %>%
distinct(type, title, word) %>%
add_count(word, name = "word_total") %>%
filter(word_total >= 40) %>%
pairwise_cor(word, title, sort = TRUE) %>%
filter(correlation >= .1) %>%
igraph::graph_from_data_frame() %>%
ggraph(layout = "fr") +
geom_edge_link(aes(alpha = correlation)) +
geom_node_point() +
geom_node_text(aes(label = name),
repel = TRUE) +
theme(legend.position = "none")
## Warning: `tbl_df()` was deprecated in dplyr 1.0.0.
## Please use `tibble::as_tibble()` instead.

word_genre_log_odds <- words_unnested %>%
distinct(type, title, word, genre = listed_in) %>%
add_count(word, name = "word_total") %>%
filter(word_total >= 25) %>%
separate_rows(genre, sep = ", ") %>%
filter(fct_lump(genre, 9) != "Other") %>%
count(genre, word) %>%
bind_log_odds(genre, word, n)
word_genre_log_odds %>%
group_by(genre) %>%
top_n(10, log_odds_weighted) %>%
ungroup() %>%
mutate(word = reorder_within(word, log_odds_weighted, genre)) %>%
ggplot(aes(log_odds_weighted, word, fill = genre)) +
geom_col() +
facet_wrap(~ genre, scales = "free_y") +
scale_y_reordered() +
theme(legend.position = "none") +
labs(x = "Log-odds of word's specificity to genre",
y = "")

Lasso regression
word_ratings <- words_unnested %>%
count(type, title, rating, word) %>%
filter(!is.na(rating)) %>%
add_count(word, name = "word_total") %>%
filter(word_total >= 30)
library(glmnet)
## Warning: package 'glmnet' was built under R version 4.0.4
## Loading required package: Matrix
##
## Attaching package: 'Matrix'
## The following objects are masked from 'package:tidyr':
##
## expand, pack, unpack
## Loaded glmnet 4.1-1
library(broom)
## Warning: package 'broom' was built under R version 4.0.5
other_features <- netflix_titles %>%
select(title, director, cast, genre = listed_in, country) %>%
gather(feature_type, feature, director, cast, genre, country) %>%
filter(!is.na(feature)) %>%
separate_rows(feature, sep = ", ") %>%
mutate(feature_type = str_to_title(feature_type)) %>%
unite(feature, feature_type, feature, sep = ": ") %>%
add_count(feature, name = "feature_count") %>%
filter(feature_count >= 10)
feature_matrix <- word_ratings %>%
mutate(feature = paste("Description:", word)) %>%
bind_rows(other_features) %>%
cast_sparse(title, feature)
y <- netflix_titles$mature[match(rownames(feature_matrix), netflix_titles$title)]
mod <- cv.glmnet(feature_matrix, y, family = "binomial")
plot(mod)

mod$glmnet.fit %>%
tidy() %>%
separate(term, c("feature_type", "feature"), sep = ": ") %>%
filter(lambda == mod$lambda.1se) %>%
top_n(60, abs(estimate)) %>%
mutate(feature = fct_reorder(feature, estimate)) %>%
ggplot(aes(estimate, feature, fill = feature_type)) +
geom_col() +
labs(x = "Coefficient: does this make the title more likely to be TV-MA/R?",
y = "",
fill = "Feature Type")
## Warning: Expected 2 pieces. Missing pieces filled with `NA` in 86 rows [1, 2, 3,
## 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...].
