Objective

Using the information you collected on movie ratings, implement a Global Baseline Estimate recommendation system in R.
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.

Import Packages

library(tidyverse)
library(openintro)

Load & Read Data

#install.packages("readxl")
library(readxl)
## Warning: package 'readxl' was built under R version 4.3.3
# GitHub URL of the Excel file (Raw file URL)
github_raw_url <- "https://raw.githubusercontent.com/pujaroy280/DATA607Week11RecommenderSystems/main/MovieRatings.xlsx"

# File download location
download_location <- "MovieRatings.xlsx"

# Download the file from GitHub
download.file(url = github_raw_url, destfile = download_location, mode = "wb")

# Read the Excel file
movie_ratings <- read_excel(download_location)

# View the data
print(movie_ratings)
## # A tibble: 16 × 7
##    Critic  CaptainAmerica Deadpool Frozen JungleBook PitchPerfect2 StarWarsForce
##    <chr>            <dbl>    <dbl>  <dbl>      <dbl>         <dbl>         <dbl>
##  1 Burton              NA       NA     NA          4            NA             4
##  2 Charley              4        5      4          3             2             3
##  3 Dan                 NA        5     NA         NA            NA             5
##  4 Dieudo…              5        4     NA         NA            NA             5
##  5 Matt                 4       NA      2         NA             2             5
##  6 Mauric…              4       NA      3          3             4            NA
##  7 Max                  4        4      4          2             2             4
##  8 Nathan              NA       NA     NA         NA            NA             4
##  9 Param                4        4      1         NA            NA             5
## 10 Parshu               4        3      5          5             2             3
## 11 Prasha…              5        5      5          5            NA             4
## 12 Shipra              NA       NA      4          5            NA             3
## 13 Sreeja…              5        5      5          4             4             5
## 14 Steve                4       NA     NA         NA            NA             4
## 15 Vuthy                4        5      3          3             3            NA
## 16 Xingjia             NA       NA      5          5            NA            NA
# Once you're done, you can delete the downloaded file if you want
# file.remove(download_location)

Data Cleaning

# Replace NA values with 0
#movie_ratings_new <- replace(movie_ratings, is.na(movie_ratings), 0)
#print(movie_ratings_new)
# Print column names of the data frame
#print(names(movie_ratings_new))

Calculate Global Average Rating

# Subset the data frame to include only the columns for specific movies
movies_subset <- movie_ratings[, c("CaptainAmerica", "Deadpool", "Frozen", "JungleBook", "PitchPerfect2", "StarWarsForce")]

# Calculate the global average rating excluding NA values
global_average_rating <- mean(as.matrix(movies_subset), na.rm = TRUE)

# Print the global average rating
print(global_average_rating)
## [1] 3.934426

Calculate Baseline Estimate for Movie & Critic

# Calculate baseline estimate for each movie and critic

# Function to calculate baseline estimate
calculate_baseline <- function(ratings_matrix, mu) {
  # Initialize empty matrix for baseline estimates
  baseline_matrix <- matrix(0, nrow = nrow(ratings_matrix), ncol = ncol(ratings_matrix))
  
  # Loop through each critic
  for (i in 1:nrow(ratings_matrix)) {
    # Loop through each movie
    for (j in 1:ncol(ratings_matrix)) {
      # Calculate baseline estimate for critic i and movie j
      if (!is.na(ratings_matrix[i, j])) {
        baseline_matrix[i, j] <- mu + mean(ratings_matrix[i, ], na.rm = TRUE) - mu + mean(ratings_matrix[, j], na.rm = TRUE) - mu
      }
    }
  }
  
  return(baseline_matrix)
}

# Calculate global average rating excluding NA values
mu <- mean(as.matrix(movie_ratings), na.rm = TRUE)
## Warning in mean.default(as.matrix(movie_ratings), na.rm = TRUE): argument is
## not numeric or logical: returning NA
# Subset the data to exclude the first column (Critic column)
ratings_matrix <- as.matrix(movie_ratings[, -1])

# Calculate baseline estimates
baseline_estimates <- calculate_baseline(ratings_matrix, mu)

Recommend Movies

# Recommend movies based on highest baseline estimate for each user
recommend_movies <- function(baseline_matrix) {
  # Initialize empty list to store recommendations
  recommendations <- list()
  
  # Loop through each user
  for (i in 1:nrow(baseline_matrix)) {
    # Find movie with highest baseline estimate for user i
    max_index <- which.max(baseline_matrix[i, ])
    
    # Store recommendation
    recommendations[[i]] <- names(movie_ratings)[-1][max_index]
  }
  
  return(recommendations)
}

