Contexto

Los sistemas de recomendación son herramientas y algoritmos diseñados para sugerir productos, servicios, información o contenido a los usuarios, basándose en sus preferencias y comportamientos previos, existen dos tipos de sistemas de recomendaicones principales:

  • Sistema Basado en Contenido
    ** Recomienda cosas similares a lo que ya te ha gustado. Por ejemplo, si te gusta una película de acción, te sugiere otras películas del mismo género.
    ** Ejemplo: Netflix te recomienda series y películas basadas en lo que has visto.
  • Sistema Colaborativo
    ** Recomienda basándose en lo que les ha gustado a personas similares a ti, sin importar las características del contenido.
    ** Ejemplo: Facebook sugiere amigos o páginas basadas en tus amigos.

Actividad

Durante esta actividad se realizará un sistema de recomendaciones colaborativo en el que asignaremos ponderación con respecto a las similitudes entre los gustos de las personas para poder recomendar películas con alta probabildiad de que les gusten a los usuarios.

library(tidyverse)   
library(recommenderlab)

Paso 1

1.- Crear la matriz

movie_ratings_tarea <- data.frame(
  KP1 = c(5, 4, 5, 4), 
  ENREDADOS = c(4, 5, 4, NA),
  UP = c(5, 4, 5, 2),
  CARS1 = c(4, 5, 3, NA),
  TS3  = c(3, 5, 3, 2),
  SHREK2 = c(3, 5, 4, 3),
  MONJA = c(3, NA, 3, NA)
)
rownames(movie_ratings_tarea) <- c('Diego','Lalo',  'Ale', 'You' )
movie_ratings_tarea
##       KP1 ENREDADOS UP CARS1 TS3 SHREK2 MONJA
## Diego   5         4  5     4   3      3     3
## Lalo    4         5  4     5   5      5    NA
## Ale     5         4  5     3   3      4     3
## You     4        NA  2    NA   2      3    NA

Paso 2

Proceso de matrices:
Durante esta parte del proceso se crean un vector a cada usuario con respecto a las calificaciones que les asignaron a cada una de las películas, después se grafican los vectores y finalmente se calculan las distancias entre ellos para encontrar la mayor similitud entre usuarios, a menor distancia mayor similitud de gustos.

cosine_similarity <- function(vec_1, vec_2) {
  vec_len <- length(vec_1)
  
  # NA values are replaced with 0
  vec_1[is.na(vec_1)] <- 0
  vec_2[is.na(vec_2)] <- 0
  
  # Computing the denominator
  vec_1_denom <- sqrt(sum(vec_1^2))
  vec_2_denom <- sqrt(sum(vec_2^2))
  denominator <- vec_1_denom * vec_2_denom
  
  # Computing the numerator
  tib = tibble(vec_1 = vec_1, vec_2 = vec_2)
  tib <- tib %>% mutate(products = vec_1 * vec_2)
  numerator <- sum(tib$products)
  
  # Return the cosine similarity
  return (numerator / denominator)
}

Vectorizamos las puntuaciones:

# Obtenemos los vectores de cada persona
You <- as.numeric(as.vector(movie_ratings_tarea['You',]))
Diego <- as.numeric(as.vector(movie_ratings_tarea['Diego',]))
Lalo <- as.numeric(as.vector(movie_ratings_tarea['Lalo',]))
Ale <- as.numeric(as.vector(movie_ratings_tarea['Ale',]))


# Obtenemos "distancia" usando similitud por conseno
similarities_tarea <- data.frame(
  cosine_similarity = c(cosine_similarity(You, Diego), cosine_similarity(You, Lalo), cosine_similarity(You, Ale))
)
rownames(similarities_tarea) <- c('Diego', 'Lalo', 'Ale')
similarities_tarea
##       cosine_similarity
## Diego         0.7503127
## Lalo          0.7424242
## Ale           0.8003335

Paso 3

A continuación se realizan una ponderación tomando en cuenta las similitudes con los demás usuarios con la cual prediciremos que tan probable es que a alguien le guste una película que no ha visto comparando con los demás usuarios (aquellos con mayor similitud tendrán un mayor impacto en la predicción)

