Creación de un sistema de recomendación simple en Python usando Pandas

¿Alguna vez te has preguntado cómo Netflix te sugiere películas en función de las películas que ya has visto? ¿O cómo un sitio web de comercio electrónico muestra opciones como...

Introducción

¿Alguna vez te has preguntado cómo Netflix te sugiere películas en función de las películas que ya has visto? ¿O cómo un sitio web de comercio electrónico muestra opciones como "Comprados juntos con frecuencia"? Pueden parecer opciones relativamente simples, pero detrás de escena, se ejecuta un algoritmo estadístico complejo para predecir estas recomendaciones. Estos sistemas se denominan sistemas de recomendación, sistemas de recomendación o motores de recomendación. Un Sistema de recomendación es una de las aplicaciones más famosas de la ciencia de datos y el aprendizaje automático.

Un sistema de recomendación emplea un algoritmo estadístico que busca predecir las calificaciones de los usuarios para una entidad en particular, en función de la similitud entre las entidades o la similitud entre los usuarios que calificaron previamente esas entidades. La intuición es que es probable que tipos similares de usuarios tengan calificaciones similares para un conjunto de entidades.

Actualmente, muchas de las grandes empresas tecnológicas utilizan un Sistema de recomendación de una forma u otra. Puede encontrarlos en cualquier lugar, desde Amazon (recomendaciones de productos) hasta YouTube (recomendaciones de videos) y Facebook (recomendaciones de amigos). La capacidad de recomendar productos o servicios relevantes a los usuarios puede ser un gran impulso para una empresa, razón por la cual es tan común encontrar esta técnica empleada en tantos sitios.

En este artículo, veremos cómo podemos construir un sistema de recomendación simple en Python.

Tipos de sistemas de recomendación

Hay dos enfoques principales para crear sistemas de recomendación: filtrado basado en contenido y filtrado colaborativo:

Filtrado basado en contenido

En el filtrado basado en contenido, la similitud entre diferentes productos se calcula sobre la base de los atributos de los productos. Por ejemplo, en un sistema de recomendación de películas basado en el contenido, la similitud entre las películas se calcula en función de los géneros, los actores de la película, el director de la película, etc.

Filtrado colaborativo

El filtrado colaborativo aprovecha el poder de la multitud. La intuición detrás del filtrado colaborativo es que si a un usuario A le gustan los productos X e Y, y si a otro usuario B le gusta el producto X, existe una gran posibilidad de que a él también le guste el producto Y.

Tomemos el ejemplo de un sistema de recomendación de películas. Supongamos que una gran cantidad de usuarios han asignado las mismas calificaciones a las películas X e Y. Llega un nuevo usuario que ha asignado la misma calificación a la película X pero aún no ha visto la película Y. El sistema de filtrado colaborativo le recomendará la película Y.

Implementación del sistema de recomendación de películas en Python

En esta sección, desarrollaremos un sistema de recomendación de películas muy simple en Python que usa la correlación entre las calificaciones asignadas a diferentes películas, para encontrar la similitud entre las películas.

El conjunto de datos que vamos a utilizar para este problema es el conjunto de datos de MovieLens. Para descargar el conjunto de datos, vaya a la página de inicio del conjunto de datos y descargue el archivo "ml-latest-small.zip", que contiene un subconjunto del conjunto de datos de la película real y contiene 100000 calificaciones para 9000 películas de 700 usuarios.

Una vez que descomprima el archivo descargado, verá los archivos "links.csv", "movies.csv", "ratings.csv" y "tags.csv", junto con "README\ " documento. En este artículo, vamos a utilizar los archivos "movies.csv" y "ratings.csv".

Para los scripts de este artículo, la carpeta "ml-latest-small" descomprimida se colocó dentro de la carpeta "Datasets" en la unidad "E".

Visualización y preprocesamiento de datos

El primer paso en cada problema de ciencia de datos es visualizar y preprocesar los datos. Haremos lo mismo, así que primero importemos el archivo "ratings.csv" y veamos qué contiene. Ejecute el siguiente script:

1
2
3
4
5
import numpy as np
import pandas as pd

ratings_data = pd.read_csv("E:\Datasets\ml-latest-small\\ratings.csv")
ratings_data.head()

En el script anterior usamos el método read_csv() de la biblioteca de pandas para leer el archivo "ratings.csv" . A continuación, llamamos al método head() desde el objeto de marco de datos devuelto por la función read_csv(), que mostrará las primeras cinco filas del conjunto de datos.

La salida se ve así:

  userId   movieId   rating   timestamp

0 1 31 2.5 1260759144 1 1 1029 3.0 1260759179 2 1 1061 3.0 1260759182 3 1 1129 2.0 1260759185 4 1 1172 4.0 1260759205

Puede ver en el resultado que el archivo "ratings.csv" contiene los atributos userId, movieId, ratings y timestamp. Cada fila en el conjunto de datos corresponde a una calificación. La columna de ID de usuario contiene la ID del usuario que dejó la calificación. La columna movieId contiene el Id de la película, la columna de calificación contiene la calificación dejada por el usuario. Las calificaciones pueden tener valores entre 1 y 5. Y finalmente, la marca de tiempo se refiere a la hora en que el usuario dejó la calificación.

Hay un problema con este conjunto de datos. Contiene los ID de las películas pero no sus títulos. Necesitaremos nombres de películas para las películas que estamos recomendando. Los nombres de las películas se almacenan en el archivo "movies.csv". Importemos el archivo y veamos los datos que contiene. Ejecute el siguiente script:

1
2
movie_names = pd.read_csv("E:\Datasets\ml-latest-small\\movies.csv")
movie_names.head()

La salida se ve así:

  movieId   title                                genres

0 1 Toy Story (1995) Aventura|Animación|Infantil|Comedia|Fantasía 1 2 Jumanji (1995) Aventura|Infantil|Fantasía 2 3 Viejos gruñones (1995) Comedia|Romance 3 4 Esperando para exhalar (1995) Comedia|Drama|Romance 4 5 Padre de la novia Parte II (1995) Comedia

Como puede ver, este conjunto de datos contiene movieId, el título de la película y su género. Necesitamos un conjunto de datos que contenga el ID de usuario, el título de la película y sus calificaciones. Tenemos esta información en dos objetos de marco de datos diferentes: "ratings_data" y "movie_names". Para obtener la información deseada en un solo marco de datos, podemos fusionar los dos objetos de marcos de datos en la columna movieId, ya que es común entre los dos marcos de datos.

Podemos hacer esto usando la función merge() de la biblioteca de Pandas, como se muestra a continuación:

1
movie_data = pd.merge(ratings_data, movie_names, on='movieId')

Ahora veamos nuestro nuevo marco de datos:

1
movie_data.head()

La salida se ve así:

  userId   movieId   rating   timestamp     title                    genres

0 1 31 2.5 1260759144 Mentes peligrosas (1995) Drama 1 7 31 3.0 851868750 Mentes peligrosas (1995) Drama 2 31 31 4.0 12703541953 Mentes peligrosas (1995) Drama 3 32 31 4.0 834828440 Mentes peligrosas (1995) Drama 4 36 31 3.0 847057202 Mentes peligrosas (1995) Drama

Puede ver que nuestro marco de datos recién creado contiene ID de usuario, título y calificación de la película según sea necesario.

Ahora echemos un vistazo a la calificación promedio de cada película. Para hacerlo, podemos agrupar el conjunto de datos por el título de la película y luego calcular la media de la calificación de cada película. Luego mostraremos las primeras cinco películas junto con su calificación promedio usando el método head(). Mira el siguiente script:

1
movie_data.groupby('title')['rating'].mean().head()

La salida se ve así:

1
2
3
4
5
6
7
title
"Great Performances" Cats (1998)           1.750000
$9.99 (2008)                               3.833333
'Hellboy': The Seeds of Creation (2004)    2.000000
'Neath the Arizona Skies (1934)            0.500000
'Round Midnight (1986)                     2.250000
Name: rating, dtype: float64