# Get movie recommendations
movie_recommendations <- recommend_movies(baseline_estimates)

# Print movie recommendations for each user
for (i in 1:length(movie_recommendations)) {
  cat("Critic", i, ":", movie_recommendations[[i]], "\n")
}
## Critic 1 : CaptainAmerica 
## Critic 2 :  
## Critic 3 : CaptainAmerica 
## Critic 4 : Frozen 
## Critic 5 : Deadpool 
## Critic 6 : Deadpool 
## Critic 7 :  
## Critic 8 : CaptainAmerica 
## Critic 9 : JungleBook 
## Critic 10 :  
## Critic 11 : PitchPerfect2 
## Critic 12 : CaptainAmerica 
## Critic 13 :  
## Critic 14 : Deadpool 
## Critic 15 : StarWarsForce 
## Critic 16 : CaptainAmerica
LS0tDQp0aXRsZTogIkRBVEEgNjA3OiBXZWVrIDExIEV4dHJhIENyZWRpdDogUmVjb21tZW5kZXIgU3lzdGVtcyINCmF1dGhvcjogIlB1amEgUm95Ig0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiBvcGVuaW50cm86OmxhYl9yZXBvcnQNCi0tLQ0KDQojIyMgT2JqZWN0aXZlDQpVc2luZyB0aGUgaW5mb3JtYXRpb24geW91IGNvbGxlY3RlZCBvbiBtb3ZpZSByYXRpbmdzLCBpbXBsZW1lbnQgYSBHbG9iYWwgQmFzZWxpbmUgRXN0aW1hdGUgcmVjb21tZW5kYXRpb24gc3lzdGVtIGluIFIuICANCk1vc3QgcmVjb21tZW5kZXIgc3lzdGVtcyB1c2UgcGVyc29uYWxpemVkIGFsZ29yaXRobXMgbGlrZSDigJxjb250ZW50IG1hbmFnZW1lbnTigJ0gYW5kIOKAnGl0ZW0taXRlbSBjb2xsYWJvcmF0aXZlIGZpbHRlcmluZy7igJ0gU29tZXRpbWVzIG5vbi1wZXJzb25hbGl6ZWQgcmVjb21tZW5kZXJzIGFyZSBhbHNvIHVzZWZ1bCBvciBuZWNlc3NhcnkuIE9uZSBvZiB0aGUgYmVzdCBub24tcGVyc29uYWxpemVkIHJlY29tbWVuZGVyIHN5c3RlbSBhbGdvcml0aG1zIGlzIHRoZSDigJxHbG9iYWwgQmFzZWxpbmUgRXN0aW1hdGUuDQpUaGUgam9iIGhlcmUgaXMgdG8gdXNlIHRoZSBzdXJ2ZXkgZGF0YSBjb2xsZWN0ZWQgYW5kIHdyaXRlIHRoZSBSIGNvZGUgdGhhdCBtYWtlcyBhIG1vdmllIHJlY29tbWVuZGF0aW9uIHVzaW5nIHRoZSBHbG9iYWwgQmFzZWxpbmUgRXN0aW1hdGUgYWxnb3JpdGhtLiANCg0KDQojIyMgSW1wb3J0IFBhY2thZ2VzDQpgYGB7ciBsb2FkLXBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KG9wZW5pbnRybykNCmBgYA0KDQojIyMgTG9hZCAmIFJlYWQgRGF0YQ0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikNCmxpYnJhcnkocmVhZHhsKQ0KDQojIEdpdEh1YiBVUkwgb2YgdGhlIEV4Y2VsIGZpbGUgKFJhdyBmaWxlIFVSTCkNCmdpdGh1Yl9yYXdfdXJsIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcHVqYXJveTI4MC9EQVRBNjA3V2VlazExUmVjb21tZW5kZXJTeXN0ZW1zL21haW4vTW92aWVSYXRpbmdzLnhsc3giDQoNCiMgRmlsZSBkb3dubG9hZCBsb2NhdGlvbg0KZG93bmxvYWRfbG9jYXRpb24gPC0gIk1vdmllUmF0aW5ncy54bHN4Ig0KDQojIERvd25sb2FkIHRoZSBmaWxlIGZyb20gR2l0SHViDQpkb3dubG9hZC5maWxlKHVybCA9IGdpdGh1Yl9yYXdfdXJsLCBkZXN0ZmlsZSA9IGRvd25sb2FkX2xvY2F0aW9uLCBtb2RlID0gIndiIikNCg0KIyBSZWFkIHRoZSBFeGNlbCBmaWxlDQptb3ZpZV9yYXRpbmdzIDwtIHJlYWRfZXhjZWwoZG93bmxvYWRfbG9jYXRpb24pDQoNCiMgVmlldyB0aGUgZGF0YQ0KcHJpbnQobW92aWVfcmF0aW5ncykNCg0KIyBPbmNlIHlvdSdyZSBkb25lLCB5b3UgY2FuIGRlbGV0ZSB0aGUgZG93bmxvYWRlZCBmaWxlIGlmIHlvdSB3YW50DQojIGZpbGUucmVtb3ZlKGRvd25sb2FkX2xvY2F0aW9uKQ0KDQpgYGANCiMjIyBEYXRhIENsZWFuaW5nDQpgYGB7cn0NCiMgUmVwbGFjZSBOQSB2YWx1ZXMgd2l0aCAwDQojbW92aWVfcmF0aW5nc19uZXcgPC0gcmVwbGFjZShtb3ZpZV9yYXRpbmdzLCBpcy5uYShtb3ZpZV9yYXRpbmdzKSwgMCkNCiNwcmludChtb3ZpZV9yYXRpbmdzX25ldykNCmBgYA0KDQpgYGB7cn0NCiMgUHJpbnQgY29sdW1uIG5hbWVzIG9mIHRoZSBkYXRhIGZyYW1lDQojcHJpbnQobmFtZXMobW92aWVfcmF0aW5nc19uZXcpKQ0KYGBgDQoNCiMjIyBDYWxjdWxhdGUgR2xvYmFsIEF2ZXJhZ2UgUmF0aW5nDQpgYGB7cn0NCiMgU3Vic2V0IHRoZSBkYXRhIGZyYW1lIHRvIGluY2x1ZGUgb25seSB0aGUgY29sdW1ucyBmb3Igc3BlY2lmaWMgbW92aWVzDQptb3ZpZXNfc3Vic2V0IDwtIG1vdmllX3JhdGluZ3NbLCBjKCJDYXB0YWluQW1lcmljYSIsICJEZWFkcG9vbCIsICJGcm96ZW4iLCAiSnVuZ2xlQm9vayIsICJQaXRjaFBlcmZlY3QyIiwgIlN0YXJXYXJzRm9yY2UiKV0NCg0KIyBDYWxjdWxhdGUgdGhlIGdsb2JhbCBhdmVyYWdlIHJhdGluZyBleGNsdWRpbmcgTkEgdmFsdWVzDQpnbG9iYWxfYXZlcmFnZV9yYXRpbmcgPC0gbWVhbihhcy5tYXRyaXgobW92aWVzX3N1YnNldCksIG5hLnJtID0gVFJVRSkNCg0KIyBQcmludCB0aGUgZ2xvYmFsIGF2ZXJhZ2UgcmF0aW5nDQpwcmludChnbG9iYWxfYXZlcmFnZV9yYXRpbmcpDQoNCmBgYA0KIyMjIENhbGN1bGF0ZSBCYXNlbGluZSBFc3RpbWF0ZSBmb3IgTW92aWUgJiBDcml0aWMNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgYmFzZWxpbmUgZXN0aW1hdGUgZm9yIGVhY2ggbW92aWUgYW5kIGNyaXRpYw0KDQojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBiYXNlbGluZSBlc3RpbWF0ZQ0KY2FsY3VsYXRlX2Jhc2VsaW5lIDwtIGZ1bmN0aW9uKHJhdGluZ3NfbWF0cml4LCBtdSkgew0KICAjIEluaXRpYWxpemUgZW1wdHkgbWF0cml4IGZvciBiYXNlbGluZSBlc3RpbWF0ZXMNCiAgYmFzZWxpbmVfbWF0cml4IDwtIG1hdHJpeCgwLCBucm93ID0gbnJvdyhyYXRpbmdzX21hdHJpeCksIG5jb2wgPSBuY29sKHJhdGluZ3NfbWF0cml4KSkNCiAgDQogICMgTG9vcCB0aHJvdWdoIGVhY2ggY3JpdGljDQogIGZvciAoaSBpbiAxOm5yb3cocmF0aW5nc19tYXRyaXgpKSB7DQogICAgIyBMb29wIHRocm91Z2ggZWFjaCBtb3ZpZQ0KICAgIGZvciAoaiBpbiAxOm5jb2wocmF0aW5nc19tYXRyaXgpKSB7DQogICAgICAjIENhbGN1bGF0ZSBiYXNlbGluZSBlc3RpbWF0ZSBmb3IgY3JpdGljIGkgYW5kIG1vdmllIGoNCiAgICAgIGlmICghaXMubmEocmF0aW5nc19tYXRyaXhbaSwgal0pKSB7DQogICAgICAgIGJhc2VsaW5lX21hdHJpeFtpLCBqXSA8LSBtdSArIG1lYW4ocmF0aW5nc19tYXRyaXhbaSwgXSwgbmEucm0gPSBUUlVFKSAtIG11ICsgbWVhbihyYXRpbmdzX21hdHJpeFssIGpdLCBuYS5ybSA9IFRSVUUpIC0gbXUNCiAgICAgIH0NCiAgICB9DQogIH0NCiAgDQogIHJldHVybihiYXNlbGluZV9tYXRyaXgpDQp9DQoNCiMgQ2FsY3VsYXRlIGdsb2JhbCBhdmVyYWdlIHJhdGluZyBleGNsdWRpbmcgTkEgdmFsdWVzDQptdSA8LSBtZWFuKGFzLm1hdHJpeChtb3ZpZV9yYXRpbmdzKSwgbmEucm0gPSBUUlVFKQ0KDQojIFN1YnNldCB0aGUgZGF0YSB0byBleGNsdWRlIHRoZSBmaXJzdCBjb2x1bW4gKENyaXRpYyBjb2x1bW4pDQpyYXRpbmdzX21hdHJpeCA8LSBhcy5tYXRyaXgobW92aWVfcmF0aW5nc1ssIC0xXSkNCg0KIyBDYWxjdWxhdGUgYmFzZWxpbmUgZXN0aW1hdGVzDQpiYXNlbGluZV9lc3RpbWF0ZXMgPC0gY2FsY3VsYXRlX2Jhc2VsaW5lKHJhdGluZ3NfbWF0cml4LCBtdSkNCmBgYA0KDQojIyMgUmVjb21tZW5kIE1vdmllcw0KYGBge3J9DQojIFJlY29tbWVuZCBtb3ZpZXMgYmFzZWQgb24gaGlnaGVzdCBiYXNlbGluZSBlc3RpbWF0ZSBmb3IgZWFjaCB1c2VyDQpyZWNvbW1lbmRfbW92aWVzIDwtIGZ1bmN0aW9uKGJhc2VsaW5lX21hdHJpeCkgew0KICAjIEluaXRpYWxpemUgZW1wdHkgbGlzdCB0byBzdG9yZSByZWNvbW1lbmRhdGlvbnMNCiAgcmVjb21tZW5kYXRpb25zIDwtIGxpc3QoKQ0KICANCiAgIyBMb29wIHRocm91Z2ggZWFjaCB1c2VyDQogIGZvciAoaSBpbiAxOm5yb3coYmFzZWxpbmVfbWF0cml4KSkgew0KICAgICMgRmluZCBtb3ZpZSB3aXRoIGhpZ2hlc3QgYmFzZWxpbmUgZXN0aW1hdGUgZm9yIHVzZXIgaQ0KICAgIG1heF9pbmRleCA8LSB3aGljaC5tYXgoYmFzZWxpbmVfbWF0cml4W2ksIF0pDQogICAgDQogICAgIyBTdG9yZSByZWNvbW1lbmRhdGlvbg0KICAgIHJlY29tbWVuZGF0aW9uc1tbaV1dIDwtIG5hbWVzKG1vdmllX3JhdGluZ3MpWy0xXVttYXhfaW5kZXhdDQogIH0NCiAgDQogIHJldHVybihyZWNvbW1lbmRhdGlvbnMpDQp9DQoNCiMgR2V0IG1vdmllIHJlY29tbWVuZGF0aW9ucw0KbW92aWVfcmVjb21tZW5kYXRpb25zIDwtIHJlY29tbWVuZF9tb3ZpZXMoYmFzZWxpbmVfZXN0aW1hdGVzKQ0KDQojIFByaW50IG1vdmllIHJlY29tbWVuZGF0aW9ucyBmb3IgZWFjaCB1c2VyDQpmb3IgKGkgaW4gMTpsZW5ndGgobW92aWVfcmVjb21tZW5kYXRpb25zKSkgew0KICBjYXQoIkNyaXRpYyIsIGksICI6IiwgbW92aWVfcmVjb21tZW5kYXRpb25zW1tpXV0sICJcbiIpDQp9DQoNCmBgYA0K