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:
Anthropic. (2025). Claude Opus 4.5 [Large language model]. (https://claude.ai/) Accessed January 8, 2026. Link to chat.
Analysis
#Not shown: Location on computer where I am grabbing the data and creating a saved location
Code
#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 usethis, #r environment RPostgres, #SQL DBI, #SQL rstuioapi, #api# 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 )
Installing package into 'C:/Users/tournat/AppData/Local/R/win-library/4.6'
(as 'lib' is unspecified)
Code
#Load data#connect to database sql <-dbConnect( RPostgres::Postgres(),host =Sys.getenv("PG_HOST"),port =as.integer(Sys.getenv("PG_PORT")),dbname =Sys.getenv("PG_DB"),user =Sys.getenv("PG_USER"),password =Sys.getenv("PG_PASSWORD"))#extract data created movies <-dbGetQuery(sql, "SELECT * FROM movies_edit;")#desktop#movies <- read.csv(paste0(csv_location, "movies_edit.csv"), header=TRUE, colClasses = "character", na.strings = c("NULL")) ##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")
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.
Code
#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.
#seen: seen <- movies_long |>filter(first_name =="Kathryn" ) |>distinct(movie)seen
Code
#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 #The only movie Katrhyn did not see was disclosure day. The preticted rating is 2.4 if she watches it.
#seen: seen <- movies_long |>filter(first_name =="Mike" ) |>distinct(movie)seen
Code
#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 #The only movie Mike did not see was kpop demon hunters. The preticted rating is 3.67 if he watches it.
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 but then ran into issues with rendering and switching to a csv table. I ended up fixing the errors by creating a new r environment to store the password rather than typing it in.
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.