Introduction

These are the packages I installed initially

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dbplyr)
## 
## Attaching package: 'dbplyr'
## 
## The following objects are masked from 'package:dplyr':
## 
##     ident, sql, sql_escape_ident, sql_escape_string
library(dplyr)

Also, I converted the Excel file in which I had inserted the actual rankings into a CSV. However, some of the names included colons in the title which messed things up when selecting columns. Also, later, I had to create a new Excel/CSV file where I inserted zeros instead of empty spaces for non-responses. R converted the empty spaces into NAs. This also prevented me from doing some calculations. However, I could not start with the file with zeros for NAs, because I had to calculate averages and the zeros would have been counted as numbers.

url<- "https://raw.githubusercontent.com/Enrique01234/607-Fall-2026/refs/heads/main/Movies%20to%20Export.xlsx%20-%20Sheet3.csv"
Mov2 <- read_csv(file = url,
 show_col_types = FALSE,
 progress = FALSE)

This is the way the actual rankings look like

Mov2
## # A tibble: 5 × 6
##   Name      Barbie Captain  Dune Nomadland Oppenheimer
##   <chr>      <dbl>   <dbl> <dbl>     <dbl>       <dbl>
## 1 Brenda         4      NA    NA         3          NA
## 2 Cornelia       1      NA    NA         5          NA
## 3 Alberto        3      NA     3        NA           5
## 4 Nathaniel      5       3    NA        NA          NA
## 5 Enrique        4       3    NA        NA          NA

Movies and personal averages

In order to calculate the Mean Movie Rating (MMR), first the averages by person and by movie need to be calculated. These will be used to calculate the relative to average ratings (again by person and by movie), which go into the MMR formula.

First, I calculated the averages per person (person_mean) and added it to the data fare. The row mean is calculated using all columns with numbers (i.e., excluding the column with names). Also, the na.rm = TRUE is used to skip the NAs in calculating the average.

Mov2 <- Mov2 %>%
 mutate(person_mean = rowMeans(pick(where(is.numeric)), na.rm = TRUE))
  glimpse(Mov2)
## Rows: 5
## Columns: 7
## $ Name        <chr> "Brenda", "Cornelia", "Alberto", "Nathaniel", "Enrique"
## $ Barbie      <dbl> 4, 1, 3, 5, 4
## $ Captain     <dbl> NA, NA, NA, 3, 3
## $ Dune        <dbl> NA, NA, 3, NA, NA
## $ Nomadland   <dbl> 3, 5, NA, NA, NA
## $ Oppenheimer <dbl> NA, NA, 5, NA, NA
## $ person_mean <dbl> 3.500000, 3.000000, 3.666667, 4.000000, 3.500000

Then I calculated the average ranking per movie. Each one is shown separately and the names are always constructed by adding “_mean” to the movie name.

Mov2 <- Mov2 %>%
 mutate(Barbie_mean = mean(Barbie))
Mov2$Barbie_mean
## [1] 3.4 3.4 3.4 3.4 3.4

Barbie is the only movie that had been watched by everybody in my sample. The na.rm = TRUE command is added in the calculation of the other means in order to deal with the NAs

Mov2 <- Mov2 %>%
 mutate(Captain_mean = mean(Captain, na.rm = TRUE))
 Mov2$Captain_mean
## [1] 3 3 3 3 3
Mov2 <- Mov2 %>%
 mutate(Dune_mean = mean(Dune, na.rm = TRUE))
 Mov2$Dune_mean
## [1] 3 3 3 3 3
Mov2 <- Mov2 %>%
 mutate(Nomad_mean = mean(Nomadland, na.rm = TRUE))
 Mov2$Nomad_mean
## [1] 4 4 4 4 4
Mov2 <- Mov2 %>%
 mutate(Oppen_mean = mean(Oppenheimer, na.rm = TRUE))
 Mov2$Oppen_mean
## [1] 5 5 5 5 5

