Packages.

# renv::init()

library(rprojroot)
library(readxl)
library(dplyr)
library(tidyr )
# library(DT)
library(shiny)
library(shinythemes)
# library(shinydashboard)

# re0nv::snapshot()

Data.

movie_ratings <- read_excel("MovieRatings.xlsx", sheet = "MovieRatings")


# long format for easier computation
ratings_long <- pivot_longer(movie_ratings, cols = -Critic, names_to = "Movie", values_to = "Rating")

# global average rating
mu <- mean(ratings_long$Rating, na.rm = TRUE)

# user biases
user_biases <- ratings_long %>%
  group_by(Critic) %>%
  summarize(user_avg = mean(Rating, na.rm = TRUE)) %>%
  mutate(b_u = user_avg - mu)

# movie biases
movie_biases <- ratings_long %>%
  group_by(Movie) %>%
  summarize(movie_avg = mean(Rating, na.rm = TRUE)) %>%
  mutate(b_i = movie_avg - mu)

# joining biases back to the original ratings
ratings_with_biases <- ratings_long %>%
  left_join(user_biases, by = "Critic") %>%
  left_join(movie_biases, by = "Movie")

# Global Baseline Estimates
ratings_with_biases <- ratings_with_biases %>%
  mutate(global_baseline_estimate = mu + b_u + b_i)

Analyzing the predictions and making recommendations.

# movies not rated by each user and with highest estimates
recommendations <- ratings_with_biases %>%
  filter(is.na(Rating)) %>%
  arrange(desc(global_baseline_estimate))

# top 5 recommendations for each user
top_recommendations <- recommendations %>%
  group_by(Critic) %>%
  slice_max(global_baseline_estimate, n = 5) %>%
  ungroup()

print(top_recommendations)
## # A tibble: 35 × 8
##    Critic  Movie Rating user_avg    b_u movie_avg     b_i global_baseline_esti…¹
##    <chr>   <chr>  <dbl>    <dbl>  <dbl>     <dbl>   <dbl>                  <dbl>
##  1 Burton  Dead…     NA     4    0.0656      4.44  0.510                    4.51
##  2 Burton  Capt…     NA     4    0.0656      4.27  0.338                    4.34
##  3 Burton  Froz…     NA     4    0.0656      3.73 -0.207                    3.79
##  4 Burton  Pitc…     NA     4    0.0656      2.71 -1.22                     2.78
##  5 Dan     Capt…     NA     5    1.07        4.27  0.338                    5.34
##  6 Dan     Jung…     NA     5    1.07        3.9  -0.0344                   4.97
##  7 Dan     Froz…     NA     5    1.07        3.73 -0.207                    4.79
##  8 Dan     Pitc…     NA     5    1.07        2.71 -1.22                     3.78
##  9 Dieudo… Jung…     NA     4.67 0.732       3.9  -0.0344                   4.63
## 10 Dieudo… Froz…     NA     4.67 0.732       3.73 -0.207                    4.46
## # ℹ 25 more rows
## # ℹ abbreviated name: ¹​global_baseline_estimate

Creating a Shiny App to Show the movie recommendations.

# UI
ui <- fluidPage(
  theme = shinytheme("flatly"),
  titlePanel("Movie Recommendations"),
  sidebarLayout(
    sidebarPanel(
      selectInput("selectedCritic", "Choose a Critic:",
                  choices = unique(na.omit(top_recommendations$Critic))) 
    ),
    mainPanel(
      tableOutput("recommendationsTable")
    )
  )
)

# Server
server <- function(input, output, session) {
  output$recommendationsTable <- renderTable({
    tryCatch({
      filtered <- filter(top_recommendations, Critic == input$selectedCritic)
      head(filtered, 5)
    }, error = function(e) {
      data.frame(Error = e$message)
    })
  })
  # session information logging after session ending
  session$onSessionEnded(function() {
    info <- sessionInfo()
    print(info)
  })
}

# the app
shinyApp(ui = ui, server = server)
Shiny applications not supported in static R Markdown documents

Deploy the app to shinyapps.io.

# library(rsconnect)
# deployApp("607_week11_movieRecommender_shinyapp.rmd")

Please see the deployed shiny app at this link.