Puede ver que las calificaciones promedio no están ordenadas. Ordenemos las calificaciones en orden descendente de sus calificaciones promedio:

1
movie_data.groupby('title')['rating'].mean().sort_values(ascending=False).head()

Si ejecuta el script anterior, la salida se verá así:

1
2
3
4
5
6
7
title
Burn Up! (1991)                                     5.0
Absolute Giganten (1999)                            5.0
Gentlemen of Fortune (Dzhentlmeny udachi) (1972)    5.0
Erik the Viking (1989)                              5.0
Reality (2014)                                      5.0
Name: rating, dtype: float64

Las películas ahora se han clasificado de acuerdo con el orden ascendente de sus calificaciones. Sin embargo, hay un problema. Una película puede llegar a la parte superior de la lista anterior incluso si solo un usuario le ha dado cinco estrellas. Por lo tanto, las estadísticas anteriores pueden ser engañosas. Normalmente, una película que es realmente buena obtiene una calificación más alta por parte de un gran número de usuarios.

Tracemos ahora el número total de calificaciones para una película:

1
movie_data.groupby('title')['rating'].count().sort_values(ascending=False).head()

Ejecutar el script anterior devuelve el siguiente resultado:

1
2
3
4
5
6
7
title
Forrest Gump (1994)                          341
Pulp Fiction (1994)                          324
Shawshank Redemption, The (1994)             311
Silence of the Lambs, The (1991)             304
Star Wars: Episode IV - A New Hope (1977)    291
Name: rating, dtype: int64

Ahora puedes ver algunas películas realmente buenas en la parte superior. La lista anterior respalda nuestro punto de que las buenas películas normalmente reciben calificaciones más altas. Ahora sabemos que tanto la calificación promedio por película como la cantidad de calificaciones por película son atributos importantes. Vamos a crear un nuevo marco de datos que contenga estos dos atributos.

Ejecute el siguiente script para crear el marco de datos ratings_mean_count y primero agregue la calificación promedio de cada película a este marco de datos:

1
ratings_mean_count = pd.DataFrame(movie_data.groupby('title')['rating'].mean())

A continuación, debemos agregar el número de calificaciones de una película al marco de datos ratings_mean_count. Ejecute el siguiente script para hacerlo:

1
ratings_mean_count['rating_counts'] = pd.DataFrame(movie_data.groupby('title')['rating'].count())

Ahora echemos un vistazo a nuestro marco de datos recién creado.

1
ratings_mean_count.head()

La salida se ve así:

calificación del título rating_counts


"Grandes Actuaciones" Gatos (1998) 1.750000 2 $9.99 (2008) 3.833333 3 'Hellboy': Las semillas de la creación (2004) 2.000000 1 'Bajo los cielos de Arizona (1934) 0.500000 1 'Ronda de medianoche (1986) 2.250000 2

Puede ver el título de la película, junto con la calificación promedio y el número de calificaciones de la película.

Tracemos un histograma para el número de calificaciones representadas por la columna "rating_counts" en el marco de datos anterior. Ejecute el siguiente script:

1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('dark')
%matplotlib inline

plt.figure(figsize=(8,6))
plt.rcParams['patch.force_edgecolor'] = True
ratings_mean_count['rating_counts'].hist(bins=50)

Aquí está el resultado del script anterior:

Ratings histogram{.img-responsive}

A partir de la salida, puede ver que la mayoría de las películas han recibido menos de 50 calificaciones. Mientras que la cantidad de películas que tienen más de 100 calificaciones es muy baja.

Ahora trazaremos un histograma para las calificaciones promedio. Aquí está el código para hacerlo:

1
2
3
plt.figure(figsize=(8,6))
plt.rcParams['patch.force_edgecolor'] = True
ratings_mean_count['rating'].hist(bins=50)

La salida se ve así:

Average ratings histogram{.img-responsive}

Puede ver que los valores enteros tienen barras más altas que los valores flotantes, ya que la mayoría de los usuarios asignan la calificación como valor entero, es decir, 1, 2, 3, 4 o 5. Además, es evidente que los datos tienen una distribución normal débil con el media de alrededor de 3,5. Hay algunos valores atípicos en los datos.

