Filtro basado en contenido
Los sistemas recomendadores son colecciones de algoritmos utilizados para sugerir elementos a los usuarios basados en información del usuario. Estos sistemas suelen verse en tiendas online, base datos de películas y buscadores de trabajos. Vamos a explorar los sistemas Content-based de recomendadores basados en contenidos e implementar una simple versión de uno utilizando Python con las librerías Pandas.
Primero, importemos las librerías necesarias:
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Librería de manipulación del marco de datos
import pandas as pd
#Funciones matemáticas, necesitaremos la función sqrt para importar sólo lo necesario
from math import sqrt
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
[/sourcecode]
Obteniendo los Datos
Para obtener y extraer los datos, solo corre los siguientes scripts Bash:
Descargamos el set de datos utilizando !wget.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
!wget -O moviedataset.zip https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/moviedataset.zip
print(‘descomprimiendo …’)
!unzip -o -j moviedataset.zip
[/sourcecode]
--2020-03-02 14:22:28-- https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/moviedataset.zip Resolving s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)... 67.228.254.196 Connecting to s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)|67.228.254.196|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 160301210 (153M) [application/zip] Saving to: ‘moviedataset.zip’ moviedataset.zip 100%[===================>] 152.88M 19.3MB/s in 8.8s 2020-03-02 14:22:37 (17.4 MB/s) - ‘moviedataset.zip’ saved [160301210/160301210] descomprimiendo ... Archive: moviedataset.zip inflating: links.csv inflating: movies.csv inflating: ratings.csv
Ahora carguemos cada archivo en su propio marco de datos:
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Guardando la información de las películas dentro del marco de datos pandas
movies_df = pd.read_csv(‘movies.csv’)
#Guardando la información del usuario dentro del marco de datos pandas
ratings_df = pd.read_csv(‘ratings.csv’)
#Head es una función que obtiene los primeros N registros de un marco de datos. El valor por omision de N es 5.
movies_df.head()
[/sourcecode]
movieId | title | genres | |
---|---|---|---|
0 | 1 | Toy Story (1995) | Adventure|Animation|Children|Comedy|Fantasy |
1 | 2 | Jumanji (1995) | Adventure|Children|Fantasy |
2 | 3 | Grumpier Old Men (1995) | Comedy|Romance |
3 | 4 | Waiting to Exhale (1995) | Comedy|Drama|Romance |
4 | 5 | Father of the Bride Part II (1995) | Comedy |
Ahora saquemos el año de la columna title utilizando la función replace y la guardamos en una nueva columna llamada year.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Utilizando expresiones regulares para encontrar un año guardado entre paréntesis
#Especificamos los paréntesis para no tener conflicto con las películas que tienen años como parte de su título
movies_df[‘year’] = movies_df.title.str.extract(‘(
#Eliminando los paréntesis
movies_df[‘year’] = movies_df.year.str.extract(‘(\d\d\d\d)’,expand=False)
#Eliminando los años de la columna ‘title’
movies_df[‘title’] = movies_df.title.str.replace(‘(
#Aplicando la función strip para eliminar los caracteres blancos finales
movies_df[‘title’] = movies_df[‘title’].apply(lambda x: x.strip())
movies_df.head()
[/sourcecode]
movieId | title | genres | year | |
---|---|---|---|---|
0 | 1 | Toy Story | Adventure|Animation|Children|Comedy|Fantasy | 1995 |
1 | 2 | Jumanji | Adventure|Children|Fantasy | 1995 |
2 | 3 | Grumpier Old Men | Comedy|Romance | 1995 |
3 | 4 | Waiting to Exhale | Comedy|Drama|Romance | 1995 |
4 | 5 | Father of the Bride Part II | Comedy | 1995 |
Con ello, separemos los valores de la columna Genres y pongámoslo todos en list of Genres para simplificar una utilización que haremos después. Esto también se puede lograr la función split string de Python dentro de la columna que corresponde.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Cada género está separado por un | para simplificar la llamada que se haga solo a |
movies_df[‘genres’] = movies_df.genres.str.split(‘|’)
movies_df.head()
[/sourcecode]
movieId | title | genres | year | |
---|---|---|---|---|
0 | 1 | Toy Story | [Adventure, Animation, Children, Comedy, Fantasy] | 1995 |
1 | 2 | Jumanji | [Adventure, Children, Fantasy] | 1995 |
2 | 3 | Grumpier Old Men | [Comedy, Romance] | 1995 |
3 | 4 | Waiting to Exhale | [Comedy, Drama, Romance] | 1995 |
4 | 5 | Father of the Bride Part II | [Comedy] | 1995 |
Ya que poner los géneros en una lista no es lo más óptimo para la técnica de sistemas recomendadores basados en contenidos, usaremos la técnica One Hot Encoding para convertir la lista de géneros en un vector donde cada colunna corresponde a un valor de la característica mencionada. Esta codificación se necesita para alimentar datos categóricos. En este caso, guardamos cada género distinto en colunnas que tienen 1 ó 0. 1 quiere decir que una película tiene género y 0 que no lo tiene. Guardames también un marco de datos en otra variable ya que los géneros no serán importante en esta primera instancia de sistemas recomendadores.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Copiando el marco de datos de la pelicula en uno nuevo ya que no necesitamos la información del género por ahora.
moviesWithGenres_df = movies_df.copy()
#Para cada fila del marco de datos, iterar la lista de géneros y colocar un 1 en la columna que corresponda
for index, row in movies_df.iterrows():
for genre in row[‘genres’]:
moviesWithGenres_df.at[index, genre] = 1
#Completar los valores NaN con 0 para mostrar que una película no tiene el género de la columna
moviesWithGenres_df = moviesWithGenres_df.fillna(0)
moviesWithGenres_df.head()
[/sourcecode]
movieId | title | genres | year | Adventure | Animation | Children | Comedy | Fantasy | Romance | … | Horror | Mystery | Sci-Fi | IMAX | Documentary | War | Musical | Western | Film-Noir | (no genres listed) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | Toy Story | [Adventure, Animation, Children, Comedy, Fantasy] | 1995 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1 | 2 | Jumanji | [Adventure, Children, Fantasy] | 1995 | 1.0 | 0.0 | 1.0 | 0.0 | 1.0 | 0.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
2 | 3 | Grumpier Old Men | [Comedy, Romance] | 1995 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 1.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3 | 4 | Waiting to Exhale | [Comedy, Drama, Romance] | 1995 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 1.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
4 | 5 | Father of the Bride Part II | [Comedy] | 1995 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
A continuación, miremos los ratings de los marcos de datos.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
ratings_df.head()
[/sourcecode]
userId | movieId | rating | timestamp | |
---|---|---|---|---|
0 | 1 | 169 | 2.5 | 1204927694 |
1 | 1 | 2471 | 3.0 | 1204927438 |
2 | 1 | 48516 | 5.0 | 1204927435 |
3 | 2 | 2571 | 3.5 | 1436165433 |
4 | 2 | 109487 | 4.0 | 1436165496 |
Cada fila dentro de los ratings tienen un user id asociado con al menos una película, un rating y una marca de tiempo que muestra cuándo se revisó la película. No se necesitará la columna de la marca de tiempo, por lo que la podemos eliminar para ahorrar espacio de memoria.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#La sentencia Drop elimina la fila o columna señalada del marco de datos
ratings_df = ratings_df.drop(‘timestamp’, 1)
ratings_df.head()
[/sourcecode]
userId | movieId | rating | |
---|---|---|---|
0 | 1 | 169 | 2.5 |
1 | 1 | 2471 | 3.0 |
2 | 1 | 48516 | 5.0 |
3 | 2 | 2571 | 3.5 |
4 | 2 | 109487 | 4.0 |
Sistema de recomendación Basado en Contenido
Ahora miremos cómo implementar Content-Based o Item-Item recommendation systems. Esta técnica intenta descubrir los aspectos favoritos del usuario para algún elemento, y luego recomienda elementos que presentan esos aspectos. En nuestro caso, vamos a intentar descubrir los géneros favoritos a partir de las películas y los ratings.
Comencemos por crear una entrada para el usuario para que recomiende películas
Nota: Para agregar más películas, aumenta la cantidad de elementos en userInput. Agrega tantos como desees! Solo asegúrate de escribir en letras mayúsculas y si una película comienza con un «The», como «The Matrix» entonces escríbelo así: ‘Matrix, The’ .
[sourcecode language=»python» wraplines=»false» collapse=»false»]
userInput = [
{‘title’:’Breakfast Club, The’, ‘rating’:5},
{‘title’:’Toy Story’, ‘rating’:3.5},
{‘title’:’Jumanji’, ‘rating’:2},
{‘title’:"Pulp Fiction", ‘rating’:5},
{‘title’:’Akira’, ‘rating’:4.5}
]
inputMovies = pd.DataFrame(userInput)
inputMovies
[/sourcecode]
title | rating | |
---|---|---|
0 | Breakfast Club, The | 5.0 |
1 | Toy Story | 3.5 |
2 | Jumanji | 2.0 |
3 | Pulp Fiction | 5.0 |
4 | Akira | 4.5 |
Agregar movieId al ingreso del usuario
Con las datos ingresados completos, extraigamos los ID de las películas del dataframe de películas y agreguémosla.
Esto se logra primero sacando las filas que tienen que tienen títulos de películas y luego une este subconjunto con el dataframe de entrada. También sacamos columnas que no se necesitan para ahorrar espacio de memoria.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Filtrar las películas por título
inputId = movies_df[movies_df[‘title’].isin(inputMovies[‘title’].tolist())]
#Luego juntarlas para obtener el movieId. Implícitamente, lo está uniendo por título.
inputMovies = pd.merge(inputId, inputMovies)
#Eliminando información que no utilizaremos del dataframe de entrada
inputMovies = inputMovies.drop(‘genres’, 1).drop(‘year’, 1)
#Dataframe de entrada final
#Si una película que se agregó no se encuentra, entonces podría no estar en el dataframe
#original o podría estar escrito de otra forma, por favor revisar mayúscula o minúscula.
inputMovies
[/sourcecode]
movieId | title | rating | |
---|---|---|---|
0 | 1 | Toy Story | 3.5 |
1 | 2 | Jumanji | 2.0 |
2 | 296 | Pulp Fiction | 5.0 |
3 | 1274 | Akira | 4.5 |
4 | 1968 | Breakfast Club, The | 5.0 |
Ahora vamos a comenzar aprendiendo las preferencias del ingreso de datos, por lo que obtendremos el subconjunto de películas que se vieron a partir del marco de datos que contienen los géneros definidos con valores binarios.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Descartando las películas de la entrada de datos
userMovies = moviesWithGenres_df[moviesWithGenres_df[‘movieId’].isin(inputMovies[‘movieId’].tolist())]
userMovies
[/sourcecode]
movieId | title | genres | year | Adventure | Animation | Children | Comedy | Fantasy | Romance | … | Horror | Mystery | Sci-Fi | IMAX | Documentary | War | Musical | Western | Film-Noir | (no genres listed) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | Toy Story | [Adventure, Animation, Children, Comedy, Fantasy] | 1995 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1 | 2 | Jumanji | [Adventure, Children, Fantasy] | 1995 | 1.0 | 0.0 | 1.0 | 0.0 | 1.0 | 0.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
293 | 296 | Pulp Fiction | [Comedy, Crime, Drama, Thriller] | 1994 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1246 | 1274 | Akira | [Action, Adventure, Animation, Sci-Fi] | 1988 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1885 | 1968 | Breakfast Club, The | [Comedy, Drama] | 1985 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Necesitaremos solamente la tabla actual de géneros, por lo que ordenaremos un poco inicializando el índice y eliminando las columnas movieId, title, genres e year.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Inicializando el índice para evitar problemas a futuro
userMovies = userMovies.reset_index(drop=True)
#Eliminando problemas innecesarios para ahorrar memoria y evitar conflictos
userGenreTable = userMovies.drop(‘movieId’, 1).drop(‘title’, 1).drop(‘genres’, 1).drop(‘year’, 1)
userGenreTable
[/sourcecode]
Adventure | Animation | Children | Comedy | Fantasy | Romance | Drama | Action | Crime | Thriller | Horror | Mystery | Sci-Fi | IMAX | Documentary | War | Musical | Western | Film-Noir | (no genres listed) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1 | 1.0 | 0.0 | 1.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
2 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 1.0 | 0.0 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
4 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Ahora si estamos listos para comenzar a aprender las preferencias recibidas!
Para lograrlo, ponderaremos cada género. Esto se puede lograr utilizando las revisiones y multiplicádolas dentro de la tabla de ingreso de género para luego juntar la tabla resultante por columna. Esta operación en realidad es un producto escalar entre una matriz y un vector. Esto se logra invocando la función de Panda llamada «dot».
[sourcecode language=»python» wraplines=»false» collapse=»false»]
inputMovies[‘rating’]
[/sourcecode]
0 3.5 1 2.0 2 5.0 3 4.5 4 5.0 Name: rating, dtype: float64
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Producto escalar para obtener los pesos
userProfile = userGenreTable.transpose().dot(inputMovies[‘rating’])
#Perfil del usuario
userProfile
[/sourcecode]
Adventure 10.0 Animation 8.0 Children 5.5 Comedy 13.5 Fantasy 5.5 Romance 0.0 Drama 10.0 Action 4.5 Crime 5.0 Thriller 5.0 Horror 0.0 Mystery 0.0 Sci-Fi 4.5 IMAX 0.0 Documentary 0.0 War 0.0 Musical 0.0 Western 0.0 Film-Noir 0.0 (no genres listed) 0.0 dtype: float64
Ahora, tenemos los pesos para cada preferencia del usuario. Esto se conoce como Perfil del Usuario. Utilizando esto, podemos sugerir películas que satisfagan las preferencias del usuario.
Comencemos extrayendo la tabla de géner del marco de datos original:
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Ahora llevemos los géneros de cada película al marco de datos original
genreTable = moviesWithGenres_df.set_index(moviesWithGenres_df[‘movieId’])
#Y eliminemos información innecesaria
genreTable = genreTable.drop(‘movieId’, 1).drop(‘title’, 1).drop(‘genres’, 1).drop(‘year’, 1)
genreTable.head()
[/sourcecode]
Adventure | Animation | Children | Comedy | Fantasy | Romance | Drama | Action | Crime | Thriller | Horror | Mystery | Sci-Fi | IMAX | Documentary | War | Musical | Western | Film-Noir | (no genres listed) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
movieId | ||||||||||||||||||||
1 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
2 | 1.0 | 0.0 | 1.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
4 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
5 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
[sourcecode language=»python» wraplines=»false» collapse=»false»]
genreTable.shape
[/sourcecode]
(34208, 20)
Habiendo completado el perfil de entrada y la lista completa de películas con sus respectivos géneros, llevaremos el peso promedio de cada película basado en el perfil de ingreso para luego recomendar las primeras veinte películas que más se adecuan a tal perfil.
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Multiplicando los géneros por los pesos para luego calcular el peso promedio
recommendationTable_df = ((genreTable*userProfile).sum(axis=1))/(userProfile.sum())
recommendationTable_df.head()
[/sourcecode]
movieId 1 0.594406 2 0.293706 3 0.188811 4 0.328671 5 0.188811 dtype: float64
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Ordena nuestra recomendación en orden descendente
recommendationTable_df = recommendationTable_df.sort_values(ascending=False)
#Miremos los valores
recommendationTable_df.head()
[/sourcecode]
movieId 5018 0.748252 26093 0.734266 27344 0.720280 148775 0.685315 6902 0.678322 dtype: float64
Ahora vemos la tabla de recomendación!
[sourcecode language=»python» wraplines=»false» collapse=»false»]
#Tabla de recomendaciones final
movies_df.loc[movies_df[‘movieId’].isin(recommendationTable_df.head(20).keys())]
[/sourcecode]
movieId | title | genres | year | |
---|---|---|---|---|
664 | 673 | Space Jam | [Adventure, Animation, Children, Comedy, Fanta… | 1996 |
1824 | 1907 | Mulan | [Adventure, Animation, Children, Comedy, Drama… | 1998 |
2902 | 2987 | Who Framed Roger Rabbit? | [Adventure, Animation, Children, Comedy, Crime… | 1988 |
4923 | 5018 | Motorama | [Adventure, Comedy, Crime, Drama, Fantasy, Mys… | 1991 |
6793 | 6902 | Interstate 60 | [Adventure, Comedy, Drama, Fantasy, Mystery, S… | 2002 |
8605 | 26093 | Wonderful World of the Brothers Grimm, The | [Adventure, Animation, Children, Comedy, Drama… | 1962 |
8783 | 26340 | Twelve Tasks of Asterix, The (Les douze travau… | [Action, Adventure, Animation, Children, Comed… | 1976 |
9296 | 27344 | Revolutionary Girl Utena: Adolescence of Utena… | [Action, Adventure, Animation, Comedy, Drama, … | 1999 |
9825 | 32031 | Robots | [Adventure, Animation, Children, Comedy, Fanta… | 2005 |
11716 | 51632 | Atlantis: Milo’s Return | [Action, Adventure, Animation, Children, Comed… | 2003 |
11751 | 51939 | TMNT (Teenage Mutant Ninja Turtles) | [Action, Adventure, Animation, Children, Comed… | 2007 |
13250 | 64645 | The Wrecking Crew | [Action, Adventure, Comedy, Crime, Drama, Thri… | 1968 |
16055 | 81132 | Rubber | [Action, Adventure, Comedy, Crime, Drama, Film… | 2010 |
18312 | 91335 | Gruffalo, The | [Adventure, Animation, Children, Comedy, Drama] | 2009 |
22778 | 108540 | Ernest & Célestine (Ernest et Célestine) | [Adventure, Animation, Children, Comedy, Drama… | 2012 |
22881 | 108932 | The Lego Movie | [Action, Adventure, Animation, Children, Comed… | 2014 |
25218 | 117646 | Dragonheart 2: A New Beginning | [Action, Adventure, Comedy, Drama, Fantasy, Th… | 2000 |
26442 | 122787 | The 39 Steps | [Action, Adventure, Comedy, Crime, Drama, Thri… | 1959 |
32854 | 146305 | Princes and Princesses | [Animation, Children, Comedy, Drama, Fantasy, … | 2000 |
33509 | 148775 | Wizards of Waverly Place: The Movie | [Adventure, Children, Comedy, Drama, Fantasy, … | 2009 |
Ventajas y Desventajas del Filtrado Basado en Contenido
- Ventajas:
- Se aprende de las preferencias del usuario
- Es muy personalizado para el usuario
- Desventajas:
- No se tiene en cuenta lo que otros piensan del item, por lo que podemos encontrarnos con items de baja calidad
- La extracción de datos no siempre es intuitiva
- Determinar que características de las observaciones le gustan o no al usuario no sienpre es obvio