Loading...

Loading...

Song recommendation system with Python

Song recommendation system with Python

Un recomendador de canciones por metodología de filtrado colaborativo usuario-usuario e ítem-ítem.

Proyecto simple de machine learning con Python

Ing. Leonardo López

Introducción:

En este proyecto para diseñar el recomendador busco calcular las similitudes entre las personas que son usuarios de nuestro sistema, por lo que calculo las distancias vectoriales que las separan, y para ello me baso primero trazar la Matriz de Utilidad.

In [4]:

#Importamos las librerías principales
import math
from scipy.spatial.distance import euclidean
from pandas import read_csv
import seaborn as sns
import pandas as pd

In [5]:

#Importamos los datos levantados del servidor de GitHub
from pandas import read_csv
data_url = 'https://gist.githubusercontent.com/jackbandy/5cd988ab5c3d95b79219364dce7ee5ae/raw/731ecdbecc7b33030f23cd919e6067dfbaf42feb/song-ratings.csv'
ratings = read_csv(data_url,index_col=0)

In [6]:

from IPython.display import display, HTML
display(HTML(ratings.to_html()))
 One Dance (Drake)Lean On (Major Lazer)Sunflower (Post Malone and Swae Lee)Somebody That I Used To Know (Gotye)Rolling in the Deep (Adele)Can’t Hold Us (Macklemore)7 Rings (Ariana Grande)Wake Me Up (Avicii)Love The Way You Lie (Eminem and Rihanna)bad guy (Billie Eilish)Rather Be (Clean Bandit and Jess Glynne)Call Me Maybe (Carly Rae Jepsen)We Are Young (fun.)Shape of You (Ed Sheeran)Closer (The Chainsmokers)Cheerleader (OMI)Radioactive (Imagine Dragons)Señorita (Shawn Mendes and Camila Cabello)Airplanes (B.o.B and Hayley Williams)Want (Birdtalker)Without You (David Guetta and Usher)Half Love (Red Hearse)Old Town Road – Remix (Lil Nas X and Billy Ray Cyrus)Never Really Over (Katy Perry)
First Name (or Alias)                        
Jack3.05.05.0244NaN5.02.04.05.054.025.02.052.02.05.05.04.055
Nick2.05.05.04535.02.03.05.04.055.011.02.023.03.04.03.04.025
Jubilee5.04.02.03354.03.02.05.05.024.013.02.051.05.01.01.01.045
Jules5.05.03.03543.04.03.05.03.033.034.02.032.03.0NaN4.0NaN15
Trevor4.03.02.04313.04.03.02.0NaN13.044.01.054.03.0NaNNaNNaN13
Megan5.04.04.03333.05.04.05.05.043.043.02.053.03.03.04.0NaN25
Joe3.04.05.04543.04.03.04.03.044.042.04.033.04.04.03.04.053
HallieNaNNaNNaN353NaNNaNNaNNaNNaN3NaN5NaNNaN4NaNNaNNaNNaNNaN24

Tratamiento de nulos.

Como ocurre en la vida real, no todos los usuarios han puntuado los ítems, por lo que tengo que reemplazar los valores nulos, ya que el método de distancia euclídea no soporta valores nulos, entonces elijo los reemplazarlos por valores 3, como un “promedio”.

In [7]:

#Ajustamos y ploteamos un mapa de calor para observar los resultados hasta aquí
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 9))
ratings=ratings.fillna(3)
sns.heatmap(ratings, annot=True)

Out[7]:

<AxesSubplot:ylabel='First Name (or Alias)'>

Funciones para calcular distancias

Existen diferentes metodos para calcular distancias entre elementos.

Resulta que el confiable teorema de Pitágoras se generaliza bastante bien. (Aunque aclaro que la mayoría de los recomendadores usan otras métricas de distancia como la distancia del coseno). Si hay tres dimensiones, podemos llamarlas a, b y c, y luego simplemente sumarlas debajo de la raíz cuadrada: distancia = sqrt (a ^ 2 + b ^ 2 + c ^ 2). Y de hecho podemos hacer esto con cualquier número de dimensiones: distancia = sqrt (a ^ 2 + b ^ 2 + c ^ 2 + d ^ 2 + … + n ^ 2).

In [46]:

#Utilizo esta función euclidiana del sistema para calcular la similitud entre los usuarios
def eDistance(person1,person2):
  distance = euclidean(person1,person2)
  return distance

Solución en 4 fases

Fase 1: Definir función para encontrar similitud entre personas.

Sistema de Recomendacion de Filtrado Colaborativo User-User

Definir una función o varias que crean necesarios para encontrar la persona mas similar a otra recibida por parametro. Para ello realizo con la función una comparación iterativa entre todos los usuarios, y retorna el usuario mas similar.

In [47]:

#Definir función para calcular similitud entre personas
def most_similar_to(name):
  person = ratings.loc[name]
  closest_distance=float('inf')
  closest_person=''
  for other_person in ratings.itertuples():
    if other_person.Index==name:
      # don't compare a person to themself
      continue
    distance_to_other_person = eDistance(person,ratings.loc[other_person.Index])
    #print(distance_to_other_person)
    if distance_to_other_person < closest_distance:
       # new high score! save it
       closest_distance = distance_to_other_person
       closest_person = other_person.Index
  return closest_person, closest_distance

In [48]:

#Introduzca entre las comillas, el nombre de la persona a la que que quiere encontrar el mas similar
#Las opciones son Hallie, Jack, Joe, Megan, Trevor, Jules, Jubilee y Nick

name='Nick'
closest_person, closest_distance = most_similar_to(name)
print(name,"es más similar a",closest_person,"con una distancia de",closest_distance)
Nick es más similar a Joe con una distancia de 6.6332495807108

Fase 2: Definir función para obtener los ítems preferidos de una persona.

Ahora la nueva función recibe un nombre de usuario por parametro, y devuelve las canciones preferidas ( con puntuacion = 5)

In [49]:

#Devuelvo la función con los ítems o canciones más preferidas por el usuario
def preferred_items(name):
    person = ratings.loc[name]
    preferred_items = person[person==5].index.values
    return preferred_items

In [50]:

#Introduzca entre las comillas, el nombre de la persona a la que que quiere encontrar sus canciones preferidas
#Los usuarios son Hallie, Jack, Joe, Megan, Trevor, Jules, Jubilee y Nick

name='Joe'
recommended_items = preferred_items(closest_person)
print("Las canciones preferidas de",name,"son:\n",recommended_items)
Las canciones preferidas de Joe son:
 ['Sunflower (Post Malone and Swae Lee)' 'Rolling in the Deep (Adele)'
 'Old Town Road - Remix (Lil Nas X and Billy Ray Cyrus)']

Fase 3: Integración de las funciones definidas anteriormente.

Integro las funciones anteriormente definidas, para generar una solucion que reciba por parametro una persona por ejemplo “Hallie”, luego que encuentre la persona mas similiar a ella, y le recomiende sus items preferidos.

In [55]:

#Introduzca entre las comillas, el nombre de la persona a la que que quiere encontrar su usuario más similar
#y sus canciones preferidas
#Los usuarios posibles son Hallie, Jack, Joe, Megan, Trevor, Jules, Jubilee y Nick

name='Jules'
closest_person, closest_distance = most_similar_to(name)
recommended_items = preferred_items(closest_person)
print(name,"es más similar a",closest_person,"con una distancia de",closest_distance)
print("Las canciones preferidas de",name,"son:\n",recommended_items)
Jules es más similar a Megan con una distancia de 4.69041575982343
Las canciones preferidas de Jules son:
 ['One Dance (Drake)' 'Wake Me Up (Avicii)' 'bad guy (Billie Eilish)'
 'Rather Be (Clean Bandit and Jess Glynne)'
 'Radioactive (Imagine Dragons)' 'Never Really Over (Katy Perry)']

Fase 4: Definir función para encontrar ítems similares entre sí.

Sistema de Recomendacion de Filtrado Colaborativo Item-Item

Defino una función que recibe por parametro un item y retorna el item mas parecido.

In [58]:

#Metodo para encontrar el item mas similiar a otro
def most_similar_item_to(item):
    item = ratings.loc[:,item_name]
    closest_distance=float('inf')
    closest_item=''
    for column in ratings:
        if column==item_name:
            continue   
        distance_to_other_item = eDistance(item,ratings.loc[:,column])
        if distance_to_other_item < closest_distance:
         closest_distance = distance_to_other_item
         closest_item = column
    return closest_item, closest_distance

In [59]:

#Tema preferido de Hallie
item_name = "Lean On (Major Lazer)"
closest_item  = most_similar_item_to(item_name)
print(item_name,"es muy similar a",closest_item)
Lean On (Major Lazer) es muy similar a ('bad guy (Billie Eilish)', 2.0)

In [60]:

#Tema preferido de Hallie
item_name = "Rolling in the Deep (Adele)"
closest_item  = most_similar_item_to(item_name)
print(item_name,"es muy similar a",closest_item)
Rolling in the Deep (Adele) es muy similar a ('Lean On (Major Lazer)', 2.8284271247461903)

In [62]:

tema1 = ratings.loc[:,"Rolling in the Deep (Adele)"]
tema2 = ratings.loc[:,"Never Really Over (Katy Perry)"]
plt.figure(figsize=(12, 9))
comparativo = pd.concat([tema1, tema2], axis=1)
sns.heatmap(comparativo, annot=True)

Out[62]:

<AxesSubplot:ylabel='First Name (or Alias)'>

Muchas gracias.