Week 3A Assignment - Global Baseline Estimate

Author

Muhammad Ali

Global Baseline Estimate

Approach

Recap

This assignment is a follow up on the previous assignment Week 2A’s assignment, Movie Ratings where the goal of that assignment was to conduct a survey using a Google Form about recent popular movies and the users ratings of them.

Goal

To expand on the use of that survey’s data, we will now be creating a recommender system using the Global Baseline Algorithm to estimate what a user would rate a movie they had not seen using a formula that consists of information of:

  • Global Mean \(\mu\)

  • User Bias \(b_u\)

  • Item Bias \(b_i\)

\[\begin{equation*} \hat{r}_{ui} = \mu + b_u + b_i \end{equation*}\]

About the Algorithm

The algorithm as mentioned is an estimate to predict what a user would rate something that they had not seen before.

We have the formula above, but what does it mean specifically rather than taking averages in a spreadsheet and computing addition and subtraction between the variables?

Definitions

  • Predicted Rating \(\hat{r}_{ui}\): Our predicted rating after computing the arithmetic among the variables. This is not an observed rating.

  • Global Mean \(\mu\): The mean of all the ratings for all movies in 1 variable.

  • User Bias \(b_u\): This is a way to see if the user rated a something higher or lower than the global mean with its users

  • Item Bias \(b_i\): Similar to user bias, this is a way to see if someone rated the movies higher than the global mean.

In different contexts, the formula still applies but in this context it is on movie ratings as a great introduction to recommendation systems as in the future the complexity will increase.

The Plan

Given that this is a step up to the previous assignment, we need to have the cleaned version of the dataset as it contains appropriate column names and column typings.

Following that we need to rearrange our table from a wide table to a long table to be able to compute calculations easier.

Once we have our long table, we can then start computing the averages by hand and then implementing it by code.

After doing one person, we can apply the same format for people that had not seen a certain movie.

Codebase

First we need to import the data values once again into a tribble

Import Library

library(tidyverse)

Creating Tribble

ratings_wide <- tribble(
  ~user, ~spider_man_bnd, ~the_odyssey, ~coyote_vs_acme, ~the_whisper_man, ~toy_story_5, ~project_hail_mary,
  "1", 4, NA, NA, NA, 4, 5,
  "2", 4, 3, NA, NA, NA, 5,
  "3", 4, 3, NA, NA, 2, 5,
  "4", 4, 5, NA, NA, 4, 5,
  "5", NA, NA, NA, NA, NA, NA,
  "6", 5, NA, 5, NA, 1, 5,
  "7", 5, 4, 5, NA, 4, 5,
  "8", NA, NA, NA, NA, NA, NA,
  "9", NA, NA, NA, NA, NA, NA,
  "10", 3, 5, NA, NA, NA, 5,
  "11", 4, NA, NA, NA, NA, 4,
  "12", NA, NA, NA, NA, NA, 1,
  "13", 5, 4, 4, 4, 5, 5,
  "14", 5, 5, 4, NA, 3, 3,
  "15", NA, NA, NA, NA, NA, 5,
  "16", 5, 1, 3, 5, 5, 2,
  "17", 3, 1, 2, 4, NA, 5,
  "18", 5, 5, NA, NA, NA, 5,
  "19", 5, NA, NA, NA, 5, 5,
  "20", NA, NA, NA, NA, 4, 5,
  "21", NA, NA, NA, NA, NA, 2
)

ratings_wide
# A tibble: 21 × 7
   user  spider_man_bnd the_odyssey coyote_vs_acme the_whisper_man toy_story_5
   <chr>          <dbl>       <dbl>          <dbl>           <dbl>       <dbl>
 1 1                  4          NA             NA              NA           4
 2 2                  4           3             NA              NA          NA
 3 3                  4           3             NA              NA           2
 4 4                  4           5             NA              NA           4
 5 5                 NA          NA             NA              NA          NA
 6 6                  5          NA              5              NA           1
 7 7                  5           4              5              NA           4
 8 8                 NA          NA             NA              NA          NA
 9 9                 NA          NA             NA              NA          NA
