En este trabajo se busca determinar el estado de ánimo de una persona, en la última semana, basándonos en sus escuchas registradas en la plataforma Last.fm.
Para ello, seguiremos este proceso:
from lastfm import lastfm_api
from genius import genius_api
from analysis import mood_analysis
from visualization import emotion_visualization
from datetime import date, datetime, timedelta
# To format the dictionaries.
from pprint import pprint
# To print the plots in the notebook.
import psutil
from plotly.offline import init_notebook_mode
import plotly.io as pio
init_notebook_mode(connected=False)

Last.fm es una red social que registra la música que escuchan sus usuarios. Ofrece una gran variedad de estadísticas, como los más escuchados, géneros preferidos... También es capaz de hacer recomendaciones en base a las escuchas: artistas similares, canciones que le puedan gustar, etc.
En nuestro caso, solo nos centraremos en obtener las canciones escuchadas por el usuario. Para ello, primero necesitamos proveer un usuario de Last.FM válido.
user = input()
print(user)
# Check if the provided user exists.
print(f'Retrieving information from the user \'{user}\'')
userInfo = lastfm_api.user_info(user)
if 'error' in userInfo:
print(f"{userInfo['message']} - {user}")
exit()
else:
print(f"The user '{user}' is a valid user.")
A continuación, obtenemos las escuchas en la última semana, agrupadas por artista:
# Get the tracks from the last 7 days.
today = date.today()
lastWeekDate = datetime(today.year, today.month, today.day) - timedelta(days = 7)
user_tracks = lastfm_api.user_tracks_from_date(user, lastWeekDate)
songs_by_artist = lastfm_api.group_tracks_by_artist(user_tracks)
print(f"Found {len(user_tracks)} songs by {len(songs_by_artist)} different artists.")
first_artist = list(songs_by_artist.keys())[0]
pprint(songs_by_artist[first_artist])
lyrics_by_artist = genius_api.lyrics_by_artists(songs_by_artist)
artist = lyrics_by_artist[0]
print(f"Name: {artist['name']}")
first_song = list(artist['songs'].keys())[0]
print(f"Song: {first_song}\n")
print(f"Date listened: {artist['songs'][first_song]['date_listened']}")
print(artist['songs'][first_song]['lyrics'])
Para analizar las emociones de las letras de las canciones, vamos a utilizar la librería NCRLex. Esta librería toma de base la colección de palabras en inglés EmoLex, compilada por la National Research Council (NRC) de Canada.
Esta colección asigna a las palabras que la componen una o más emociones del modelo de Plutchik. Es un modelo creado por Robert Plutchik en 1980 y que tiene la forma de una rueda. Cuenta con 8 emociones básicas: alegría, confianza, miedo, sorpresa, tristeza, aversión, ira y anticipación. Estas emociones básicas pueden combinarse para definir emociones compuestas más concretas, como el amor o los remordimientos.
![]()
En la colección, las palabras aparecen de la siguiente forma: 'abandoned': ['anger', 'fear', 'sadness']
De cada una de las canciones, calcularemos la prevalencia de las emociones y las devolveremos como un vector.
Por ejemplo: [0.0, 0.09230769230769231, 0.0, 0.0, 0.18461538461538463, 0.0, 0.0, 0.1076923076923077]
song_emotions_by_artist = mood_analysis.analyse_lyrics(lyrics_by_artist)
emotion_by_user = { user: song_emotions_by_artist }
artist = song_emotions_by_artist[1]
print(f"Name: {artist['name']}")
first_song = list(artist['songs'].keys())[0]
print(f"Song: {first_song}\n")
print(song_emotions_by_artist[0]['categories'])
print(artist['songs'][first_song])
Solo nos queda agregar los datos para poder visualizarlos. Vamos a agregarlos de dos formas distintas:
En ambos casos, sumamos todos los pesos de las emociones obtenidos y normalizamos el vector. El resultado será el estado anímico del usuario durante la semana completa, y su estado en cada día concreto.
user_emotions = mood_analysis.normalize_user_emotions(song_emotions_by_artist, lastWeekDate)
emotion_by_user = {user: user_emotions}
pprint(emotion_by_user)
Ya solo nos queda visualizar los datos. Se han elegido distintas gráficas para estudiarlos.
La primera de ellas es una visualización de serie temporal. En ella, vemos la evolución diaria de las emociones del usuario.
chart = emotion_visualization._create_time_series_chart(user, emotion_by_user[user], mood_analysis.emotions)
chart
La siguiente visualización es mediante un gráfico de tipo radar (radar chart), que nos permite comparar gráficamente los sentimientos del usuario:
chart = emotion_visualization._create_radar_chart(emotion_by_user, mood_analysis.emotions)
chart
Además, aprovechando esta misma visualización, podemos comparar distintos usuarios de la platforma.
NOTA: En este caso, se va a utilizar los datos de otro usuario calculado previamente. El script LastFmMoodDetection.py acepta varios usuarios simultaneos para hacer el análisis completo.
# Add precalculated user emotions.
emotion_by_user['MissLannet'] = {'total': [0.55886765, 0.35558085, 0.22091529, 0.09942512, 0.2337744 , 0.07233408, 0.16831641, 0.64368252]}
chart = emotion_visualization._create_radar_chart(emotion_by_user, mood_analysis.emotions)
chart
Si queremos comparar muchos usuarios, el radar chart nos puede parecer insuficiente. Es dificil compararlos ya que no se distingue apenas.
emotion_by_user['DartSoul'] = {'total': [0.28606621551798916, 0.44747382623474274, 0.3934163358050923, 0.27063167723415776, 0.4643379065242006, 0.23456629517295632, 0.32643895466563194, 0.33574089382072886]}
emotion_by_user['TechnoJazz'] = {'total': [0.499486660842346, 0.3710087165225994, 0.25125548453575264, 0.33280029821609003, 0.3972576962785898, 0.27001179693125393, 0.22408326698964579, 0.39755111950972094]}
emotion_by_user['newmailson'] = {'total': [0.5450923681249055, 0.3259429615008384, 0.36709638037321246, 0.19264969059702178, 0.41176630489645083, 0.2309836781485909, 0.31284530966533536, 0.3224665922092935]}
emotion_by_user['escalberto'] = {'total': [0.5185218580327058, 0.4070076127690966, 0.3598331468479219, 0.18713271962753733, 0.4200637514506998, 0.207653596200937, 0.3116937141650205, 0.29026676491915604]}
chart = emotion_visualization._create_radar_chart(emotion_by_user, mood_analysis.emotions)
chart
Entonces, para ver la similitud entre usuarios, vamos a usar una aproximacióm de Multidimension Scaling. Así, visualizando la matriz de proximidad, veremos cuanto se parecen los usuarios al dado.
chart = emotion_visualization._create_proximity_matrix_chart(emotion_by_user, mood_analysis.emotions)
chart