# Creamos una función para obtener un promedio ponderado en base a los amigos
movie_rating_weighted_average <- function(movie, friends) {
  denominator <- 0
  numerator <- 0
  for (friend in friends) {
    friend_similarity <- similarities_tarea[friend,][1]
    friend_rating <- movie_ratings_tarea[friend, movie][1]
    
    # Tomaremos el promedio ponderado en cuenta solo para películas con calificación
    if (is.na(friend_rating)) next
    
    denominator <- denominator + friend_similarity
    numerator <- numerator + (friend_similarity * friend_rating)
  }
  
  return (numerator / denominator)
}
#file.choose()

Paso 4

Predicción En este último paso procedemos a predecir la posible puntuación que el usuario podría asignarle a las películas que no ha visto, a mayor puntuación más recomendada es la palícula

friend_names <- c('Diego', 'Lalo', 'Ale')
new_movies <- c('ENREDADOS', 'CARS1', 'MONJA')
new_movie_predicted_ratings <- tibble()
for (n in new_movies) {
  predicted_rating <- movie_rating_weighted_average(n, friend_names)
  prediction_tibble <- tibble(movie = n, predicted_rating = predicted_rating)
  new_movie_predicted_ratings <- bind_rows(new_movie_predicted_ratings, prediction_tibble)
}
new_movie_predicted_ratings
## # A tibble: 3 × 2
##   movie     predicted_rating
##   <chr>                <dbl>
## 1 ENREDADOS             4.32
## 2 CARS1                 3.97
## 3 MONJA                 3

Como podemos ver, la película con mayor rating predicho es ENREDADOS por lo que será la película recomendada en primer lugar