Although these numbers matched what I had in the Excel table, at this stage I tried to reproduce the averages by calculating the sums and then dividing by the counts. However, I could not count only the numbers. In other words, I did not find a way to skip the NAs and count only the instances of a value. Nevertheless, I was able to proceed with the steps for calculating the MMR. However, this issue of avoiding the NAs came up later.

Getting to MMR

Having calculated the sums of the ratings per movie in order to reproduced the average was needed in any case. They are used to calculate the overall average rating (MMR). However, as I could not get R to count the total observations automatically, I generated a new column called “counted” which I manually set at eleven.

Mov2 <- Mov2 %>%
 mutate(Barbie_sum = sum(Barbie))
  Mov2$Barbie_sum
## [1] 17 17 17 17 17
Mov2 <- Mov2 %>%
 mutate(Captain_sum = sum (Captain, na.rm = TRUE))
  Mov2$Captain_sum
## [1] 6 6 6 6 6
Mov2 <- Mov2 %>%
 mutate(Dune_sum = sum(Dune, na.rm = TRUE))
  Mov2$Dune_sum
## [1] 3 3 3 3 3
Mov2 <- Mov2 %>%
 mutate(Nomad_sum = sum(Nomadland, na.rm = TRUE))
  Mov2$Nomad_sum
## [1] 8 8 8 8 8
Mov2 <- Mov2 %>%
 mutate(Oppen_sum = sum(Oppenheimer, na.rm = TRUE))
  Mov2$Oppen_sum
## [1] 5 5 5 5 5

All these individual movie sums were added and the new variable (Movies_sum) included in the data frame.

Mov2 <- Mov2 %>%
 mutate(Movies_sum = Barbie_sum + Captain_sum + Dune_sum + Nomad_sum + Oppen_sum)
  Mov2$Movies_sum
## [1] 39 39 39 39 39

The MMR, then, was calculated diving Movie_sum by 11 (as explained above).

Mov2 <- Mov2 %>%
mutate(counted = 11)
 Mov2$counted
## [1] 11 11 11 11 11

MMR = Movies_sum/ counted

Mov2 <- Mov2 %>%
mutate(MMR = Movies_sum/ counted)
 Mov2$MMR
## [1] 3.545455 3.545455 3.545455 3.545455 3.545455

Global Baseline Estimates using movie and personal relative rankings

In order to calculate the Global Baseline Estimates,the movie and personal relative rankings are needed. They are just the difference between MMR and the individual movie and personal average rankings. All of them have been labeled by adding “_Relv” to the movie’s (or person’s) name.

Mov2 <- Mov2 %>%
 mutate(Barbie_Relv = Barbie_mean - MMR)  
 Mov2$Barbie_Relv
## [1] -0.1454545 -0.1454545 -0.1454545 -0.1454545 -0.1454545
Mov2 <- Mov2 %>%
 mutate(Captain_Relv = Captain_mean - MMR)  
 Mov2$Captain_Relv
## [1] -0.5454545 -0.5454545 -0.5454545 -0.5454545 -0.5454545
Mov2 <- Mov2 %>%
 mutate(Dune_Relv = Dune_mean - MMR)
 Mov2$Dune_Relv
## [1] -0.5454545 -0.5454545 -0.5454545 -0.5454545 -0.5454545
Mov2 <- Mov2 %>%
 mutate(Nomad_Relv = Nomad_mean - MMR)
 Mov2$Nomad_Relv
## [1] 0.4545455 0.4545455 0.4545455 0.4545455 0.4545455
 Mov2 <- Mov2 %>%
 mutate(Oppen_Relv = Oppen_mean - MMR)
 Mov2$Oppen_Relv
## [1] 1.454545 1.454545 1.454545 1.454545 1.454545
Mov2 <- Mov2 %>%
 mutate(person_Relv = person_mean - MMR)
 Mov2$person_Relv
## [1] -0.04545455 -0.54545455  0.12121212  0.45454545 -0.04545455