Anteriormente, dijimos que las películas con una mayor cantidad de calificaciones generalmente también tienen una calificación promedio alta, ya que una buena película normalmente es muy conocida y una película muy conocida es vista por una gran cantidad de personas y, por lo tanto, generalmente tiene una calificación más alta. clasificación. Veamos si este también es el caso con las películas en nuestro conjunto de datos. Graficaremos las calificaciones promedio contra el número de calificaciones:

1
2
3
plt.figure(figsize=(8,6))
plt.rcParams['patch.force_edgecolor'] = True
sns.jointplot(x='rating', y='rating_counts', data=ratings_mean_count, alpha=0.4)

La salida se ve así:

Calificaciones promedio vs número de calificaciones{.img-responsive}

El gráfico muestra que, en general, las películas con calificaciones promedio más altas en realidad tienen más calificaciones, en comparación con las películas que tienen calificaciones promedio más bajas.

Búsqueda de similitudes entre películas {#findingsimilarities betweenmovies}

Dedicamos bastante tiempo a visualizar y preprocesar nuestros datos. Ahora es el momento de encontrar la similitud entre las películas.

Usaremos la correlación entre las calificaciones de una película como la métrica de similitud. Para encontrar la correlación entre las calificaciones de la película, necesitamos crear una matriz donde cada columna sea el nombre de una película y cada fila contenga la calificación asignada por un usuario específico a esa película. Tenga en cuenta que esta matriz tendrá muchos valores nulos, ya que no todos los usuarios califican cada película.

Para crear la matriz de títulos de películas y las calificaciones de los usuarios correspondientes, ejecute el siguiente script:

1
user_movie_rating = movie_data.pivot_table(index='userId', columns='title', values='rating')
1
user_movie_rating.head()

título "Grandes actuaciones" Cats (1998) $9.99 (1998) 'Hellboy': Las semillas de la creación (2008) 'Neath the Arizona Skies (1934) 'Round Midnight (1986) 'Salem' s Lot (2004) 'Til There Was You (1997) 'burbs, The (1989) 'night Mother (1986) (500) Days of Summer (2009) ... Zulu (1964) Zulu ( 2013)


ID de usuario 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN 3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN 4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN 5 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN

Sabemos que cada columna contiene todas las calificaciones de los usuarios para una película en particular. Busquemos todas las calificaciones de los usuarios para la película "Forrest Gump (1994)" y busquemos las películas similares. Elegimos esta película porque tiene la mayor cantidad de calificaciones y queremos encontrar la correlación entre las películas que tienen una mayor cantidad de calificaciones.

Para encontrar las calificaciones de los usuarios para "Forrest Gump (1994)", ejecute el siguiente script:

1
forrest_gump_ratings = user_movie_rating['Forrest Gump (1994)']

El script anterior devolverá una serie de Pandas. Vamos a ver cómo se ve.

1
forrest_gump_ratings.head()
1
2
3
4
5
6
7
userId
1    NaN
2    3.0
3    5.0
4    5.0
5    4.0
Name: Forrest Gump (1994), dtype: float64

Ahora recuperemos todas las películas similares a "Forrest Gump (1994)". Podemos encontrar la correlación entre las valoraciones de los usuarios de "Forest Gump (1994)" y todas las demás películas usando la función corrwith() como se muestra a continuación:

1
2
3
4
5
movies_like_forest_gump = user_movie_rating.corrwith(forrest_gump_ratings)

corr_forrest_gump = pd.DataFrame(movies_like_forest_gump, columns=['Correlation'])
corr_forrest_gump.dropna(inplace=True)
corr_forrest_gump.head()

En el script anterior, primero recuperamos la lista de todas las películas relacionadas con "Forrest Gump (1994)" junto con su valor de correlación, utilizando la función corrwith(). A continuación, creamos un marco de datos que contiene el título de la película y las columnas de correlación. Luego eliminamos todos los valores NA del marco de datos y mostramos sus primeras 5 filas usando la función head.

La salida se ve así:

título Correlación


$9.99 (2008) 1.000000 'burbs, El (1989) 0.044946 (500) Días de verano (2009) 0,624458 *pilas no incluidas (1987) 0,603023 ...Y Justicia para Todos (1979) 0.173422

Ordenemos las películas en orden descendente de correlación para ver las películas altamente correlacionadas en la parte superior. Ejecute el siguiente script:

1
corr_forrest_gump.sort_values('Correlation', ascending=False).head(10)

Aquí está el resultado del script anterior:

título Correlación


$9.99 (2008) 1.0 Di que no es así (2001) 1.0 Metrópolis (2001) 1.0 No veas el mal, no escuches el mal (1989) 1.0 Hombres medianos (2009) 1.0 Agua para elefantes (2011) 1.0 Reloj, El (2012) 1.0 La próxima película de Cheech y Chong (1980) 1.0 Forrest Gump (1994) 1.0 Guerrero (2011) 1.0

De la salida se puede ver que las películas que tienen una alta correlación con "Forrest Gump (1994)" no son muy conocidas. Esto muestra que la correlación por sí sola no es una buena métrica para la similitud porque puede haber un usuario que vio '"Forest Gump (1994)" y solo otra película y calificó ambas como 5.

Una solución a este problema es recuperar solo aquellas películas correlacionadas que tengan al menos más de 50 calificaciones. Para hacerlo, agregará la columna rating_counts del marco de datos rating_mean_count a nuestro marco de datos corr_forrest_gump. Ejecute el siguiente script para hacerlo:

1
2
corr_forrest_gump = corr_forrest_gump.join(ratings_mean_count['rating_counts'])
corr_forrest_gump.head()

La salida se ve así:

título Correlación rating_counts


$9.99 (2008) 1.000000 3 'burbs, El (1989) 0.044946 19 (500) Días de verano (2009) 0,624458 45 *pilas no incluidas (1987) 0,603023 7 ...Y Justicia para Todos (1979) 0.173422 13

Puede ver que la película "$9.99", que tiene la correlación más alta, solo tiene tres calificaciones. Esto significa que solo tres usuarios dieron las mismas calificaciones a "Forest Gump (1994)", "$9.99". Sin embargo, podemos deducir que una película no puede ser declarada similar a otra película en base a solo 3 calificaciones. Es por eso que agregamos la columna "rating_counts". Filtremos ahora las películas relacionadas con "Forest Gump (1994)", que tienen más de 50 calificaciones. El siguiente código hará eso:

1
corr_forrest_gump[corr_forrest_gump ['rating_counts']>50].sort_values('Correlation', ascending=False).head()

La salida del script, se parece a esto:

título Correlación rating_counts


Forrest Gump (1994) 1.000000 341 Mi gran boda griega (2002) 0.626240 51 Mente maravillosa, A (2001) 0,575922 114 Pocos hombres buenos, A (1992) 0,555206 76 El bebé del millón de dólares (2004) 0,545638 65

Ahora puede ver en la salida las películas que están altamente correlacionadas con "Forrest Gump (1994)". Las películas de la lista son algunas de las películas más famosas de Hollywood, y dado que "Forest Gump (1994)" también es una película muy famosa, existe una alta probabilidad de que estas películas estén relacionadas.

Conclusión

En este artículo, estudiamos qué es un sistema de recomendación y cómo podemos crearlo en Python usando solo la biblioteca de Pandas. Es importante mencionar que el sistema de recomendación que creamos es muy simple. Los sistemas de recomendación de la vida real utilizan algoritmos muy complejos y se analizarán en un artículo posterior.

Si desea obtener más información sobre los sistemas de recomendación, le sugiero que consulte los libros [Sistemas prácticos de recomendación] (https://stackabu.se/practical-recommender-systems-book) y [Sistemas de recomendaciones: el libro de texto] (https://stackabu.se/recommender-systems-the-texbook). Profundizan mucho más en este tema y cubren métodos más complejos y precisos que los que hicimos en este artículo.