# Utils
import re
import json
import yaml
import pprint as pp
import importlib
import urllib
import time
from thefuzz import fuzz
from thefuzz import process
import textwrap as tw
# Data
import numpy as np
import pandas as pd
pd.options.display.max_columns = None
# Visualization
import matplotlib.pyplot as plt
import seaborn as sns
import vapeplot
import dufte
plt.style.use(dufte.style)
vapeplot.set_palette('jazzcup')
palette = vapeplot.palette("jazzcup")
# Spotify
import spotipy
from spotipy.oauth2 import SpotifyOAuth
from spotify_functions import offset_api_limit
# Genius
import genius_functions as genius
importlib.reload(genius)
# Scraping
import requests
import urllib.request
import urllib.parse
from bs4 import BeautifulSoup
# Models
from sentence_transformers import SentenceTransformer
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score, silhouette_samples
from sklearn.preprocessing import MinMaxScaler
from pysentimiento import create_analyzer
with open("spotify/spotify_details.yml", 'r') as stream:
spotify_details = yaml.safe_load(stream)
# https://developer.spotify.com/web-api/using-scopes/
scope = "user-library-read user-follow-read user-top-read playlist-read-private"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
client_id=spotify_details['SPOTIPY_CLIENT_ID'],
client_secret=spotify_details['SPOTIPY_CLIENT_SECRET'],
redirect_uri=spotify_details['SPOTIPY_REDIRECT_URI'],
scope=scope,
))
badbunny_uri = 'spotify:artist:4q3ewBCX7sLwd24euuV69X'
artist = sp.artist(badbunny_uri)
albums = offset_api_limit(sp, sp.artist_albums(artist_id=badbunny_uri,
album_type=['album', 'single'],
country=None,
limit=20))
pp.pprint(artist)
{'external_urls': {'spotify': 'https://open.spotify.com/artist/4q3ewBCX7sLwd24euuV69X'},
'followers': {'href': None, 'total': 50650663},
'genres': ['latin', 'reggaeton', 'trap latino'],
'href': 'https://api.spotify.com/v1/artists/4q3ewBCX7sLwd24euuV69X',
'id': '4q3ewBCX7sLwd24euuV69X',
'images': [{'height': 640,
'url': 'https://i.scdn.co/image/ab6761610000e5eb8ee9a6f54dcbd4bc95126b14',
'width': 640},
{'height': 320,
'url': 'https://i.scdn.co/image/ab676161000051748ee9a6f54dcbd4bc95126b14',
'width': 320},
{'height': 160,
'url': 'https://i.scdn.co/image/ab6761610000f1788ee9a6f54dcbd4bc95126b14',
'width': 160}],
'name': 'Bad Bunny',
'popularity': 100,
'type': 'artist',
'uri': 'spotify:artist:4q3ewBCX7sLwd24euuV69X'}
df_albums = pd.DataFrame(columns=['album_name', 'artists', 'total_tracks',
'album_url', 'cover_url', 'album_uri'])
for i, album in enumerate(albums['items']):
df_albums.loc[i] = {
'album_name': album['name'],
'artists': ', '.join(art['name'] for art in album['artists']),
'total_tracks': album['total_tracks'],
'album_url': album['external_urls']['spotify'],
'cover_url': album['name'],
'album_uri': album['uri']
}
df_albums
df_tracks = pd.DataFrame(columns=['song_name', 'song_artists', 'song_duration',
'song_is_explicit', 'song_url',
'song_preview_url', 'song_uri',
'album_uri'])
i = 0
for idx, row in df_albums.iterrows():
album_uri = row['album_uri']
album_tracks = offset_api_limit(
sp, sp.album_tracks(album_id=album_uri,
limit=50, offset=0, market=None)
)
for track in album_tracks:
df_tracks.loc[i] = {
'song_name': track['name'],
'song_artists': ', '.join(art['name'] for art in track['artists']),
'song_duration': (track['duration_ms']/1000)/60,
'song_is_explicit': track['explicit'],
'song_url': track['external_urls']['spotify'],
'song_preview_url': track['preview_url'],
'song_uri': track['uri'],
'album_uri': album_uri
}
i += 1
df_tracks['audio_features'] = sp.audio_features(list(df_tracks['song_uri'].values))
df_tracks = df_tracks.drop('audio_features', axis=1).assign(**df_tracks['audio_features'].apply(pd.Series))
drop_cols = ['type', 'id', 'uri', 'track_href', 'analysis_url',
'duration_ms', 'time_signature']
df_tracks.drop(columns=drop_cols, inplace=True)
df_audio = pd.merge(df_albums, df_tracks, how='left', on='album_uri')
df_audio.sample(5)
artist_name = 'Bad Bunny'
results = genius.search_artist(artist_name)
Cardi B, Bad Bunny & J Balvin: I Like It
Nio García, Casper Mágico & Bad Bunny: Te Boté (Remix)
Bad Bunny: Amorfoda
Bad Bunny: Tú No Metes Cabra
Bad Bunny: MIA
Bad Bunny: Yonaguni
Bad Bunny, Jowell & Randy & Ñengo Flow: Safaera
Jhay Cortez, J Balvin & Bad Bunny: No Me Conoce (Remix)
Bad Bunny & Jhay Cortez: DÁKITI
Bad Bunny: Chambea
pp.pprint(results[2]['result']['primary_artist'])
{'api_path': '/artists/690350',
'header_image_url': 'https://images.genius.com/3f26c4ceeed73948eff37118e65d9c15.1000x403x1.jpg',
'id': 690350,
'image_url': 'https://images.genius.com/41835ceb7df4b2f4f04f5b77673fff7a.746x746x1.jpg',
'is_meme_verified': False,
'is_verified': False,
'name': 'Bad Bunny',
'url': 'https://genius.com/artists/Bad-bunny'}
artist_id = results[2]['result']['primary_artist']['id']
artist_data = genius.get_artist(artist_id)
Followers: 1025
%%time
songs_ids = genius.get_songs_ids(artist_id)
Page 2 finished scraping
Page 3 finished scraping
Page 4 finished scraping
Page 5 finished scraping
Page 6 finished scraping
Page 7 finished scraping
Page 8 finished scraping
Page 9 finished scraping
Page 10 finished scraping
Page 11 finished scraping
Page 12 finished scraping
Page 13 finished scraping
Page 14 finished scraping
Page 15 finished scraping
Page 16 finished scraping
Page 17 finished scraping
Page 18 finished scraping
Page 19 finished scraping
Page 20 finished scraping
Page 21 finished scraping
Song id were scraped from 21 pages
Wall time: 25.5 s
len(songs_ids)
df_audio.sample(5)
def get_genius_id(song_name, songs_ids):
song_distances = []
for genius_name, genius_id in songs_ids:
distance = fuzz.ratio(song_name, genius_name)
song_distances.append((genius_name, genius_id, distance))
song_distances = sorted(song_distances,
key=lambda x: x[2],
reverse=True)
id_most_similar = song_distances[0][1]
print(f'{song_name} -> {song_distances[0][0]}')
return id_most_similar
df_audio['genius_id'] = df_audio['song_name'].apply(get_genius_id,
songs_ids=songs_ids)
Moscow Mule -> Moscow Mule
Después de la Playa -> Después de la Playa
Me Porto Bonito -> Me Porto Bonito
Tití Me Preguntó -> Tití Me Preguntó
Un Ratito -> Un Ratito
Yo No Soy Celoso -> Yo No Soy Celoso
Tarot -> Tarot
Neverita -> Neverita
La Corriente -> La Corriente
Efecto -> Efecto
Party -> Party
Aguacero -> Aguacero
Enséñame a Bailar -> Enséñame a Bailar
Ojitos Lindos -> Ojitos Lindos
Dos Mil 16 -> Dos Mil 16
El Apagón -> El Apagón
Otro Atardecer -> Otro Atardecer
Un Coco -> Un Coco
Andrea -> Andrea
Me Fui de Vacaciones -> Me Fui de Vacaciones
Un Verano Sin Ti -> Un Verano Sin Ti
Agosto -> Agosto
Callaita -> Callaita
EL MUNDO ES MÍO -> EL MUNDO ES MÍO
TE MUDASTE -> TE MUDASTE
HOY COBRÉ -> HOY COBRÉ
MALDITA POBREZA -> MALDITA POBREZA
LA NOCHE DE ANOCHE -> LA NOCHE DE ANOCHE
TE DESEO LO MEJOR -> TE DESEO LO MEJOR
YO VISTO ASÍ -> YO VISTO ASÍ
HACIENDO QUE ME AMAS -> HACIENDO QUE ME AMAS
BOOKER T -> BOOKER T
LA DROGA -> LA DROGA
DÁKITI -> DÁKITI
TRELLAS -> TRELLAS
SORRY PAPI -> SORRY PAPI
120 -> 120
ANTES QUE SE ACABE -> ANTES QUE SE ACABE
CANTARES DE NAVIDAD -> ANTES QUE SE ACABE
SI ELLA SALE -> SI ELLA SALE
MÁS DE UNA CITA -> MÁS DE UNA CITA
BYE ME FUI -> BYE ME FUI
CANCIÓN CON YANDEL -> CANCIÓN CON YANDEL
PA' ROMPERLA -> PA’ ROMPERLA
BAD CON NICKY -> BAD CON NICKY
BENDICIONES -> BENDICIONES
CÓMO SE SIENTE - Remix -> CÓMO SE SIENTE (Remix)
RONCA FREESTYLE -> RONCA FREESTYLE
EN CASITA -> EN CASITA
Si Veo a Tu Mamá -> Si Veo a Tu Mamá
La Difícil -> La Difícil
Pero Ya No -> Pero Ya No
La Santa -> La Santa
Yo Perreo Sola -> Yo Perreo Sola
Bichiyal -> Bichiyal
Soliá -> Soliá
La Zona -> La Zona
Que Malo -> Qué Malo
Vete -> Vete
Ignorantes -> Ignorantes
A Tu Merced -> A Tu Merced
Una Vez -> Una Vez
Safaera -> Safaera
25/8 -> 25/8
Está Cabrón Ser Yo -> Está Cabrón Ser Yo
Puesto Pa' Guerrial -> Puesto Pa’ Guerrial
P FKN R -> P FKN R
Hablamos Mañana -> Hablamos Mañana
<3 -> ﹤3
MOJAITA -> MOJAITA
YO LE LLEGO -> YO LE LLEGO
CUIDAO POR AHÍ -> CUIDAO POR AHÍ
QUE PRETENDES -> QUÉ PRETENDES
LA CANCIÓN -> LA CANCIÓN
UN PESO -> UN PESO
ODIO -> ODIO
COMO UN BEBÉ -> COMO UN BEBÉ
NI BIEN NI MAL -> NI BIEN NI MAL
200 Mph -> 200 MPH
¿Quien Tu Eres? -> ¿Quién Tú Eres?
Caro -> Caro
Tenemos Que Hablar -> Tenemos Que Hablar
Otra Noche en Miami -> Otra Noche en Miami
Ser Bichote -> Ser Bichote
Si Estuviésemos Juntos -> Si Estuviésemos Juntos
Solo de Mi -> Solo de Mí
Cuando Perriabas -> Cuando Perriabas
La Romana -> La Romana
Como Antes -> Como Antes
RLNDT -> RLNDT
Estamos Bien -> Estamos Bien
MIA -> MIA
df_audio['lyrics'] = np.nan
%%time
for i in df_audio.index:
if pd.isna(df_audio.at[i, 'lyrics']):
df_audio.at[i, 'lyrics'] = genius.retrieve_lyrics(
df_audio.at[i, 'genius_id'])
else:
continue
Moscow Mule --> [Letra de "Moscow Mule"] [Intro] Si yo no te escribo, tú no me escribe, ey Si
Después de la Playa --> [Letra de "Después de la Playa"] [Intro] Yeah-yeah, yeah-yeah, yeah-yeah Eh-e
Me Porto Bonito --> [Letra de "Me Porto Bonito"] [Intro: Bad Bunny & Chencho Corleone ] Yeah-ye
Tití Me Preguntó --> [Letra de "Tití Me Preguntó"] [Intro: Bad Bunny] Ey [Coro: Bad Bunny] Tití m
Un Ratito --> [Letra de "Un Ratito"] [Intro] Yeah-yeah-yeah-yeah Yeah-yeah-yeah-yeah-yeah
Yo No Soy Celoso --> [Letra de "Yo No Soy Celoso"] [Intro] Ey, ey [Verso 1] El que esté libre de
Tarot --> [Letra de "Tarot"] [Intro: Jhay Cortez] Verte en el VIP de lejito e un privile
Neverita --> [Letra de "Neverita"] [Coro] Yo toy puesto pa ti y tú te me quita Diablo, qué
La Corriente --> [Letra de "La Corriente"] [Intro: Tony Dize] La melodía con la calle Tony Diz
Efecto --> [Letra de "Efecto"] [Intro] Yeah-yeah-yeah-yeah-yeah-yeah Eh-eh-eh-eh [Pre-C
Party --> [Letra de "Party"] [Intro: Rauw Alejandro] Yeah, dice Ra-Rauw [Pre-Coro: Rau
Aguacero --> [Letra de "Aguacero"] [Intro] (Ey, ey) Ey, ey [Verso 1] Me tienes el bicho
Enséñame a Bailar --> [Letra de "Enséñame a Bailar"] [Intro: Bad Bunny] Yeh-yeh-yeh-yeh-yeh-yeh-yeh
Ojitos Lindos --> [Letra de "Ojitos Lindos"] [Verso 1: Li Saumet] Hace mucho tiempo le hago caso
Dos Mil 16 --> [Letra de "Dos Mil 16"] [Intro] Bad Bunny, baby Eh-eh, eh-eh-eh La vida e so
El Apagón --> [Letra de "El Apagón"] [Intro: Bad Bunny] Con mucho cariño para todos ustedes
Otro Atardecer --> [Letra de "Otro Atardecer"] [Coro: Bad Bunny & The Marías] Aún quedan dos bote
Un Coco --> [Letra de "Un Coco"] [Intro] Ante que este Phillie se apague, yo te voy olvida
Andrea --> [Letra de "Andrea"] [Intro: Buscabulla] (Eh-ah) Quiero alguien que se atreva
Me Fui de Vacaciones --> [Letra de "Me Fui de Vacaciones"] [Intro] Ey, ey El sol salió a janguear con
Un Verano Sin Ti --> [Letra de "Un Verano Sin Ti"] [Intro] No sé qué pasó Otro amor que de repente
Agosto --> [Letra de "Agosto"] [Coro] Baby, vamo a hacerlo bajo las estrella Y que la lu
Callaita --> [Letra de "Callaita"] [Intro: Bad Bunny] Se acostó temprano, mañana hay que es
EL MUNDO ES MÍO --> [Letra de "EL MUNDO ES MÍO"] [Intro] (Uh-uh) Ey, ey [Pre-Coro] ¿Quién dijo
TE MUDASTE --> [Letra de "TE MUDASTE"] [Intro] Pasa el tiempo (Pasa el tiempo) Y no te veo (
HOY COBRÉ --> [Letra de "HOY COBRÉ"] [Intro] Yeh-yeh-yeh, ey Bad Bunny, baby, be-bé Ey, ey
MALDITA POBREZA --> [Letra de "MALDITA POBREZA"] [Pre-Coro: Bad Bunny] Yo quiero comprarle un Ferr
LA NOCHE DE ANOCHE --> [Letra de "LA NOCHE DE ANOCHE"] [Intro: ROSALÍA] Yo sé que esto no volverá a p
TE DESEO LO MEJOR --> [Letra de "TE DESEO LO MEJOR"] [Verso 1: Bad Bunny] Yo sé que soy un cabrón Q
YO VISTO ASÍ --> [Letra de "YO VISTO ASÍ"] [Intro] Yeh-yeh, yeh, yeh-yeh, yeh-yeh, yeh-yeh Yeh
HACIENDO QUE ME AMAS --> [Letra de "HACIENDO QUE ME AMAS"] [Intro] Ya no confío en tu mirada Y tus bes
BOOKER T --> [Letra de "BOOKER T"] [Intro: Bad Bunny] Ey, ey [Verso 1: Bad Bunny] Los pon
LA DROGA --> [Letra de "LA DROGA"] [Intro] Uh, uh-uh Uh-uh, uh-uh [Refrán] Tú ere la dro
DÁKITI --> [Letra de "DÁKITI"] [Coro: Jhay Cortez] Baby, ya yo me enteré, se nota cuando
TRELLAS --> [Letra de "TRELLAS"] [Coro] Mirando las estrellas me acordé de ti Y me pregun
SORRY PAPI --> [Letra de "SORRY PAPI"] [Intro: Bad Bunny] Ey, ey [Verso 1: Bad Bunny] La ti
120 --> [Letra de "120"] [Intro] Ey, ey [Verso] Por ti me monto en el carro y meto 1
ANTES QUE SE ACABE --> [Letra de "ANTES QUE SE ACABE"] [Intro] Y que reciban de mí siempre paz, mucha
ANTES QUE SE ACABE --> [Letra de "ANTES QUE SE ACABE"] [Intro] Y que reciban de mí siempre paz, mucha
SI ELLA SALE --> [Letra de "SI ELLA SALE"] [Intro] Yeh-yeh, yeh-yeh, yeh-yeh La calle está apa
MÁS DE UNA CITA --> [Letra de "MÁS DE UNA CITA"] [Intro: Bad Bunny] Hace mese que se dejó, que se
BYE ME FUI --> [Letra de "BYE ME FUI"] [Intro] Yeh, yeh, yeh Yeh, yeh, yeh [Pre-Estribillo]
CANCIÓN CON YANDEL --> [Letra de "CANCIÓN CON YANDEL"] [Intro: Yandel] (Eh-eh) Ella es la que me enc
PA’ ROMPERLA --> [Letra de "PA ROMPERLA"] [Intro: Bad Bunny] Yeh-yeh-yeh-yeh Yeh-yeh-yeh-yeh
BAD CON NICKY --> [Letra de "BAD CON NICKY"] [Intro: Bad Bunny] Yeh-yeh-yeh-yeh (Yeh) Yeh-yeh-y
BENDICIONES --> [Letra de "BENDICIONES"] [Intro] Estoy aquí porque Dios tuvo planes conmigo G
CÓMO SE SIENTE (Remix) --> [Letra de "CÓMO SE SIENTE (Remix)"] [Intro: Bad Bunny] Yeh-yeh-yeh-yeh, ey [P
RONCA FREESTYLE --> [Letra de "Ronca (Freestyle)"] [Intro] Yeh, yeh Okay, ey [Verso] Como phill
EN CASITA --> [Letra de "EN CASITA" ft. Gabriela] [Intro: Bad Bunny] J.R Ey, ey, ey [Coro:
Si Veo a Tu Mamá --> [Letra de "Si Veo a Tu Mamá"] [Intro] Yeh, yeh [Coro] Todavía yo te quiero
La Difícil --> [Letra de "La Difícil"] [Pre-Coro] Ella desaparece, pero aparece cuando le dan
Pero Ya No --> [Letra de "Pero Ya No"] [Intro] Yeh-yeh-yeh-yeh-yeh-yeh-yeh-yeh-eh ¡Ju! Okey,
La Santa --> [Letra de "La Santa"] [Intro: Bad Bunny & Daddy Yankee ] Tú no ere una sant
Yo Perreo Sola --> [Letra de "Yo Perreo Sola" ft. Nesi] [Refrán: Nesi] Ante tú me pichaba (Tú me
Bichiyal --> [Letra de "Bichiyal"] [Intro: Yaviah] Ah, ¡auh! [Estribillo: Bad Bunny] Ella
Soliá --> [Letra de "Soliá"] [Pre-Coro] Llegó soliá, salió sin la amiga Revelá, en el a
La Zona --> [Letra de "La Zona"] [Intro: Bad Bunny] Ello rezan to los día pa que nosotro n
Qué Malo --> [Letra de "Qué Malo"] [Intro: Bad Bunny & Ñengo Flow ] Hace tiempo que no s
Vete --> [Letra de "Vete"] [Intro] (Yeh-yeh-yeh-yeh, yeh-yeh-yeh-yeh) Si te vas, yo qu
Ignorantes --> [Letra de "Ignorantes"] [Intro: Sech, Bad Bunny & Ambos ] Baby, este es
A Tu Merced --> [Letra de "A Tu Merced"] [Intro] Yeh, yeh, yeh, yeh Yeh, yeh, ey [Estribillo
Una Vez --> [Letra de "Una Vez" ft. Mora] [Intro: Mora] Lo nuestro no te conviene, eh Per
Safaera --> [Letra de "Safaera"] [Intro: Randy Nota Loca & Jowell ] Bla, bla, bla, bla,
25/8 --> [Letra de "25/8"] [Intro] Eh, ey Yo sigo siendo el mismo, yo no he cambiao (¡
Está Cabrón Ser Yo --> [Letra de "Está Cabrón Ser Yo"] [Intro: Anuel AA] Brr Its Payday, fellas Yo
Puesto Pa’ Guerrial --> [Letra de "Puesto Pa Guerrial"] [Intro: Myke Towers] Eh, Myke Towers, nigga, a
P FKN R --> [Letra de "P FKN R"] [Intro: Bad Bunny] Yeh-eh, ey Si no sabe de dónde soy, n
Hablamos Mañana --> [Letra de "Hablamos Mañana"] [Estribillo: Bad Bunny] Yo hago lo que me dé la g
﹤3 --> [Letra de "<3"] [Intro] Yeh Gracia, gracia, ey [Verso] Gracia a to el que c
MOJAITA --> [Letra de "MOJAITA"] [Intro: J Balvin, Bad Bunny ] ( Vamo a afincar la cos
YO LE LLEGO --> [Letra de "YO LE LLEGO"] [Intro: Bad Bunny] Ey [Coro: Bad Bunny] Dime dónde
CUIDAO POR AHÍ --> [Letra de "Cuidao Por Ahí"] [Intro: J Balvin] Leggo [Coro: J Balvin] Vamo a
QUÉ PRETENDES --> [Letra de "QUÉ PRETENDES"] [Intro: J Balvin] Uah, uh-uh Uah Yeah, yeah, yeah
LA CANCIÓN --> [Letra de "LA CANCIÓN"] [Intro: Bad Bunny] Yeh, yeh, yeh [Estribillo: Bad Bun
UN PESO --> [Letra de "UN PESO" ft. Marciano Cantero] [Coro: Bad Bunny] ¿Pa qué me vas a a
ODIO --> [Letra de "ODIO"] [Intro: Bad Bunny] No quiero saber de ti (No) No sé qué hac
COMO UN BEBÉ --> [Letra de "COMO UN BEBÉ" ft. Mr Eazi] [Intro: J Balvin] Legendury Beatz Yeah,
NI BIEN NI MAL --> [Letra de "NI BIEN NI MAL"] [Intro: Bad Bunny] Yeh-yeh Yeh-yeh Yeh-yeh [Cor
200 MPH --> [Letra de "200 MPH" ft. Diplo] [Intro] ¡Ju-Ju! Yeh-yeah-yeah Yeah-yeah-yeah-
¿Quién Tú Eres? --> [Letra de "¿Quién Tú Eres?"] [Intro] Uh-uh, uh-uh-uh-uh Rrrrr, ah-ah, ah-ah-a
Caro --> [Letra de "Caro"] [Estribillo: Bad Bunny] Me ven y me preguntan por qué visto
Tenemos Que Hablar --> [Letra de "Tenemos Que Hablar"] [Intro] Yeh-yeh-yeh-yeh-yeh Yeh-yeh-yeh-yeh-y
Otra Noche en Miami --> [Letra de "Otra Noche en Miami"] [Intro] Yeh, yeh [Verso 1] Diez en punto de
Ser Bichote --> [Letra de "Ser Bichote"] [Intro] Eh, eh, eh, eh, eh [Estribillo] Yo siempre
Si Estuviésemos Juntos --> [Letra de "Si Estuviésemos Juntos"] [Intro] Yeh-yeh-yeh-yeh-yeh Yeh-yeh-yeh-y
Solo de Mí --> [Letra de "Solo de Mí"] [Coro] No me vuelvas a decir "Bebé" (¡No!) Yo no soy
Cuando Perriabas --> [Letra de "Cuando Perriabas"] [Intro: Bad Bunny] Yeah-yeah-yeah Yeah Yo te l
La Romana --> [Letra de "La Romana" ft. El Alfa] [Intro: Bad Bunny] (Yeh, yeh, yeh) [Coro 1
Como Antes --> [Letra de "Como Antes"] [Intro] Yeh-yeh-yeh-yeh, ey [Verso 1] Ayer me acordé
RLNDT --> [Letra de "RLNDT"] [Intro] (Hola, ¿quién soy? No sé, se me olvidó Hola, ¿quié
Estamos Bien --> [Letra de "Estamos Bien"] [Intro] Yeah, yeah (Yeah) [Coro] Tamos bien (Wouh)
MIA --> [Letra de "MÍA" ft. Drake] [Intro: Bad Bunny & Drake ] Bad Bunny, baby, beb
Wall time: 4min 44s
df_audio.sample(3)
df_audio.isna().sum()
df_audio.to_csv('data/df_songs.csv', index=False)
df_songs = pd.read_csv('data/df_songs.csv')
df_songs.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 92 entries, 0 to 91
Data columns (total 26 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 album_name 92 non-null object
1 artists 92 non-null object
2 total_tracks 92 non-null int64
3 album_url 92 non-null object
4 cover_url 92 non-null object
5 album_uri 92 non-null object
6 song_name 92 non-null object
7 song_artists 92 non-null object
8 song_duration 92 non-null float64
9 song_is_explicit 92 non-null bool
10 song_url 92 non-null object
11 song_preview_url 91 non-null object
12 song_uri 92 non-null object
13 danceability 92 non-null float64
14 energy 92 non-null float64
15 key 92 non-null int64
16 loudness 92 non-null float64
17 mode 92 non-null int64
18 speechiness 92 non-null float64
19 acousticness 92 non-null float64
20 instrumentalness 92 non-null float64
21 liveness 92 non-null float64
22 valence 92 non-null float64
23 tempo 92 non-null float64
24 genius_id 92 non-null int64
25 lyrics 92 non-null object
dtypes: bool(1), float64(10), int64(4), object(11)
memory usage: 18.2+ KB
df_songs.sample(5)
album_nameobject
artistsobject
73
OASIS
J Balvin, Bad Bunny
91
X 100PRE
Bad Bunny
48
LAS QUE NO IBAN A SALIR
Bad Bunny
28
EL ÚLTIMO TOUR DEL MUNDO
Bad Bunny
1
Un Verano Sin Ti
Bad Bunny
print(df_songs.iloc[11]['lyrics'])
[Letra de "Aguacero"]
[Intro]
(Ey, ey)
Ey, ey
[Verso 1]
Me tienes el bicho ansioso, hey, ey
Quédate en cuatro, que se ve precioso (Ey, ey)
Ese culito e un tramposo, ey
Si abres una iglesia, me hago religioso
Exquisito y delicioso, me la como y ni reposo, ey, ey
Baby, lo nuestro es amistoso, je
Pero si tú quieres, cuando sea soy tu esposo
[Refrán]
Okay (Okay), sí, sí (Sí, sí)
Encima de mí fue que te conocí (Ey)
Ey, mami tú ere así
No te haga, yo también soy así
Okay, sí, sí
Encima de mí fue que te conocí
Ey, mami tú ere así
No te haga, yo también soy así
[Estribillo]
Porque yo soy un cuero y tú también, y tú también
Si el calor es de noventa, el aguacero es de cien
Vamo a pasarla bien
Yo soy un cuero y tú también, y tú también
Si el calor es de noventa, el aguacero es de cien
Vamo a pasarla bien
[Post-Estribillo]
Pero dale easy, easy, ey
Que sabes que soy Pisci, ey
Y enamorarme es bien fácil, ey
Pero olvidarme es difícil
Mami, dale easy, easy, ey
Que sabes que soy Pisci, je
Y enamorarme es bien fácil, ey
Pero olvidarme es difícil
regex_bracket = re.compile(r'\[.*?\]')
regex_newline = re.compile(r'\n')
regex_word = re.compile(r'\u2005')
regex_word2 = re.compile(r'\u205f')
lyrics_clean = (
df_songs.lyrics
.str.replace(r'\n', '', regex=True)
.str.replace(r'\[.*?\]', '', regex=True)
.str.replace(r'\u2005', ' ', regex=True)
.str.replace(r'\u205f', ' ', regex=True)
.str.replace(r'[(),-]', ' ', regex=True)
.str.replace(r'\s+', ' ', regex=True)
.str.strip()
.str.lower()
)
df_songs['lyrics_clean'] = lyrics_clean
print(tw.fill(lyrics_clean.iloc[11], 90))
ey ey ey ey me tienes el bicho ansioso hey ey quédate en cuatro que se ve precioso ey ey
ese culito e un tramposo ey si abres una iglesia me hago religioso exquisito y delicioso
me la como y ni reposo ey ey baby lo nuestro es amistoso je pero si tú quieres cuando sea
soy tu esposo okay okay sí sí sí sí encima de mí fue que te conocí ey ey mami tú ere así
no te haga yo también soy así okay sí sí encima de mí fue que te conocí ey mami tú ere así
no te haga yo también soy así porque yo soy un cuero y tú también y tú también si el calor
es de noventa el aguacero es de cien vamo a pasarla bien yo soy un cuero y tú también y tú
también si el calor es de noventa el aguacero es de cien vamo a pasarla bien pero dale
easy easy ey que sabes que soy pisci ey y enamorarme es bien fácil ey pero olvidarme es
difícil mami dale easy easy ey que sabes que soy pisci je y enamorarme es bien fácil ey
pero olvidarme es difícil
embedding_model = SentenceTransformer('hiiamsid/sentence_similarity_spanish_es')
embeddings = embedding_model.encode(list(lyrics_clean))
Downloading: 100%|██████████| 1.18k/1.18k [00:00<00:00, 733kB/s]
Downloading: 100%|██████████| 190/190 [00:00<00:00, 187kB/s]
Downloading: 100%|██████████| 4.46k/4.46k [00:00<00:00, 2.51MB/s]
Downloading: 100%|██████████| 701/701 [00:00<00:00, 446kB/s]
Downloading: 100%|██████████| 123/123 [00:00<00:00, 81.8kB/s]
Downloading: 100%|██████████| 4.00/4.00 [00:00<00:00, 2.73kB/s]
Downloading: 100%|██████████| 770/770 [00:00<00:00, 657kB/s]
Downloading: 100%|██████████| 439M/439M [00:05<00:00, 79.6MB/s]
Downloading: 100%|██████████| 53.0/53.0 [00:00<00:00, 37.4kB/s]
Downloading: 100%|██████████| 299/299 [00:00<00:00, 347kB/s]
Downloading: 100%|██████████| 112/112 [00:00<00:00, 129kB/s]
Downloading: 100%|██████████| 480k/480k [00:00<00:00, 66.5MB/s]
Downloading: 100%|██████████| 556/556 [00:00<00:00, 669kB/s]
Downloading: 100%|██████████| 242k/242k [00:00<00:00, 42.2MB/s]
Downloading: 100%|██████████| 229/229 [00:00<00:00, 189kB/s]
embeddings = embedding_model.encode(list(lyrics_clean))
print(embeddings.shape)
(92, 768)
pca = PCA(n_components=2)
pca_embeddings = pca.fit_transform(embeddings)
sentiment_analyzer = create_analyzer(task="sentiment", lang="es")
emotion_analyzer = create_analyzer(task="emotion", lang="es")
happy_song = df_songs.iloc[90]['lyrics']
sad_song = df_songs.iloc[20]['lyrics']
print(
'Estamos bien (happy song):',
sentiment_analyzer.predict(happy_song),
emotion_analyzer.predict(happy_song),
sep='\n',
end='\n\n'
)
print(
'Un veranto sin ti (sad song):',
sentiment_analyzer.predict(sad_song),
emotion_analyzer.predict(sad_song),
sep='\n'
)
Estamos bien (happy song):
AnalyzerOutput(output=POS, probas={POS: 0.852, NEU: 0.147, NEG: 0.002})
AnalyzerOutput(output=others, probas={others: 0.622, joy: 0.358, surprise: 0.010, sadness: 0.003, anger: 0.003, disgust: 0.002, fear: 0.001})
Un veranto sin ti (sad song):
AnalyzerOutput(output=NEU, probas={NEU: 0.888, NEG: 0.107, POS: 0.006})
AnalyzerOutput(output=sadness, probas={sadness: 0.621, others: 0.291, anger: 0.030, joy: 0.020, fear: 0.016, surprise: 0.012, disgust: 0.010})
df_lyrics = pd.DataFrame(data=pca_embeddings, columns=['component1', 'component2'])
df_lyrics['is_explicit'] = df_songs.song_is_explicit
%%time
for idx in df_lyrics.index:
if idx % 5 == 0: print(idx)
lyric = df_songs.at[idx, 'lyrics']
sentiment = sentiment_analyzer.predict(lyric)
emotion = emotion_analyzer.predict(lyric)
df_lyrics.at[idx, 'sentiment'] = sentiment.probas['POS']
df_lyrics.at[idx, 'emotion'] = sorted(
[emo for emo in emotion.probas.keys() if emo != 'others'],
key=lambda x: emotion.probas[x],
reverse=True
)[0]
0
5
10
15
20
25
30
35
40
45
50
55
60
65
70
75
80
85
90
CPU times: user 1min 38s, sys: 1.78 s, total: 1min 40s
Wall time: 1min 43s
df_lyrics.is_explicit = df_lyrics.is_explicit.astype(np.int32)
df_lyrics_pre = pd.concat([df_lyrics.drop('emotion', axis=1),
pd.get_dummies(df_lyrics.emotion)],
axis=1)
df_lyrics_pre
component1float32
-4.331412315368652 - 5.221500396728516
component2float32
-3.0199506282806396 - 5.197409629821777
0
-0.3411604166
-0.7771310806
1
-2.923129082
-2.029856205
2
2.052580118
-0.8592008352
3
0.1324528903
-0.7751188874
4
-2.216681004
1.13269186
5
1.897252798
0.4964691103
6
1.78876543
-0.03410467505
7
-1.612326741
-1.873506188
8
2.391081095
0.3909966648
9
0.04299218208
-0.5735672116
scaler = MinMaxScaler()
x_scaled = scaler.fit_transform(df_lyrics_pre.values)
df_lyrics_pre_scaled = pd.DataFrame(x_scaled, columns=df_lyrics_pre.columns)
inertia = []
sil = []
k_list = range(2, 11)
for k in k_list:
km = KMeans(n_clusters=k, random_state=0)
km.fit(df_lyrics_pre_scaled)
inertia.append(km.inertia_)
sil.append(silhouette_score(df_lyrics_pre_scaled, km.labels_))
plt.figure(figsize=(8, 6))
sns.lineplot(x=k_list, y=inertia, marker='o')
plt.xlim(min(k_list) - max(k_list) * 0.05, max(k_list) * 1.05)
plt.ylim(min(inertia) - max(inertia) * 0.05, max(inertia) * 1.05)
plt.xlabel('Clusters')
plt.ylabel('Inercia')
plt.title('Inercia por número de clusters')
plt.show()
plt.figure(figsize=(8, 6))
sns.lineplot(x=k_list, y=sil, marker='o', color=palette[1])
plt.xlim(min(k_list) - max(k_list) * 0.05, max(k_list) * 1.05)
plt.ylim(min(sil) - max(sil) * 0.05, max(sil) * 1.05)
plt.xlabel('Clusters')
plt.ylabel('Silhouette media')
plt.title('Silhouette media para el dataset')
plt.show()
X_std = df_lyrics_pre_scaled
k_list = [5, 6, 7]
for i, k in enumerate(k_list):
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.set_size_inches(18, 7)
# Run the Kmeans algorithm
km = KMeans(n_clusters=k, random_state=0)
labels = km.fit_predict(X_std)
centroids = km.cluster_centers_
# Get silhouette samples
silhouette_vals = silhouette_samples(X_std, labels)
silho_score = silhouette_score(X_std, km.labels_)
# Silhouette plot
y_ticks = []
y_lower, y_upper = 0, 0
for i, cluster in enumerate(np.unique(labels)):
cluster_silhouette_vals = silhouette_vals[labels == cluster]
cluster_silhouette_vals.sort()
y_upper += len(cluster_silhouette_vals)
ax1.barh(range(y_lower, y_upper),
cluster_silhouette_vals,
edgecolor='none',
height=1)
ax1.text(-0.03, (y_lower + y_upper) / 2, str(i))
y_lower += len(cluster_silhouette_vals)
# Get the average silhouette score and plot it
avg_score = np.mean(silhouette_vals)
ax1.axvline(avg_score, linestyle='--', linewidth=2, color='0.7')
ax1.set_yticks([])
ax1.set_xlim([-0.1, 1.0])
ax1.set_xlabel('Coeficiente Silhouette')
ax1.set_ylabel('Etiquetas de los clusters')
ax1.set_title('Silhouette para cada cluster', y=1.00)
# Scatter plot of data colored with labels
sns.scatterplot(x=X_std.iloc[:, 0],
y=X_std.iloc[:, 1],
hue=labels,
palette=(palette*2)[:k],
ax=ax2)
ax2.scatter(centroids[:, 0],
centroids[:, 1],
marker='+',
c='0.2',
s=1000)
ax2.set_xlim([0, 1])
ax2.set_ylim([0, 1])
ax2.set_xlabel('Dimensión 1 del embedding')
ax2.set_ylabel('Dimensión 2 del embedding')
ax2.set_title('Visualización de los clusters', y=1.00)
ax2.set_aspect('equal')
plt.suptitle(f'Silhouette usando {k} clusters: ({silho_score:.3f})',
fontsize=18,
fontweight=550,
y=1.05)
plt.show()
lyrics_clustering = KMeans(n_clusters=5, random_state=0)
lyrics_clustering.fit(df_lyrics_pre_scaled)
lyrics_clusters = lyrics_clustering.labels_
features_audio = [
# Medidas de confianza
#"acousticness",
#"liveness",
#"speechiness",
#"instrumentalness",
# Medidas perceptivas
"energy",
#"loudness",
"danceability",
"valence",
# Descriptores músicales
#"key",
#"mode",
"tempo"
]
df_audio = df_songs[features_audio].copy()
n_components = 2
pca = PCA(n_components=n_components)
pca_audio = pca.fit_transform(df_audio)
#df_audio = pd.DataFrame(data=pca_audio, columns=list(range(n_components)))
scaler = MinMaxScaler()
x_scaled = scaler.fit_transform(df_audio.values)
df_audio_scaled = pd.DataFrame(x_scaled, columns=df_audio.columns)
inertia = []
sil = []
k_list = range(2, 11)
for k in k_list:
km = KMeans(n_clusters=k, random_state=0)
km.fit(df_audio_scaled)
inertia.append(km.inertia_)
sil.append(silhouette_score(df_audio_scaled, km.labels_))
plt.figure(figsize=(8, 6))
sns.lineplot(x=k_list, y=inertia, marker='o', color=palette[2])
plt.xlim(min(k_list) - max(k_list) * 0.05, max(k_list) * 1.05)
plt.ylim(min(inertia) - max(inertia) * 0.05, max(inertia) * 1.05)
plt.xlabel('Clusters')
plt.ylabel('Inercia')
plt.title('Inercia por número de clusters')
plt.show()
plt.figure(figsize=(8, 6))
sns.lineplot(x=k_list, y=sil, marker='o', color=palette[3])
plt.xlim(min(k_list) - max(k_list) * 0.05, max(k_list) * 1.05)
plt.ylim(min(sil) - max(sil) * 0.05, max(sil) * 1.05)
plt.xlabel('Clusters')
plt.ylabel('Silhouette media')
plt.title('Silhouette media para el dataset')
plt.show()
audio_clustering = KMeans(n_clusters=4, random_state=0)
audio_clustering.fit(df_audio_scaled)
audio_clusters = audio_clustering.labels_
df_songs[['sentiment', 'emotion']] = df_lyrics[['sentiment', 'emotion']]
df_songs['audio_clusters'] = audio_clusters
df_songs['lyrics_clusters'] = lyrics_clusters
crosstab = pd.crosstab(df_songs['audio_clusters'],
df_songs['lyrics_clusters'],
normalize='columns')
plt.figure(figsize=(8, 5))
sns.heatmap(
data=crosstab,
cbar=False,
square=True,
annot=True,
fmt= '.0%',
annot_kws={'size': 14},
xticklabels=crosstab.columns,
yticklabels=crosstab.index,
vmin=0,
vmax=None,
cmap=LinearSegmentedColormap.from_list('cmap', [
'#ffffff',
palette[0]
], N=256, gamma=1.0)
)
plt.title('Correlación entre clusters')
plt.xlabel('Clusters de la letras')
plt.ylabel('Clusters del audio')
plt.xticks(rotation=0)
plt.yticks(rotation=0)
plt.show()
for c_audio in range(df_songs.audio_clusters.max()+1):
cluster = df_songs.query('audio_clusters==@c_audio')
print(f'Cluster {c_audio} de audio')
print(f'\tEnergía: {cluster.energy.mean():.2f} (+/-{cluster.energy.std():.2f})')
print(f'\tTempo: {cluster.tempo.mean():.2f} (+/-{cluster.tempo.std():.2f})')
print(f'\tBailabilidad: {cluster.danceability.mean():.2f} (+/-{cluster.danceability.std():.2f})')
print(f'\tValencia: {cluster.valence.mean():.2f} (+/-{cluster.valence.std():.2f})\n')
Cluster 0 de audio
Energía: 0.59 (+/-0.10)
Tempo: 102.21 (+/-16.80)
Bailabilidad: 0.75 (+/-0.11)
Valencia: 0.32 (+/-0.14)
Cluster 1 de audio
Energía: 0.62 (+/-0.11)
Tempo: 172.30 (+/-14.89)
Bailabilidad: 0.65 (+/-0.09)
Valencia: 0.41 (+/-0.20)
Cluster 2 de audio
Energía: 0.67 (+/-0.10)
Tempo: 142.35 (+/-16.86)
Bailabilidad: 0.83 (+/-0.06)
Valencia: 0.72 (+/-0.16)
Cluster 3 de audio
Energía: 0.78 (+/-0.07)
Tempo: 95.64 (+/-13.68)
Bailabilidad: 0.74 (+/-0.10)
Valencia: 0.66 (+/-0.15)
stopwords = nltk.corpus.stopwords.words('spanish')
stopwords.append('yeah')
for c_lyric in range(df_songs.lyrics_clusters.max()+1):
cluster = df_songs.query('lyrics_clusters==@c_lyric')
all_words = nltk.tokenize.word_tokenize(
' '.join(list(cluster.lyrics_clean.str.lower())))
all_words_dist = nltk.FreqDist(
w.lower() for w in all_words
if (w not in stopwords) and (len(w) > 3))
most_common = [x[0] for x in all_words_dist.most_common(10)]
print(f'Cluster {c_lyric} de la letra')
print(f'\tCanciones explicitas: {cluster.song_is_explicit.mean():.0%}')
print(f'\tSentimiento POS: {cluster.sentiment.mean():.3f} (+/-{cluster.sentiment.std():.3f})')
print(f'\tEmoción: {cluster.emotion.mode()[0]}')
print(f'\tKeywords: {", ".join(most_common)}\n')
Cluster 0 de la letra
Canciones explicitas: 100%
Sentimiento POS: 0.034 (+/-0.027)
Emoción: joy
Keywords: siempre, baby, noche, quiero, sabe, quiere, ahora, nunca, romperla, conmigo
Cluster 1 de la letra
Canciones explicitas: 76%
Sentimiento POS: 0.018 (+/-0.014)
Emoción: anger
Keywords: rico, cabrón, puerto, gusta, chocha, ahora, quiero, odio, ¿qué, quiere
Cluster 2 de la letra
Canciones explicitas: 0%
Sentimiento POS: 0.120 (+/-0.210)
Emoción: joy
Keywords: bien, quiero, dime, party, baby, dale, sale, mami, vida, siempre
Cluster 3 de la letra
Canciones explicitas: 62%
Sentimiento POS: 0.012 (+/-0.007)
Emoción: sadness
Keywords: noche, dime, tiempo, dejaste, amor, baby, quiere, dónde, dando, encima
Cluster 4 de la letra
Canciones explicitas: 86%
Sentimiento POS: 0.015 (+/-0.006)
Emoción: surprise
Keywords: hola, toca, siempre, noche, quiero, casi, ¿quién, olvidó, baby, hace
for c_audio in range(df_songs.audio_clusters.max()+1):
for c_lyric in range(df_songs.lyrics_clusters.max()+1):
print(f'Cluster {c_audio}-{c_lyric}')
cluster = (
df_songs.query('audio_clusters==@c_audio & lyrics_clusters==@c_lyric')
[['song_name', 'song_preview_url']]
)
try:
cluster_sample = cluster.sample(5, random_state=42)
except:
cluster_sample = cluster
#display(df_songs.query('audio_clusters == @c')[features_audio].describe())
for idx, row in cluster_sample.iterrows():
print(row['song_name'], '==>', row['song_preview_url'])
print('-' * 100)
Cluster 0-0
Ser Bichote ==> https://p.scdn.co/mp3-preview/f6851064dd0f12a01f7c186e0b0c59d3873c032d?cid=15500ca1388444ff91e425a3e0815156
La Corriente ==> https://p.scdn.co/mp3-preview/5b652748ee9a5a10433e28fa0f63f365b8bf58c2?cid=15500ca1388444ff91e425a3e0815156
200 Mph ==> https://p.scdn.co/mp3-preview/5bc2bc152f29a7879772fc8c3e50402478a99464?cid=15500ca1388444ff91e425a3e0815156
Yo No Soy Celoso ==> https://p.scdn.co/mp3-preview/8b867e2dbbeb7af42c1efbc76d29d186a8ca4a61?cid=15500ca1388444ff91e425a3e0815156
Solo de Mi ==> https://p.scdn.co/mp3-preview/f7e44d65287ea4ce95f36fb99cd93964a49f2fe5?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 0-1
Está Cabrón Ser Yo ==> https://p.scdn.co/mp3-preview/0859f08bb1b6a80c229ae4930b105ce262da94f7?cid=15500ca1388444ff91e425a3e0815156
Tenemos Que Hablar ==> https://p.scdn.co/mp3-preview/558e92a5b765d4fe61f598905883c096cfb290ef?cid=15500ca1388444ff91e425a3e0815156
ODIO ==> https://p.scdn.co/mp3-preview/02f6bdcc59432b6fa1e84d4e2607af53ad048bc4?cid=15500ca1388444ff91e425a3e0815156
BYE ME FUI ==> https://p.scdn.co/mp3-preview/19d2b4b72f5cb2be0bd3ba0ae02dc3f6f9803fac?cid=15500ca1388444ff91e425a3e0815156
Caro ==> https://p.scdn.co/mp3-preview/2fd0d96e9db7160350067d885d2660664f425493?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 0-2
YO LE LLEGO ==> https://p.scdn.co/mp3-preview/885bf8de78fac485be251c8886721f84e469132c?cid=15500ca1388444ff91e425a3e0815156
MOJAITA ==> https://p.scdn.co/mp3-preview/4647dd187d6d6854c6ff67ec5a2735d329804b47?cid=15500ca1388444ff91e425a3e0815156
Un Coco ==> https://p.scdn.co/mp3-preview/59a2830f61c6874453500a324d3739926b23ac84?cid=15500ca1388444ff91e425a3e0815156
Hablamos Mañana ==> https://p.scdn.co/mp3-preview/99d4baf77eddf06e4d7f36241cf31326f25810dc?cid=15500ca1388444ff91e425a3e0815156
EN CASITA ==> https://p.scdn.co/mp3-preview/e7c4218c6f2c1af3253aa3fa7eced43a7c6a2089?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 0-3
Un Verano Sin Ti ==> https://p.scdn.co/mp3-preview/1fb5a3fdf9beb04f853b95fe8b99a8698b8aa70c?cid=15500ca1388444ff91e425a3e0815156
HACIENDO QUE ME AMAS ==> https://p.scdn.co/mp3-preview/de1744c8e57650074a4166f233841b17c36e0117?cid=15500ca1388444ff91e425a3e0815156
Si Estuviésemos Juntos ==> https://p.scdn.co/mp3-preview/fd4b1363c44ad03c9931015511446e6d17a77812?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 0-4
120 ==> https://p.scdn.co/mp3-preview/582661b89f7a110b3c312fe5076dee369d1d7e65?cid=15500ca1388444ff91e425a3e0815156
La Difícil ==> https://p.scdn.co/mp3-preview/9f0ba371413d258d4f772f1d996acaa83894a4af?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 1-0
Una Vez ==> https://p.scdn.co/mp3-preview/950a45e1ec69518f546b40925e08d35fc0b1923d?cid=15500ca1388444ff91e425a3e0815156
NI BIEN NI MAL ==> https://p.scdn.co/mp3-preview/1a1121547c456c50e535a451bd9415b960958db6?cid=15500ca1388444ff91e425a3e0815156
Moscow Mule ==> https://p.scdn.co/mp3-preview/585736d2d4dba1eef13b6d63c54dd4c16a3275fb?cid=15500ca1388444ff91e425a3e0815156
Otra Noche en Miami ==> https://p.scdn.co/mp3-preview/290c7755a64f9797a8168464150afa3931b5d48f?cid=15500ca1388444ff91e425a3e0815156
PA' ROMPERLA ==> https://p.scdn.co/mp3-preview/acbeb16d2a372881639cfe7f6eef580ffba8cb11?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 1-1
UN PESO ==> https://p.scdn.co/mp3-preview/f318e0b0fa5077eb0e486c16b119c8e6eb3cc04c?cid=15500ca1388444ff91e425a3e0815156
QUE PRETENDES ==> https://p.scdn.co/mp3-preview/aa4a4ef8e4b0adf2163c343e04b5ffddef30ae4b?cid=15500ca1388444ff91e425a3e0815156
El Apagón ==> https://p.scdn.co/mp3-preview/8ef3103b46993ed56f0b0ca5918a046fc828260f?cid=15500ca1388444ff91e425a3e0815156
Safaera ==> https://p.scdn.co/mp3-preview/161fd43b212f9ed0077665c7cb73d2a5469c1527?cid=15500ca1388444ff91e425a3e0815156
RONCA FREESTYLE ==> https://p.scdn.co/mp3-preview/28555ef5e3fb3acb27979cab619591a0f1d93e8b?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 1-2
Dos Mil 16 ==> https://p.scdn.co/mp3-preview/32b762468315fd6a22d2efaaf05d5b2e50d543bb?cid=15500ca1388444ff91e425a3e0815156
Si Veo a Tu Mamá ==> https://p.scdn.co/mp3-preview/b7ee6f127e09252d6897c38503f9558f1dceaf66?cid=15500ca1388444ff91e425a3e0815156
Después de la Playa ==> https://p.scdn.co/mp3-preview/9dddf0cfb0bd4f88b4ec64bf547cd66e569fbf96?cid=15500ca1388444ff91e425a3e0815156
Estamos Bien ==> https://p.scdn.co/mp3-preview/340843267ba2f3b98f1578304e3204fc27fb70f7?cid=15500ca1388444ff91e425a3e0815156
Agosto ==> https://p.scdn.co/mp3-preview/d1c87642a15006e6b55ef84b2e63d3e23fe8bd0a?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 1-3
TE MUDASTE ==> https://p.scdn.co/mp3-preview/8f409c1992e7834a1c5f3b3762ba8b79d02bde11?cid=15500ca1388444ff91e425a3e0815156
Que Malo ==> https://p.scdn.co/mp3-preview/67cfde496db215886dc2112be95314b71075d06d?cid=15500ca1388444ff91e425a3e0815156
LA NOCHE DE ANOCHE ==> https://p.scdn.co/mp3-preview/d843fb10f6c38bbe76680b47e8679bbe31650c19?cid=15500ca1388444ff91e425a3e0815156
Un Ratito ==> https://p.scdn.co/mp3-preview/62c5c24c4264ce0788d1655686a4eca459c82b11?cid=15500ca1388444ff91e425a3e0815156
Soliá ==> https://p.scdn.co/mp3-preview/3bcbb13f70e54ec587862286a6efe467598b5480?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
Cluster 1-4
MÁS DE UNA CITA ==> https://p.scdn.co/mp3-preview/6000f2613360c584cfeb9bf0a70e8009cb9f7976?cid=15500ca1388444ff91e425a3e0815156
RLNDT ==> https://p.scdn.co/mp3-preview/1a2b8e40af7152e28b964e1052d90d101d5f723b?cid=15500ca1388444ff91e425a3e0815156
CÓMO SE SIENTE - Remix ==> nan
Efecto ==> https://p.scdn.co/mp3-preview/10a488c0e8056119b63f2d51e6507a6c1ac4235b?cid=15500ca1388444ff91e425a3e0815156
Bichiyal ==> https://p.scdn.co/mp3-preview/34928a1005d07a06a768ea1f352c1a8dfed038df?cid=15500ca1388444ff91e425a3e0815156
----------------------------------------------------------------------------------------------------
df_complete = pd.concat([df_audio_scaled, df_lyrics_pre_scaled], axis=1)
n_components = 4
pca = PCA(n_components=n_components)
pca_complete = pca.fit_transform(df_complete)
inertia = []
sil = []
k_list = range(2, 11)
for k in k_list:
km = KMeans(n_clusters=k, random_state=0)
km.fit(pca_complete)
inertia.append(km.inertia_)
sil.append(silhouette_score(pca_complete, km.labels_))
plt.figure(figsize=(8, 6))
sns.lineplot(x=k_list, y=inertia, marker='o', color=palette[2])
plt.xlim(min(k_list) - max(k_list) * 0.05, max(k_list) * 1.05)
plt.ylim(min(inertia) - max(inertia) * 0.05, max(inertia) * 1.05)
plt.xlabel('Clusters')
plt.ylabel('Inercia')
plt.title('Inercia por número de clusters')
plt.show()
plt.figure(figsize=(8, 6))
sns.lineplot(x=k_list, y=sil, marker='o', color=palette[3])
plt.xlim(min(k_list) - max(k_list) * 0.05, max(k_list) * 1.05)
plt.ylim(min(sil) - max(sil) * 0.05, max(sil) * 1.05)
plt.xlabel('Clusters')
plt.ylabel('Silhouette media')
plt.title('Silhouette media para el dataset')
plt.show()
k_full = 4
km = KMeans(n_clusters=k_full, random_state=0)
km.fit(pca_complete)
full_clusters = km.labels_
df_songs['full_clusters'] = full_clusters
stopwords = nltk.corpus.stopwords.words('spanish')
stopwords.append('yeah')
for c_full in range(df_songs.full_clusters.max()+1):
cluster = df_songs.query('full_clusters==@c_full')
all_words = nltk.tokenize.word_tokenize(
' '.join(list(cluster.lyrics_clean.str.lower())))
all_words_dist = nltk.FreqDist(
w.lower() for w in all_words
if (w not in stopwords) and (len(w) > 3))
most_common = [x[0] for x in all_words_dist.most_common(10)]
print(f'Cluster {c_full}')
print(f'\tNúmero de canciones: {len(cluster)}')
print(f'\tCanciones explicitas: {cluster.song_is_explicit.mean():.0%}')
print(f'\tSentimiento POS: {cluster.sentiment.mean():.3f}')
print(f'\tEmoción: {cluster.emotion.mode()[0]}')
print(f'\tEnergía: {cluster.energy.mean():.2f}')
print(f'\tTempo: {cluster.tempo.mean():.2f}')
print(f'\tBailabilidad: {cluster.danceability.mean():.2f}')
print(f'\tValencia: {cluster.valence.mean():.2f}')
print(f'\tKeywords: {", ".join(most_common)}')
print(f'\tAlbum: {cluster.album_name.mode()[0]}')
print(f'\tEjemplares:')
try:
cluster_sample = cluster.sample(10, random_state=42)
except:
cluster_sample = cluster
finally:
for idx, row in cluster_sample.iterrows():
print(f'\t\t{row["song_name"]} ==> {row["song_preview_url"]}')
print('-'*150)
Cluster 0
Número de canciones: 23
Canciones explicitas: 100%
Sentimiento POS: 0.034
Emoción: joy
Energía: 0.67
Tempo: 121.29
Bailabilidad: 0.75
Valencia: 0.49
Keywords: siempre, baby, noche, quiero, sabe, quiere, ahora, nunca, romperla, conmigo
Album: X 100PRE
Ejemplares:
P FKN R ==> https://p.scdn.co/mp3-preview/608f3e482c7505a9c6519c93669da272a54fb1ae?cid=15500ca1388444ff91e425a3e0815156
PA' ROMPERLA ==> https://p.scdn.co/mp3-preview/acbeb16d2a372881639cfe7f6eef580ffba8cb11?cid=15500ca1388444ff91e425a3e0815156
Moscow Mule ==> https://p.scdn.co/mp3-preview/585736d2d4dba1eef13b6d63c54dd4c16a3275fb?cid=15500ca1388444ff91e425a3e0815156
CANCIÓN CON YANDEL ==> https://p.scdn.co/mp3-preview/502b2bbed9899250999344041c0f06385f0366da?cid=15500ca1388444ff91e425a3e0815156
200 Mph ==> https://p.scdn.co/mp3-preview/5bc2bc152f29a7879772fc8c3e50402478a99464?cid=15500ca1388444ff91e425a3e0815156
La Zona ==> https://p.scdn.co/mp3-preview/0a5f12b0730a2ab2f2b1edb6cadcffa1215b9017?cid=15500ca1388444ff91e425a3e0815156
Me Porto Bonito ==> https://p.scdn.co/mp3-preview/8513b15fb5b39966d8eccaa7b8c050c7a570e6e8?cid=15500ca1388444ff91e425a3e0815156
Una Vez ==> https://p.scdn.co/mp3-preview/950a45e1ec69518f546b40925e08d35fc0b1923d?cid=15500ca1388444ff91e425a3e0815156
HOY COBRÉ ==> https://p.scdn.co/mp3-preview/76d39b834af228e380221ddc534ef5e37466646e?cid=15500ca1388444ff91e425a3e0815156
Yo No Soy Celoso ==> https://p.scdn.co/mp3-preview/8b867e2dbbeb7af42c1efbc76d29d186a8ca4a61?cid=15500ca1388444ff91e425a3e0815156
------------------------------------------------------------------------------------------------------------------------------------------------------
Cluster 1
Número de canciones: 17
Canciones explicitas: 76%
Sentimiento POS: 0.018
Emoción: anger
Energía: 0.68
Tempo: 119.57
Bailabilidad: 0.74
Valencia: 0.55
Keywords: rico, cabrón, puerto, gusta, chocha, ahora, quiero, odio, ¿qué, quiere
Album: EL ÚLTIMO TOUR DEL MUNDO
Ejemplares:
El Apagón ==> https://p.scdn.co/mp3-preview/8ef3103b46993ed56f0b0ca5918a046fc828260f?cid=15500ca1388444ff91e425a3e0815156
MALDITA POBREZA ==> https://p.scdn.co/mp3-preview/56ef5327290cc3514ecc1e381f7d7335e927553e?cid=15500ca1388444ff91e425a3e0815156
BYE ME FUI ==> https://p.scdn.co/mp3-preview/19d2b4b72f5cb2be0bd3ba0ae02dc3f6f9803fac?cid=15500ca1388444ff91e425a3e0815156
Tenemos Que Hablar ==> https://p.scdn.co/mp3-preview/558e92a5b765d4fe61f598905883c096cfb290ef?cid=15500ca1388444ff91e425a3e0815156
QUE PRETENDES ==> https://p.scdn.co/mp3-preview/aa4a4ef8e4b0adf2163c343e04b5ffddef30ae4b?cid=15500ca1388444ff91e425a3e0815156
Caro ==> https://p.scdn.co/mp3-preview/2fd0d96e9db7160350067d885d2660664f425493?cid=15500ca1388444ff91e425a3e0815156
Ignorantes ==> https://p.scdn.co/mp3-preview/012fea3bbc2e414af606c6658dcbf546240e3996?cid=15500ca1388444ff91e425a3e0815156
ODIO ==> https://p.scdn.co/mp3-preview/02f6bdcc59432b6fa1e84d4e2607af53ad048bc4?cid=15500ca1388444ff91e425a3e0815156
TE DESEO LO MEJOR ==> https://p.scdn.co/mp3-preview/34463111824bafed4f242c6961d3e028e4bd4d92?cid=15500ca1388444ff91e425a3e0815156
Safaera ==> https://p.scdn.co/mp3-preview/161fd43b212f9ed0077665c7cb73d2a5469c1527?cid=15500ca1388444ff91e425a3e0815156
------------------------------------------------------------------------------------------------------------------------------------------------------
Cluster 2
Número de canciones: 37
Canciones explicitas: 0%
Sentimiento POS: 0.120
Emoción: joy
Energía: 0.67
Tempo: 120.64
Bailabilidad: 0.76
Valencia: 0.55
Keywords: bien, quiero, dime, party, baby, dale, sale, mami, vida, siempre
Album: Un Verano Sin Ti
Ejemplares:
ANTES QUE SE ACABE ==> https://p.scdn.co/mp3-preview/665d674dcc64c6dc857ab776ec6b0c0c245325aa?cid=15500ca1388444ff91e425a3e0815156
Callaita ==> https://p.scdn.co/mp3-preview/dd0a3623872d32ea48af810f08963cd37f7c01a0?cid=15500ca1388444ff91e425a3e0815156
Party ==> https://p.scdn.co/mp3-preview/4982ab22ba534ab700ddc1f2f9ac143baaad7e24?cid=15500ca1388444ff91e425a3e0815156
MOJAITA ==> https://p.scdn.co/mp3-preview/4647dd187d6d6854c6ff67ec5a2735d329804b47?cid=15500ca1388444ff91e425a3e0815156
Estamos Bien ==> https://p.scdn.co/mp3-preview/340843267ba2f3b98f1578304e3204fc27fb70f7?cid=15500ca1388444ff91e425a3e0815156
A Tu Merced ==> https://p.scdn.co/mp3-preview/a96ded059d0a200ff20673b795bb1c13d684d356?cid=15500ca1388444ff91e425a3e0815156
Enséñame a Bailar ==> https://p.scdn.co/mp3-preview/9ad4b3376360b901cc86b2abaf01803eead0a810?cid=15500ca1388444ff91e425a3e0815156
25/8 ==> https://p.scdn.co/mp3-preview/fd38e03acdb167a1320afb098549e5a00b2fcf20?cid=15500ca1388444ff91e425a3e0815156
Yo Perreo Sola ==> https://p.scdn.co/mp3-preview/fde719104e8c3b857d681ce7828fab846c1255fe?cid=15500ca1388444ff91e425a3e0815156
TRELLAS ==> https://p.scdn.co/mp3-preview/21b9a51efa9ecd7d5f11c3d802f0dadbcc2ae7b4?cid=15500ca1388444ff91e425a3e0815156
------------------------------------------------------------------------------------------------------------------------------------------------------
Cluster 3
Número de canciones: 15
Canciones explicitas: 73%
Sentimiento POS: 0.013
Emoción: sadness
Energía: 0.62
Tempo: 121.37
Bailabilidad: 0.72
Valencia: 0.37
Keywords: noche, baby, dime, siempre, toca, casi, tiempo, perreo, loca, quiere
Album: EL ÚLTIMO TOUR DEL MUNDO
Ejemplares:
La Difícil ==> https://p.scdn.co/mp3-preview/9f0ba371413d258d4f772f1d996acaa83894a4af?cid=15500ca1388444ff91e425a3e0815156
Soliá ==> https://p.scdn.co/mp3-preview/3bcbb13f70e54ec587862286a6efe467598b5480?cid=15500ca1388444ff91e425a3e0815156
Un Ratito ==> https://p.scdn.co/mp3-preview/62c5c24c4264ce0788d1655686a4eca459c82b11?cid=15500ca1388444ff91e425a3e0815156
Si Estuviésemos Juntos ==> https://p.scdn.co/mp3-preview/fd4b1363c44ad03c9931015511446e6d17a77812?cid=15500ca1388444ff91e425a3e0815156
HACIENDO QUE ME AMAS ==> https://p.scdn.co/mp3-preview/de1744c8e57650074a4166f233841b17c36e0117?cid=15500ca1388444ff91e425a3e0815156
CÓMO SE SIENTE - Remix ==> nan
Un Verano Sin Ti ==> https://p.scdn.co/mp3-preview/1fb5a3fdf9beb04f853b95fe8b99a8698b8aa70c?cid=15500ca1388444ff91e425a3e0815156
Efecto ==> https://p.scdn.co/mp3-preview/10a488c0e8056119b63f2d51e6507a6c1ac4235b?cid=15500ca1388444ff91e425a3e0815156
RLNDT ==> https://p.scdn.co/mp3-preview/1a2b8e40af7152e28b964e1052d90d101d5f723b?cid=15500ca1388444ff91e425a3e0815156
LA NOCHE DE ANOCHE ==> https://p.scdn.co/mp3-preview/d843fb10f6c38bbe76680b47e8679bbe31650c19?cid=15500ca1388444ff91e425a3e0815156
------------------------------------------------------------------------------------------------------------------------------------------------------