Now it is possible to calculate the Global Baseline Estimate, which simply sums the MMR, the movie relative ranking and the personal movie ranking. It is not calculated for Barbie because, as mentioned above, all the persons in my sample had already watched it.

Mov2 <- Mov2 %>%
 mutate(Captain_Recomm = MMR + Captain_Relv + person_Relv)
 Mov2$Captain_Recomm
## [1] 2.954545 2.454545 3.121212 3.454545 2.954545
Mov2 <- Mov2 %>%
 mutate(Dune_Recomm = MMR + Dune_Relv + person_Relv)
 Mov2$Dune_Recomm
## [1] 2.954545 2.454545 3.121212 3.454545 2.954545
Mov2 <- Mov2 %>%
 mutate(Nomad_Recomm = MMR + Nomad_Relv + person_Relv)
 Mov2$Nomad_Recomm
## [1] 3.954545 3.454545 4.121212 4.454545 3.954545
Mov2 <- Mov2 %>%
 mutate(Oppen_Recomm = MMR + Oppen_Relv + person_Relv)
 Mov2$Oppen_Recomm
## [1] 4.954545 4.454545 5.121212 5.454545 4.954545

These recommendation can now be inserted in the original matrix with the actual rankings to find the specific recommendation for each movie for each person. However, as mentioned above, I have not figured out how to deal with NAs, in this case to insert these recommendations without erasing the actual rankings by each person. In order to deal with this issue, I had to generate and use a new data frame.

Joining data frames to fill the recommendation matrix without erasing the original rankings

First, I generated a new Excel file where I filled the empty cells (where the recommendations should go) with zeros. I converted it to a CSV file, placed in GitHub and loaded it. I called this data frame WithoutNAs.

url<- "https://raw.githubusercontent.com/Enrique01234/607-Fall-2026/refs/heads/main/Movies%20to%20Export%20without%20NAs.xlsx%20-%20Sheet3.csv"
WithoutNAs <- read_csv(file = url,
 show_col_types = FALSE,
 progress = FALSE)
WithoutNAs
## # A tibble: 5 × 6
##   Name      BarbienoNAS CaptainnoNAS DunenoNAS NomadlandnoNAS OppenheimernoNAS
##   <chr>           <dbl>        <dbl>     <dbl>          <dbl>            <dbl>
## 1 Brenda              4            0         0              3                0
## 2 Cornelia            1            0         0              5                0
## 3 Alberto             3            0         3              0                5
## 4 Nathaniel           5            3         0              0                0
## 5 Enrique             4            3         0              0                0

Then I joined the two data frames. Better said, I added (left_join) the new data frame into the other one. One the one hand I did not want to lose all the variables I had generated. On the other one, I needed all the information in the new data frame which was already arranged in the same rows as the old one.

Mov2<- Mov2 %>%
 left_join(WithoutNAs)
## Joining with `by = join_by(Name)`

Then I started exploring how to construct a new column that included both the original ranking and the recommendations. This was not a problem, as discussed above, for Barbie. Eventually, for Captain America, I was able to construct the new column (FullCaptain), and include it in the data frame.

Mov2<- Mov2 %>%
 mutate(FullCaptain = if_else(CaptainnoNAS ==0,Captain_Recomm, CaptainnoNAS)
 )
 Mov2$FullCaptain
## [1] 2.954545 2.454545 3.121212 3.000000 3.000000

The round numbers with many zeros after the decimal point are the actual rankings. The other ones are the recommendations.

The same was done for the other three movies.

Mov2<- Mov2 %>%
 mutate(FullDune = if_else(DunenoNAS ==0,Dune_Recomm, DunenoNAS))
  Mov2$FullDune
## [1] 2.954545 2.454545 3.000000 3.454545 2.954545
Mov2<- Mov2 %>%
 mutate(FullNomad = if_else(NomadlandnoNAS ==0,Nomad_Recomm, NomadlandnoNAS))
  Mov2$FullNomad