10 10                 3           5             NA              NA          NA
# ℹ 11 more rows
# ℹ 1 more variable: project_hail_mary <dbl>

Now we have the data in tabular format, for this assignment and the algorithm in general, we need it in a long format rather than a wide so it is easier to calculate averages.

Wide to Long Format

ratings_long <- ratings_wide |>
  pivot_longer(-user, names_to = "movies", values_to = "rating") |>
  drop_na(rating)

ratings_long
# A tibble: 61 × 3
   user  movies            rating
   <chr> <chr>              <dbl>
 1 1     spider_man_bnd         4
 2 1     toy_story_5            4
 3 1     project_hail_mary      5
 4 2     spider_man_bnd         4
 5 2     the_odyssey            3
 6 2     project_hail_mary      5
 7 3     spider_man_bnd         4
 8 3     the_odyssey            3
 9 3     toy_story_5            2
10 3     project_hail_mary      5
# ℹ 51 more rows

We have successfully converted the table to a long format. Which means every row is now stacked upon each other

Global Average Rating

global_mean <- mean(ratings_long$rating)
global_mean
[1] 4.04918

We now have one of the variables for our formula:

  • \(\mu\): 4.04918

User Bias

The next step is to retrieve our \(b_u\) variable, the user bias, which indicates if the user average rating is higher or lower than the global mean

user_bias <- ratings_long |>
  group_by(user) |>
  summarize(b_u = mean(rating) - global_mean)

user_bias
# A tibble: 18 × 2
   user      b_u
   <chr>   <dbl>
 1 1      0.284 
 2 10     0.284 
 3 11    -0.0492
 4 12    -3.05  
 5 13     0.451 
 6 14    -0.0492
 7 15     0.951 
 8 16    -0.549 
 9 17    -1.05  
10 18     0.951 
11 19     0.951 
12 2     -0.0492
13 20     0.451 
14 21    -2.05  
15 3     -0.549 
16 4      0.451 
17 6     -0.0492
18 7      0.551 

We now have our user bias variable for each user, and at first glance, there are those slightly above or below, or way below the global mean average.

Item Bias

item_bias <- ratings_long |>
  group_by(movies) |>
  summarize(b_i = mean(rating) - global_mean)

item_bias
# A tibble: 6 × 2
  movies               b_i
  <chr>              <dbl>
1 coyote_vs_acme    -0.216
2 project_hail_mary  0.229
3 spider_man_bnd     0.308
4 the_odyssey       -0.449
5 the_whisper_man    0.284
6 toy_story_5       -0.349

We have successfully created our last variable for our formula.

With all this information we can now proceed to predict what certain users would rate a movie that they had not seen.

Some Examples Of Predictions

Given that there are 21 rows and some users have not seen all of the movies or most of them, I will be doing 3 examples to show that the formula and arithmetic works.

First Example - User 4

For the first example, we will predict movies user number 4 had not seen and predict what they would rate it had they seen it.

four_seen <- ratings_long |>
  filter(user == "4") |>
  distinct(movies)

four_seen
# A tibble: 4 × 1
  movies           
  <chr>            
1 spider_man_bnd   
2 the_odyssey      
3 toy_story_5      
4 project_hail_mary

Let us do the predictions arithmetically before writing in code:

Quantity Rounded Value How?
Global Mean, \(\mu\) 4.05 Global mean given from code
User 4 Bias, \(b_u\) 0.45 Refer to User Bias Section
Coyote vs ACME Bias \(b_i\) -0.22 Refer to Item Bias Section

Prediction: 4.05 + 0.45 + (-0.22) = 4.28

Quantity Rounded Value How?
Global Mean, \(\mu\) 4.05 Global mean given from code
User 4 Bias, \(b_u\) 0.45 Refer to User Bias Section
The Whisper Man Bias \(b_i\) 0.28 Refer to Item Bias Section