LS0tDQp0aXRsZTogIlJFQ09NRU5EQUNJT05FUy1FUVVJUE8iDQphdXRob3I6ICJGbG9yIE1hcnTDrW5leiAtIEx1aXMgRGF2aWQgQ2FzdGlsbG8iDQpkYXRlOiAiMjAyNC0wNC0xOSINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRoZW1lOiBzaW1wbGV4DQotLS0NCg0KIVtdKEM6XFxVc2Vyc1xcTHVpc0RcXERvY3VtZW50c1xcQ29uY2VudHJhY2nDs25cXE1PRFVMTyA0XFxSRUNPTU1NLmpwZykNCg0KIyBDb250ZXh0bw0KTG9zIHNpc3RlbWFzIGRlIHJlY29tZW5kYWNpw7NuIHNvbiBoZXJyYW1pZW50YXMgeSBhbGdvcml0bW9zIGRpc2XDsWFkb3MgcGFyYSBzdWdlcmlyIHByb2R1Y3Rvcywgc2VydmljaW9zLCBpbmZvcm1hY2nDs24gbyBjb250ZW5pZG8gYSBsb3MgdXN1YXJpb3MsIGJhc8OhbmRvc2UgZW4gc3VzIHByZWZlcmVuY2lhcyB5IGNvbXBvcnRhbWllbnRvcyBwcmV2aW9zLCBleGlzdGVuICBkb3MgdGlwb3MgZGUgc2lzdGVtYXMgZGUgcmVjb21lbmRhaWNvbmVzIHByaW5jaXBhbGVzOiAgDQoNCiFbXShDOlxcVXNlcnNcXEx1aXNEXFxEb2N1bWVudHNcXENvbmNlbnRyYWNpw7NuXFxNT0RVTE8gNFxccmVjb20ucG5nKSAgDQoNCiogU2lzdGVtYSBCYXNhZG8gZW4gQ29udGVuaWRvICANCioqIFJlY29taWVuZGEgY29zYXMgc2ltaWxhcmVzIGEgbG8gcXVlIHlhIHRlIGhhIGd1c3RhZG8uIFBvciBlamVtcGxvLCBzaSB0ZSBndXN0YSB1bmEgcGVsw61jdWxhIGRlIGFjY2nDs24sIHRlIHN1Z2llcmUgb3RyYXMgcGVsw61jdWxhcyBkZWwgbWlzbW8gZ8OpbmVyby4gIA0KKiogRWplbXBsbzogTmV0ZmxpeCB0ZSByZWNvbWllbmRhIHNlcmllcyB5IHBlbMOtY3VsYXMgYmFzYWRhcyBlbiBsbyBxdWUgaGFzIHZpc3RvLiAgDQoqIFNpc3RlbWEgQ29sYWJvcmF0aXZvICANCioqIFJlY29taWVuZGEgYmFzw6FuZG9zZSBlbiBsbyBxdWUgbGVzIGhhIGd1c3RhZG8gYSBwZXJzb25hcyBzaW1pbGFyZXMgYSB0aSwgc2luIGltcG9ydGFyIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGRlbCBjb250ZW5pZG8uICANCioqIEVqZW1wbG86IEZhY2Vib29rIHN1Z2llcmUgYW1pZ29zIG8gcMOhZ2luYXMgYmFzYWRhcyBlbiB0dXMgYW1pZ29zLg0KDQojIEFjdGl2aWRhZA0KRHVyYW50ZSBlc3RhIGFjdGl2aWRhZCBzZSByZWFsaXphcsOhIHVuIHNpc3RlbWEgZGUgcmVjb21lbmRhY2lvbmVzIGNvbGFib3JhdGl2byBlbiBlbCBxdWUgYXNpZ25hcmVtb3MgcG9uZGVyYWNpw7NuIGNvbiByZXNwZWN0byBhIGxhcyBzaW1pbGl0dWRlcyBlbnRyZSBsb3MgZ3VzdG9zIGRlIGxhcyBwZXJzb25hcyBwYXJhIHBvZGVyIHJlY29tZW5kYXIgcGVsw61jdWxhcyBjb24gYWx0YSBwcm9iYWJpbGRpYWQgZGUgcXVlIGxlcyBndXN0ZW4gYSBsb3MgdXN1YXJpb3MuDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpICAgDQpsaWJyYXJ5KHJlY29tbWVuZGVybGFiKQ0KYGBgDQojIyBQYXNvIDENCjEuLSBDcmVhciBsYSBtYXRyaXoNCmBgYHtyfQ0KbW92aWVfcmF0aW5nc190YXJlYSA8LSBkYXRhLmZyYW1lKA0KICBLUDEgPSBjKDUsIDQsIDUsIDQpLCANCiAgRU5SRURBRE9TID0gYyg0LCA1LCA0LCBOQSksDQogIFVQID0gYyg1LCA0LCA1LCAyKSwNCiAgQ0FSUzEgPSBjKDQsIDUsIDMsIE5BKSwNCiAgVFMzICA9IGMoMywgNSwgMywgMiksDQogIFNIUkVLMiA9IGMoMywgNSwgNCwgMyksDQogIE1PTkpBID0gYygzLCBOQSwgMywgTkEpDQopDQpyb3duYW1lcyhtb3ZpZV9yYXRpbmdzX3RhcmVhKSA8LSBjKCdEaWVnbycsJ0xhbG8nLCAgJ0FsZScsICdZb3UnICkNCm1vdmllX3JhdGluZ3NfdGFyZWENCmBgYA0KDQojIyBQYXNvIDINClByb2Nlc28gZGUgbWF0cmljZXM6ICANCkR1cmFudGUgZXN0YSBwYXJ0ZSBkZWwgcHJvY2VzbyBzZSBjcmVhbiB1biB2ZWN0b3IgYSBjYWRhIHVzdWFyaW8gY29uIHJlc3BlY3RvIGEgbGFzIGNhbGlmaWNhY2lvbmVzIHF1ZSBsZXMgYXNpZ25hcm9uIGEgY2FkYSB1bmEgZGUgbGFzIHBlbMOtY3VsYXMsIGRlc3B1w6lzIHNlIGdyYWZpY2FuIGxvcyB2ZWN0b3JlcyB5IGZpbmFsbWVudGUgc2UgY2FsY3VsYW4gbGFzIGRpc3RhbmNpYXMgZW50cmUgZWxsb3MgcGFyYSBlbmNvbnRyYXIgbGEgbWF5b3Igc2ltaWxpdHVkIGVudHJlIHVzdWFyaW9zLCBhIG1lbm9yIGRpc3RhbmNpYSBtYXlvciBzaW1pbGl0dWQgZGUgZ3VzdG9zLg0KYGBge3J9DQpjb3NpbmVfc2ltaWxhcml0eSA8LSBmdW5jdGlvbih2ZWNfMSwgdmVjXzIpIHsNCiAgdmVjX2xlbiA8LSBsZW5ndGgodmVjXzEpDQogIA0KICAjIE5BIHZhbHVlcyBhcmUgcmVwbGFjZWQgd2l0aCAwDQogIHZlY18xW2lzLm5hKHZlY18xKV0gPC0gMA0KICB2ZWNfMltpcy5uYSh2ZWNfMildIDwtIDANCiAgDQogICMgQ29tcHV0aW5nIHRoZSBkZW5vbWluYXRvcg0KICB2ZWNfMV9kZW5vbSA8LSBzcXJ0KHN1bSh2ZWNfMV4yKSkNCiAgdmVjXzJfZGVub20gPC0gc3FydChzdW0odmVjXzJeMikpDQogIGRlbm9taW5hdG9yIDwtIHZlY18xX2Rlbm9tICogdmVjXzJfZGVub20NCiAgDQogICMgQ29tcHV0aW5nIHRoZSBudW1lcmF0b3INCiAgdGliID0gdGliYmxlKHZlY18xID0gdmVjXzEsIHZlY18yID0gdmVjXzIpDQogIHRpYiA8LSB0aWIgJT4lIG11dGF0ZShwcm9kdWN0cyA9IHZlY18xICogdmVjXzIpDQogIG51bWVyYXRvciA8LSBzdW0odGliJHByb2R1Y3RzKQ0KICANCiAgIyBSZXR1cm4gdGhlIGNvc2luZSBzaW1pbGFyaXR5DQogIHJldHVybiAobnVtZXJhdG9yIC8gZGVub21pbmF0b3IpDQp9DQpgYGANCg0KVmVjdG9yaXphbW9zIGxhcyBwdW50dWFjaW9uZXM6DQpgYGB7cn0NCiMgT2J0ZW5lbW9zIGxvcyB2ZWN0b3JlcyBkZSBjYWRhIHBlcnNvbmENCllvdSA8LSBhcy5udW1lcmljKGFzLnZlY3Rvcihtb3ZpZV9yYXRpbmdzX3RhcmVhWydZb3UnLF0pKQ0KRGllZ28gPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IobW92aWVfcmF0aW5nc190YXJlYVsnRGllZ28nLF0pKQ0KTGFsbyA8LSBhcy5udW1lcmljKGFzLnZlY3Rvcihtb3ZpZV9yYXRpbmdzX3RhcmVhWydMYWxvJyxdKSkNCkFsZSA8LSBhcy5udW1lcmljKGFzLnZlY3Rvcihtb3ZpZV9yYXRpbmdzX3RhcmVhWydBbGUnLF0pKQ0KDQoNCiMgT2J0ZW5lbW9zICJkaXN0YW5jaWEiIHVzYW5kbyBzaW1pbGl0dWQgcG9yIGNvbnNlbm8NCnNpbWlsYXJpdGllc190YXJlYSA8LSBkYXRhLmZyYW1lKA0KICBjb3NpbmVfc2ltaWxhcml0eSA9IGMoY29zaW5lX3NpbWlsYXJpdHkoWW91LCBEaWVnbyksIGNvc2luZV9zaW1pbGFyaXR5KFlvdSwgTGFsbyksIGNvc2luZV9zaW1pbGFyaXR5KFlvdSwgQWxlKSkNCikNCnJvd25hbWVzKHNpbWlsYXJpdGllc190YXJlYSkgPC0gYygnRGllZ28nLCAnTGFsbycsICdBbGUnKQ0Kc2ltaWxhcml0aWVzX3RhcmVhDQpgYGANCg0KDQojIyBQYXNvIDMNCkEgY29udGludWFjacOzbiBzZSByZWFsaXphbiB1bmEgcG9uZGVyYWNpw7NuIHRvbWFuZG8gZW4gY3VlbnRhIGxhcyBzaW1pbGl0dWRlcyBjb24gbG9zIGRlbcOhcyB1c3VhcmlvcyBjb24gbGEgY3VhbCBwcmVkaWNpcmVtb3MgcXVlIHRhbiBwcm9iYWJsZSBlcyBxdWUgYSBhbGd1aWVuIGxlIGd1c3RlIHVuYSBwZWzDrWN1bGEgcXVlIG5vIGhhIHZpc3RvIGNvbXBhcmFuZG8gIGNvbiBsb3MgZGVtw6FzIHVzdWFyaW9zIChhcXVlbGxvcyBjb24gbWF5b3Igc2ltaWxpdHVkIHRlbmRyw6FuIHVuIG1heW9yIGltcGFjdG8gZW4gbGEgcHJlZGljY2nDs24pDQpgYGB7cn0NCiMgQ3JlYW1vcyB1bmEgZnVuY2nDs24gcGFyYSBvYnRlbmVyIHVuIHByb21lZGlvIHBvbmRlcmFkbyBlbiBiYXNlIGEgbG9zIGFtaWdvcw0KbW92aWVfcmF0aW5nX3dlaWdodGVkX2F2ZXJhZ2UgPC0gZnVuY3Rpb24obW92aWUsIGZyaWVuZHMpIHsNCiAgZGVub21pbmF0b3IgPC0gMA0KICBudW1lcmF0b3IgPC0gMA0KICBmb3IgKGZyaWVuZCBpbiBmcmllbmRzKSB7DQogICAgZnJpZW5kX3NpbWlsYXJpdHkgPC0gc2ltaWxhcml0aWVzX3RhcmVhW2ZyaWVuZCxdWzFdDQogICAgZnJpZW5kX3JhdGluZyA8LSBtb3ZpZV9yYXRpbmdzX3RhcmVhW2ZyaWVuZCwgbW92aWVdWzFdDQogICAgDQogICAgIyBUb21hcmVtb3MgZWwgcHJvbWVkaW8gcG9uZGVyYWRvIGVuIGN1ZW50YSBzb2xvIHBhcmEgcGVsw61jdWxhcyBjb24gY2FsaWZpY2FjacOzbg0KICAgIGlmIChpcy5uYShmcmllbmRfcmF0aW5nKSkgbmV4dA0KICAgIA0KICAgIGRlbm9taW5hdG9yIDwtIGRlbm9taW5hdG9yICsgZnJpZW5kX3NpbWlsYXJpdHkNCiAgICBudW1lcmF0b3IgPC0gbnVtZXJhdG9yICsgKGZyaWVuZF9zaW1pbGFyaXR5ICogZnJpZW5kX3JhdGluZykNCiAgfQ0KICANCiAgcmV0dXJuIChudW1lcmF0b3IgLyBkZW5vbWluYXRvcikNCn0NCmBgYA0KYGBge3J9DQojZmlsZS5jaG9vc2UoKQ0KYGBgDQoNCiMjIFBhc28gNA0KUHJlZGljY2nDs24NCkVuIGVzdGUgw7psdGltbyBwYXNvIHByb2NlZGVtb3MgYSBwcmVkZWNpciBsYSBwb3NpYmxlIHB1bnR1YWNpw7NuIHF1ZSBlbCB1c3VhcmlvIHBvZHLDrWEgYXNpZ25hcmxlIGEgbGFzIHBlbMOtY3VsYXMgcXVlIG5vIGhhIHZpc3RvLCBhIG1heW9yIHB1bnR1YWNpw7NuIG3DoXMgcmVjb21lbmRhZGEgZXMgbGEgcGFsw61jdWxhDQpgYGB7cn0NCmZyaWVuZF9uYW1lcyA8LSBjKCdEaWVnbycsICdMYWxvJywgJ0FsZScpDQpuZXdfbW92aWVzIDwtIGMoJ0VOUkVEQURPUycsICdDQVJTMScsICdNT05KQScpDQpuZXdfbW92aWVfcHJlZGljdGVkX3JhdGluZ3MgPC0gdGliYmxlKCkNCmZvciAobiBpbiBuZXdfbW92aWVzKSB7DQogIHByZWRpY3RlZF9yYXRpbmcgPC0gbW92aWVfcmF0aW5nX3dlaWdodGVkX2F2ZXJhZ2UobiwgZnJpZW5kX25hbWVzKQ0KICBwcmVkaWN0aW9uX3RpYmJsZSA8LSB0aWJibGUobW92aWUgPSBuLCBwcmVkaWN0ZWRfcmF0aW5nID0gcHJlZGljdGVkX3JhdGluZykNCiAgbmV3X21vdmllX3ByZWRpY3RlZF9yYXRpbmdzIDwtIGJpbmRfcm93cyhuZXdfbW92aWVfcHJlZGljdGVkX3JhdGluZ3MsIHByZWRpY3Rpb25fdGliYmxlKQ0KfQ0KbmV3X21vdmllX3ByZWRpY3RlZF9yYXRpbmdzDQpgYGANCg0KQ29tbyBwb2RlbW9zIHZlciwgbGEgcGVsw61jdWxhIGNvbiBtYXlvciByYXRpbmcgcHJlZGljaG8gZXMgRU5SRURBRE9TIHBvciBsbyBxdWUgc2Vyw6EgbGEgcGVsw61jdWxhIHJlY29tZW5kYWRhIGVuIHByaW1lciBsdWdhcg0K