## [1] 3.000000 5.000000 4.121212 4.454545 3.954545
Mov2<- Mov2 %>%
 mutate(FullOppen = if_else(OppenheimernoNAS ==0,Oppen_Recomm, OppenheimernoNAS))
 Mov2$FullOppen
## [1] 4.954545 4.454545 5.000000 5.454545 4.954545

Now, I had all the information I needed to construct a table with all the recommendations. However, I needed an additional package to construct the table.

Summary table and two final points

I installed an additional package (gt)

install.packages("gt")
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.6'
## (as 'lib' is unspecified)
library(gt)

Then I constructed the summary table. It includes the original rankings (the round numbers) as well as the recommendations.

Mov2 %>% 
    gt() %>% 
     tab_spanner(
         label = "Movies",
         columns = c(Barbie, FullCaptain, FullDune, FullNomad, FullOppen))
Name
Movies
Captain Dune Nomadland Oppenheimer person_mean Barbie_mean Captain_mean Dune_mean Nomad_mean Oppen_mean Barbie_sum Captain_sum Dune_sum Nomad_sum Oppen_sum Movies_sum counted MMR Barbie_Relv Captain_Relv Dune_Relv Nomad_Relv Oppen_Relv person_Relv Captain_Recomm Dune_Recomm Nomad_Recomm Oppen_Recomm BarbienoNAS CaptainnoNAS DunenoNAS NomadlandnoNAS OppenheimernoNAS
Barbie FullCaptain FullDune FullNomad FullOppen
Brenda 4 2.954545 2.954545 3.000000 4.954545 NA NA 3 NA 3.500000 3.4 3 3 4 5 17 6 3 8 5 39 11 3.545455 -0.1454545 -0.5454545 -0.5454545 0.4545455 1.454545 -0.04545455 2.954545 2.954545 3.954545 4.954545 4 0 0 3 0
Cornelia 1 2.454545 2.454545 5.000000 4.454545 NA NA 5 NA 3.000000 3.4 3 3 4 5 17 6 3 8 5 39 11 3.545455 -0.1454545 -0.5454545 -0.5454545 0.4545455 1.454545 -0.54545455 2.454545 2.454545 3.454545 4.454545 1 0 0 5 0
Alberto 3 3.121212 3.000000 4.121212 5.000000 NA 3 NA 5 3.666667 3.4 3 3 4 5 17 6 3 8 5 39 11 3.545455 -0.1454545 -0.5454545 -0.5454545 0.4545455 1.454545 0.12121212 3.121212 3.121212 4.121212 5.121212 3 0 3 0 5
Nathaniel 5 3.000000 3.454545 4.454545 5.454545 3 NA NA NA 4.000000 3.4 3 3 4 5 17 6 3 8 5 39 11 3.545455 -0.1454545 -0.5454545 -0.5454545 0.4545455 1.454545 0.45454545 3.454545 3.454545 4.454545 5.454545 5 3 0 0 0
Enrique 4 3.000000 2.954545 3.954545 4.954545 3 NA NA NA 3.500000 3.4 3 3 4 5 17 6 3 8 5 39 11 3.545455 -0.1454545 -0.5454545 -0.5454545 0.4545455 1.454545 -0.04545455 2.954545 2.954545 3.954545 4.954545 4 3 0 0 0

Two further thoughts. First, I have not figured out yet why additional columns appear in the table. In any case, the first five ones after the reviewers’ names (with the “Movies” header) are the ones that matter.

More importantly, one of the recommendations seems to be wrong. It has a value above 5. It is the one for Nathaniel/Oppenheimer.

It is possible that it is wrong. However, I suspect it is OK. I actually get the same result when I do the exercise in Excel. It may be the case that as both Nathaniel and Oppenheimer have relative rankings exceeding the average, when both of those rankings are added to the MMR, it surpasses 5.

The problem may be in having so few observations. Nathaniel had only watched one movie and Oppenheimer was watched by only one person. If this is the case, it should be easy to fix adding a cap, probably using the else_if command to make the value be 5 if the result of the calculation exceeds five. Something similar could be done if the rating falls below 1.