Prediction: 4.05 + 0.45 + 0.28 = 4.78

four_predictions <- ratings_long |>
  distinct(movies) |>
  anti_join(four_seen, by = "movies") |>
  mutate(user = "4") |> 
  left_join(user_bias, by = "user") |>
  left_join(item_bias, by = "movies") |>
  mutate(predicted_rating = global_mean + b_u + b_i) |>
  arrange(desc(predicted_rating))

four_predictions
# A tibble: 2 × 5
  movies          user    b_u    b_i predicted_rating
  <chr>           <chr> <dbl>  <dbl>            <dbl>
1 the_whisper_man 4     0.451  0.284             4.78
2 coyote_vs_acme  4     0.451 -0.216             4.28

After computing the arithmetic by carefully referring to each variable respective tibbles, we were able to code the prediction tibble and get a return of the predicted rating in a non-rounded format, but it does align with our results.

A simple formula for the R Code above:

  1. Retrieve Distinct Movies
  2. That they have not seen (NA values)
  3. Mutate so that we create a column with only showing “4”
  4. Left Join to bring in user bias
  5. Left Join to bring item bias
  6. Mutate to create a new column for predicted rating
  7. Arrange in descending order

Example 2: User 7

This time we will predict this user who has seen one of the movies out of the 6.

seven_seen <- ratings_long |>
  filter(user == "7") |>
  distinct(movies)

seven_seen
# A tibble: 5 × 1
  movies           
  <chr>            
1 spider_man_bnd   
2 the_odyssey      
3 coyote_vs_acme   
4 toy_story_5      
5 project_hail_mary

Let us do the predictions arithmetically before writing in code:

Quantity Rounded Value How?
Global Mean, \(\mu\) 4.05 Global mean given from code
User 4 Bias, \(b_u\) 0.95 Refer to User Bias Section
The Whisper Man Bias \(b_i\) 0.28 Refer to Item Bias Section

Prediction: 4.05 + 0.55 + 0.28 = 4.88

seven_predictions <- ratings_long |>
  distinct(movies) |>
  anti_join(seven_seen, by = "movies") |>
  mutate(user = "7") |>
  left_join(user_bias, by = "user") |>
  left_join(item_bias, by = "movies") |>
  mutate(predicted_rating = global_mean + b_u + b_i) |>
  arrange(desc(predicted_rating))

seven_predictions
# A tibble: 1 × 5
  movies          user    b_u   b_i predicted_rating
  <chr>           <chr> <dbl> <dbl>            <dbl>
1 the_whisper_man 7     0.551 0.284             4.88

Once again, this formulaic and arithmetic calculation was correct yet again.

Final Example - User 10

Lets try with 3 missing ratings:

ten_seen <- ratings_long |>
  filter(user == "10") |>
  distinct(movies)

ten_seen
# A tibble: 3 × 1
  movies           
  <chr>            
1 spider_man_bnd   
2 the_odyssey      
3 project_hail_mary
Quantity Rounded Value How?
Global Mean, \(\mu\) 4.05 Global mean given from code
User 10 Bias, \(b_u\) 0.28 Refer to User Bias Section
Coyote vs ACME Bias \(b_i\) -0.22 Refer to Item Bias Section

Prediction: 4.05 + 0.28+ (-0.22) = 4.11

Quantity Rounded Value How?
Global Mean, \(\mu\) 4.05 Global mean given from code
User 10 Bias, \(b_u\) 0.28 Refer to User Bias Section
The Whisper Man Bias \(b_i\) 0.28 Refer to Item Bias Section

Prediction: 4.05 + 0.28 + 0.28 = 4.61

Quantity Rounded Value How?
Global Mean, \(\mu\) 4.05 Global mean given from code
User 10 Bias, \(b_u\) 0.28 Refer to User Bias Section
Toy Story 5 Bias \(b_i\) -0.35 Refer to Item Bias Section

Prediction: 4.05 + 0.28+ (-0.35) = 3.98