Tema 8: Modelos de recomendación Películas

El dataframe generado contiene información acerca de las preferencias y recomendaciones de películas de alumnos del ITESM Campus MTY, donde 1 indica que la película es poco recomendable y 5 significa que es altamente recomendable. Con el propósito de ejemplificar el funcionamiento de los modelos de recomendación, algunos datos aparecerán vacíos. Esto simula que, en ocasiones, no todos los usuarios califican todos los ítems.

library(tidyverse) 
## Warning: package 'tidyverse' was built under R version 4.3.2
## Warning: package 'ggplot2' was built under R version 4.3.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.2     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.4.4     ✔ tibble    3.2.1
## ✔ lubridate 1.9.2     ✔ tidyr     1.3.0
## ✔ purrr     1.0.1     
## ── 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(recommenderlab)
## Warning: package 'recommenderlab' was built under R version 4.3.2
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## 
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
## 
## Loading required package: arules
## Warning: package 'arules' was built under R version 4.3.2
## 
## Attaching package: 'arules'
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## The following objects are masked from 'package:base':
## 
##     abbreviate, write
## 
## Loading required package: proxy
## 
## Attaching package: 'proxy'
## 
## The following object is masked from 'package:Matrix':
## 
##     as.matrix
## 
## The following objects are masked from 'package:stats':
## 
##     as.dist, dist
## 
## The following object is masked from 'package:base':
## 
##     as.matrix
## 
## Registered S3 methods overwritten by 'registry':
##   method               from 
##   print.registry_field proxy
##   print.registry_entry proxy
library(proxy)

I.- Dataframe

MR <- data.frame(
  Spiderman = c(5, NA, 3, 4), 
  Juegos_del_Hambre = c(NA, 4, 1, 2),
  Sonido_de_Libertad = c(3, NA, NA, 3),
  Coco = c(2, 5, 4, 5),
  Take_The_Ball_Pass_The_Ball = c(1, NA, 5, NA),
  Inception = c(4, 2, NA, NA),
  Interestelar = c(3, 5, 3, NA)
)
rownames(MR) <- c('Daniel', 'Luis', 'Gera', 'Cris')
View(MR)

II.- Modelos de recomendación

cosine_similarity <- function(vec_1, vec_2) {
  vec_len <- length(vec_1)
  
# Reemplazar NA con 0
  vec_1[is.na(vec_1)] <- 0
  vec_2[is.na(vec_2)] <- 0
  
# Denominador
  vec_1_denom <- sqrt(sum(vec_1^2))
  vec_2_denom <- sqrt(sum(vec_2^2))
  denominator <- vec_1_denom * vec_2_denom
  
# Numerador
  tib = tibble(vec_1 = vec_1, vec_2 = vec_2)
  tib <- tib %>% mutate(products = vec_1 * vec_2)
  numerator <- sum(tib$products)
  
# Coseno de similaridad
  return (numerator / denominator)
}
#Similiaridad entre amigos

Daniel <- as.numeric(as.vector(MR['Daniel',]))
Luis <- as.numeric(as.vector(MR['Luis',]))
Gera <- as.numeric(as.vector(MR['Gera',]))
Cris <- as.numeric(as.vector(MR['Cris',]))


MR_Similaridad <- data.frame(
  cosine_similarity = c(cosine_similarity(Daniel, Luis), cosine_similarity(Daniel, Gera), cosine_similarity(Daniel, Cris))
)
rownames(MR_Similaridad) <- c('Luis', 'Gera', 'Cris')
MR_Similaridad
##      cosine_similarity
## Luis         0.4930318
## Gera         0.5970849
## Cris         0.6634035
#Utilizamos el coseno de simalirad, para realizar una de Prediccion Pelis Nuevas

MR_prom <- function(movie, friends) {
  denominator <- 0
  numerator <- 0
  
  for (friend in friends) {
    Mr_sim <- MR_Similaridad[friend, 'cosine_similarity']
    Mr_calif <- MR[friend, movie]
    
    if (!is.na(Mr_calif)) {
      denominator <- denominator + Mr_sim
      numerator <- numerator + (Mr_sim * Mr_calif)
    }
  }
  
  if (denominator == 0) {
    return(NA)  
  }
  
  return(numerator / denominator)
}

friend_names <- c('Luis', 'Gera', 'Cris')
MR_Nuevo <- c('Spiderman', 'Juegos_del_Hambre', 'Sonido_de_Libertad') 

MR_Pred_Nuevo <- tibble(movie = MR_Nuevo, Pred_calif = sapply(MR_Nuevo, function(n) MR_prom(n, friend_names)))

MR_Pred_Nuevo
## # A tibble: 3 × 2
##   movie              Pred_calif
##   <chr>                   <dbl>
## 1 Spiderman                3.53
## 2 Juegos_del_Hambre        2.22
## 3 Sonido_de_Libertad       3
# convert the movie ratings data frame to a matrix
rmat <- as.matrix(MR)

# convert matrix to a recommenderlab realRatingMatrix
rmat <- as(rmat, "realRatingMatrix")

MR_Normalizado <- Recommender(rmat, "UBCF", 
      param=list(normalize = "center", method="Cosine"))

MR_Normalizado_Pred <- predict(MR_Normalizado, rmat, type="ratings")

MR
##        Spiderman Juegos_del_Hambre Sonido_de_Libertad Coco
## Daniel         5                NA                  3    2
## Luis          NA                 4                 NA    5
## Gera           3                 1                 NA    4
## Cris           4                 2                  3    5
##        Take_The_Ball_Pass_The_Ball Inception Interestelar
## Daniel                           1         4            3
## Luis                            NA         2            5
## Gera                             5        NA            3
## Cris                            NA        NA           NA
MR_Normalizado_Pred@data
## 4 x 7 sparse Matrix of class "dgCMatrix"
##        Spiderman Juegos_del_Hambre Sonido_de_Libertad Coco
## Daniel  .                  1.55337           .           .
## Luis    4.293013           .                 3.536385    .
## Gera    .                  .                 2.747675    .
## Cris    .                  .                 .           .
##        Take_The_Ball_Pass_The_Ball Inception Interestelar
## Daniel                    .         .            .       
## Luis                      5.412718  .            .       
## Gera                      .         1.627089     .       
## Cris                      4.093292  2.509024     3.801305

III. Conclusiones

Dada la naturaleza de la base de datos generada y los posibles desafíos de datos dispersos y valores faltantes, se utilizó un modelo basado en la similitud coseno con valores normalizados. De esta manera, se obtendrá un mejor análisis, ya que el conjunto de datos no es muy grande; un modelo más simple será más fácil de entender e implementar. Asimismo, debido a que la normalización de valores y el uso de la similitud coseno pueden ser útiles cuando los datos son dispersos y hay valores faltantes.