#Install packages needed
pacman::p_load(
# Package Install and Management
pacman, # package install/load
janitor, #clean up data names
# Project and File Management
readr, # import data
httr, #github passkey
# General Data Management
dplyr, # data management
tidyr, # data management
lubridate, # work with dates
zoo, # work with dates
tidyverse, # work with dates
scales, # ggplot
gmodels # freq tables SAS
)
#Load data
#sql
#library("RPostgres", "DBI", "rstuioapi")
#connect to database
#sql <- dbConnect(Postgres(), host = "localhost", port = 5432, dbname = "postgres", user = #"postgres", password = rstudioapi::askForPassword("Database password"))
#extract data created
#movies <- dbGetQuery(sql, "SELECT * FROM movies_edit;")
#desktop
movies <- read.csv(paste0(csv_location, "movies_edit.csv"), header=TRUE, stringsAsFactors = FALSE, colClasses = "character")
##Review and clean data
#cleaning names
movies <- movies %>% clean_names
#modify format to long, take out id variable, and drop na ratings
movies_long <- movies%>%
select(-id)%>%
pivot_longer(-first_name, names_to = "movie", values_to = "rating") %>%
drop_na(rating)
#for all NAs
movies_long_na <- movies%>%
select(-id)%>%
pivot_longer(-first_name, names_to = "movie", values_to = "rating")Class 607, Assignment 3a
Directions: Global Baseline Estimates
Using the information you collected on movie ratings, implement a Global Baseline Estimate recommendation system in R. The attached spreadsheet provides the implementation algorithm.
Most recommender systems use personalized algorithms like “content management” and “item-item collaborative filtering.” Sometimes non-personalized recommenders are also useful or necessary. One of the best non-personalized recommender system algorithms is the “Global Baseline Estimate.
The job here is to use the survey data collected and write the R code that makes a movie recommendation using the Global Baseline Estimate algorithm. Please see the attached spreadsheet for implementation details.
Introduction
This dataset was created in Visual Studio Code using PostgreSQL extension and was created by asking family members and friends to rate 1 (low) - 5 (high) popular movies from 2025-26. If individual asked had not seen the movie, “99” was entered.
Tackling the problem: First I’ll need to clean up the code so any codes labeled 99 are transformed to NA or NULL (in SQL). Then I’ll review the global baseline estimate from the lecture to see how to do it in R.
Anticipated data challenges: I’ve never used this estimate, but I am familiar enough with R so I hope to figure it out.
Citations:
Analysis
#Not shown: Location on computer where I am grabbing the data and creating a saved location
Global Baseline Estimate algorithm \[\widehat{r}_{ui} = \mu + b_u + b_i\]
| Term | Meaning |
|---|---|
| \(\mu\) | Global mean rating |
| \(b_u\) | User bias: this user rates above or below average eg. A critic bias measures whether that critic usually rates above or below the global mean.A positive value means the critic tends to give higher ratings than the typical rating. |
| \(b_i\) | Item bias: this item receives above or below average ratings. eg. A movie bias measures whether a movie usually receives ratings above or below the global mean. A negative value means the movie tends to receive lower ratings than the typical rating. |
The hat means “predicted,” not an observed rating.
#Step 1: get global mean
global_mean <- mean(movies_long$rating)
#Step 2: user bias
user_bias <- movies_long |>
group_by(first_name) |>
summarize(b_u = mean(rating) - global_mean)
#Step 3: item bias
item_bias <- movies_long |>
group_by(movie) |>
summarize(b_i = mean(rating) - global_mean)Predictions for unwatched movies (NA) in dataset
Your task is to create the tibble of biases and implement this arithmetic. Then score every movie first_name has not rated and recommend the highest predicted rating. First, identify movies first_name has not rated. Then calculate the baseline prediction for every candidate and sort from highest to lowest.
movies_long_na <- movies_long_na %>% filter(is.na(rating))
######################################################################
#Jordan
#####################################################################
#did not see:
not_seen <- movies_long_na |>
filter(first_name == "Jordan" ) |>
distinct(movie)
not_seen# A tibble: 0 × 1
# ℹ 1 variable: movie <chr>
#seen:
seen <- movies_long |>
filter(first_name == "Jordan" ) |>
distinct(movie)
seen# A tibble: 5 × 1
movie
<chr>
1 kpop_demon_hunter
2 project_hail_mary
3 disclosure_day
4 michael
5 the_odyssey
#predicted rating:
candidate_predictions_jordan <- movies_long |>
distinct(movie) |>
anti_join(seen, by = "movie") |>
mutate(first_name = "Jordan") |>
left_join(user_bias, by = "first_name") |>
left_join(item_bias, by = "movie") |>
mutate(predicted_rating = global_mean + b_u + b_i) |>
arrange(desc(predicted_rating))
candidate_predictions_jordan# A tibble: 0 × 5
# ℹ 5 variables: movie <chr>, first_name <chr>, b_u <dbl>, b_i <dbl>,
# predicted_rating <dbl>
#recommend project_hail_mary for Jordan
######################################################################
#Kathryn
#####################################################################
#did not see:
not_seen <- movies_long_na |>
filter(first_name == "Kathryn" ) |>
distinct(movie)
not_seen# A tibble: 0 × 1
# ℹ 1 variable: movie <chr>
#seen:
seen <- movies_long |>
filter(first_name == "Kathryn" ) |>
distinct(movie)
seen# A tibble: 5 × 1
movie
<chr>
1 kpop_demon_hunter
2 project_hail_mary
3 disclosure_day
4 michael
5 the_odyssey
#predicted rating:
candidate_predictions_kt <- movies_long |>
distinct(movie) |>
anti_join(seen, by = "movie") |>
mutate(first_name = "Kathryn") |>
left_join(user_bias, by = "first_name") |>
left_join(item_bias, by = "movie") |>
mutate(predicted_rating = global_mean + b_u + b_i) |>
arrange(desc(predicted_rating))
candidate_predictions_kt# A tibble: 0 × 5
# ℹ 5 variables: movie <chr>, first_name <chr>, b_u <dbl>, b_i <dbl>,
# predicted_rating <dbl>
#The only movie Katrhyn did not see was disclosure day. The preticted rating is 2.4 if she watches it.
######################################################################
#Chris
#####################################################################
#did not see:
not_seen <- movies_long_na |>
filter(first_name == "Chris" ) |>
distinct(movie)
not_seen# A tibble: 0 × 1
# ℹ 1 variable: movie <chr>
#seen:
seen <- movies_long |>
filter(first_name == "Chris" ) |>
distinct(movie)
seen# A tibble: 5 × 1
movie
<chr>
1 kpop_demon_hunter
2 project_hail_mary
3 disclosure_day
4 michael
5 the_odyssey
#predicted rating:
candidate_predictions_chris <- movies_long |>
distinct(movie) |>
anti_join(seen, by = "movie") |>
mutate(first_name = "Chris") |>
left_join(user_bias, by = "first_name") |>
left_join(item_bias, by = "movie") |>
mutate(predicted_rating = global_mean + b_u + b_i) |>
arrange(desc(predicted_rating))
candidate_predictions_chris# A tibble: 0 × 5
# ℹ 5 variables: movie <chr>, first_name <chr>, b_u <dbl>, b_i <dbl>,
# predicted_rating <dbl>
#recommend kpop demon hunters for chris
######################################################################
#Jenelle
#####################################################################
#did not see:
not_seen <- movies_long_na |>
filter(first_name == "Mike" ) |>
distinct(movie)
not_seen# A tibble: 0 × 1
# ℹ 1 variable: movie <chr>
#seen:
seen <- movies_long |>
filter(first_name == "Mike" ) |>
distinct(movie)
seen# A tibble: 5 × 1
movie
<chr>
1 kpop_demon_hunter
2 project_hail_mary
3 disclosure_day
4 michael
5 the_odyssey
#predicted rating:
candidate_predictions_mike <- movies_long |>
distinct(movie) |>
anti_join(seen, by = "movie") |>
mutate(first_name = "Mike") |>
left_join(user_bias, by = "first_name") |>
left_join(item_bias, by = "movie") |>
mutate(predicted_rating = global_mean + b_u + b_i) |>
arrange(desc(predicted_rating))
candidate_predictions_mike# A tibble: 0 × 5
# ℹ 5 variables: movie <chr>, first_name <chr>, b_u <dbl>, b_i <dbl>,
# predicted_rating <dbl>
#The only movie Mike did not see was kpop demon hunters. The preticted rating is 3.67 if he watches it.
######################################################################
#Jenelle
#####################################################################
#did not see:
not_seen <- movies_long_na |>
filter(first_name == "Jenelle" ) |>
distinct(movie)
not_seen# A tibble: 0 × 1
# ℹ 1 variable: movie <chr>
#seen:
seen <- movies_long |>
filter(first_name == "Jenelle" ) |>
distinct(movie)
seen# A tibble: 5 × 1
movie
<chr>
1 kpop_demon_hunter
2 project_hail_mary
3 disclosure_day
4 michael
5 the_odyssey
#predicted rating:
candidate_predictions_jenelle <- movies_long |>
distinct(movie) |>
anti_join(seen, by = "movie") |>
mutate(first_name = "Jenelle") |>
left_join(user_bias, by = "first_name") |>
left_join(item_bias, by = "movie") |>
mutate(predicted_rating = global_mean + b_u + b_i) |>
arrange(first_name, desc(predicted_rating))
candidate_predictions_jenelle# A tibble: 0 × 5
# ℹ 5 variables: movie <chr>, first_name <chr>, b_u <dbl>, b_i <dbl>,
# predicted_rating <dbl>
#would recommend Michael to Jenelle
#combine and sort
predictions <- bind_rows(candidate_predictions_jenelle, candidate_predictions_mike, candidate_predictions_chris, candidate_predictions_kt, candidate_predictions_jordan)%>%
arrange(first_name, desc(predicted_rating))%>%
select(first_name, movie, b_u, b_i, predicted_rating)
predictions# A tibble: 0 × 5
# ℹ 5 variables: first_name <chr>, movie <chr>, b_u <dbl>, b_i <dbl>,
# predicted_rating <dbl>
Conclusion
I found that after understanding the algorithm through the equation, excel document, and presentation qmd (the r examples helped a lot) it was interesting to go through and predict the survey participant’s unwatched movie ratings. I ended up changing the SQL table set up rather than the R, and it was easier than expected.
I know there is a way to speed up the predictions of each participant, maybe as a loop, so I’d like to try that next time.