Data Science, nawet w muzyce.

Jan 23 2023
Testując Spotify API znalazłem wiele funkcji ukrytych za piosenkami, których słuchamy na co dzień. Oto projekt, ale wolę pokazać go w tym artykule.

Testując Spotify API znalazłem wiele funkcji ukrytych za piosenkami, których słuchamy na co dzień.

Oto projekt, ale wolę pokazać go w tym artykule.https://github.com/ivanurquiza/Spotify-Analysis

Wpadłem na pomysł przeanalizowania najczęściej słuchanych w tej chwili piosenek.

Należy odpowiedzieć na dwa główne pytania:
1. Co jest charakterystyczne dla popularnej muzyki?
2. Czym różni się od muzyki staroświeckiej?

# FILL IN THESE VALUES WITH YOUR OWN KEYS
client_id = ...

client_secret = ...

# Make sure to add this on "Edit Settings" in your Dashboard
redirect_uri = "http://localhost:9000"

import spotipy
from spotipy.oauth2 import SpotifyOAuth

# Connect with API Keys created earlier
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id=client_id,
                                              client_secret=client_secret,
                                              redirect_uri=redirect_uri))

Musiałem dokonać wyboru. W tym przypadku wykorzystałem bardzo znaną w Argentynie playlistę stworzoną przez Spotify, która zawiera 50 najczęściej słuchanych utworów w tej chwili:

#Playlist ID
TOP50_playlist = sp.playlist('37i9dQZEVXbMMy2roB9myp')

for track_data in TOP50_playlist['tracks']['items']:
    
    #Getting just the ID (last part of the url)
    track_id = track_data['track']['uri'].split(':')[-1]
    track_name = track_data['track']['name']
    track_popularity = track_data['track']['popularity']
    list_tracksids.append((track_name,track_id,track_popularity))

df_features = pd.DataFrame()

for idx, dict_trackfeatures in enumerate(sp.audio_features(tracks=[url[1] for url in list_tracksids])):
    dict_trackfeatures['track_name'] = list_tracksids[idx][0]
    dict_trackfeatures['popularity'] = list_tracksids[idx][2]
    df_features = pd.concat([df_features,pd.Series(dict_trackfeatures)], axis=1)

                      #Selects only useful features
df_features=df_features.transpose()[['track_name','popularity','danceability','energy','loudness','speechiness','acousticness','instrumentalness','liveness','tempo']]

#Display Top 5 songs
df_features.head()

Jesteśmy gotowi. Przeanalizujmy dane!

Narysujmy kilka wykresów, aby uzyskać pierwsze wzorce dotyczące najpopularniejszej muzyki.

Z wykresu po lewej stronie widać, że muzyka, która składa się na naszą playlistę TOP 50, znajduje się głównie w drugiej połowie (ponad 0,5 z 1), co najwyraźniej oznacza, że ​​jest bardzo taneczna. Skąd możemy wiedzieć, czy to podejrzenie jest prawdziwe?

Porównajmy średnią taneczność tych utworów z muzyką klasyczną. Witaj Mozarcie!

import seaborn as sns
import matplotlib.pyplot as plt

# Let's plot some charts for getting the first patterns about the most popular music

sns.set_theme(context='notebook',style="ticks")
fig, axes = plt.subplots(1, 2, figsize=(10,5))

# Left - Danceability's histogram with kde
sns.distplot(ax=axes[0], x=df_features["danceability"], bins=6, kde=True, color="#5B3BA0")

axes[0].set(xlabel='Danceability', ylabel='Frequency')
axes[0].set_title('TOP 50 Danceability freq.')

# Right - Comparation between the playlist avarage and Mozart's most popular song

                                              # Requiem, K. 626: Lacrimosa
mozart_danceability = sp.audio_features(tracks=['3oHSL6pt9LpNrQZuQGu9wL'])[0]['danceability']

avg_top50_danceability = df_features['danceability'].mean()

sns.barplot(ax= axes[1],
            x = 'who',
            y = 'Danceability',
            data = {'who':['TOP 50','MOZART'],
                   'Danceability':[avg_top50_danceability, mozart_danceability]},
            palette=['#5B3BA0','#A378FF']
            )

axes[1].set(ylabel='Danceability')
axes[1].set_title('Mozart vs TOP 50')

plt.show()

      
                

Jak możemy sobie wyobrazić, Mozarta nie ma na liście 50 najczęściej odtwarzanych utworów. W oparciu o naszą krótką analizę, Mozart może nie mieć wystarczających zdolności tanecznych, aby znaleźć się na liście odtwarzania.

  • Taneczność Mozarta: 0,162
  • Średnia taneczności TOP 50: 0,739

#Adding Mozart's song variables to our dataframe
mozart = pd.DataFrame({'track_name':['Mozart'], 'popularity':[0.0]})
for k,v in sp.audio_features(tracks=['3oHSL6pt9LpNrQZuQGu9wL'])[0].items():
    if k in df_features.keys():
        mozart[k]=v

df_features = pd.concat([df_features,mozart], axis=0)
df_features['playlist'] = ['TOP 50' if x != 'Mozart' else 'Mozart' for x in df_features['track_name']] 
                           
df_features.tail(1)

      
                
#Scatter plot plt.figure(dpi=80) sns.scatterplot( data=df_features, x='acousticness',y='loudness', hue="playlist", sizes=(20, 200), legend="full", palette=['#5B3BA0','#D00000'] ) plt.show()

Ten wykres częściowo pomaga zrozumieć, dlaczego Mozart nie jest modny. Utwory w Top 50 mają zbliżone wartości zmiennych „głośność” i „akustyka” i są silnie skoncentrowane w lewej górnej części wykresu. Trend jest wyraźny: wysoka głośność i niski poziom akustyki. Z drugiej strony Mozart idzie wbrew trendowi!

Te same kroki, inna lista odtwarzania

A gdyby zamiast Mozarta i modnych piosenek wziąć playlistę z najlepszymi utworami lat 80.?

Zróbmy to samo, ale trochę inaczej.

AllOut80s = sp.playlist('37i9dQZF1DX4UtSsGT1Sbe')
list_tracksids = list()

for track_data in AllOut80s['tracks']['items']:
    
    #Gets just the ID (last part of the url)
    track_id = track_data['track']['uri'].split(':')[-1]
    track_name = track_data['track']['name']
    track_popularity = track_data['track']['popularity']
    list_tracksids.append((track_name,track_id,track_popularity))

AllOut80s_features = pd.DataFrame()

for idx, dict_trackfeatures in enumerate(sp.audio_features(tracks=[url[1] for url in list_tracksids])):
    dict_trackfeatures['track_name'] = list_tracksids[idx][0]
    dict_trackfeatures['popularity'] = list_tracksids[idx][2]
    AllOut80s_features = pd.concat([AllOut80s_features,pd.Series(dict_trackfeatures)], axis=1)



                           #Selects only useful features
AllOut80s_features=AllOut80s_features.transpose()[['track_name','popularity','danceability','energy','loudness','speechiness','acousticness','instrumentalness','liveness','tempo']]
AllOut80s_features['playlist'] = 'AllOut80s'

#Handles data types
AllOut80s_features[['danceability','energy','loudness','speechiness','acousticness','instrumentalness','liveness','tempo']]=AllOut80s_features[['danceability','energy','loudness','speechiness','acousticness','instrumentalness','liveness','tempo']].astype(float)

Zobacz wyniki:

# Setting up concatenated df
df_allsongs = pd.concat([df_features, AllOut80s_features[:50]]).reset_index()

df_allsongs= df_allsongs.drop(df_allsongs.index[df_allsongs['playlist'] == 'Mozart'])

plt.figure(dpi=80)


sns.lmplot(
    data=df_allsongs, x='acousticness',y='loudness', hue="playlist",
    legend="full", palette=['#5B3BA0','#D00000']
)

plt.show()

      
                

Patrząc na rozkłady i linie regresji dla każdej playlisty, zauważamy, że trendy są zupełnie inne. Chociaż związek między zmiennymi (nachylenie krzywych) pozostaje dość podobny, utwory wydają się być ułożone we wzór, a playlista Top 50 ma wyższy poziom „głośności”.

Wystarczy przyjrzeć się tym dwóm cechom, by przekonać się, że muzyka lat 80. jest bardziej zbliżona do aktualnych trendów niż Mozart.

Pójdźmy z projektem o krok dalej!

Czy możemy przewidzieć, biorąc pod uwagę wszystkie możliwe zmienne, do której playlisty należy utwór? Nie jestem pewien, ale odpowiedź brzmi prawdopodobnie KNeighbours .

Wyszukałem w Google najbardziej reprezentatywną piosenkę z lat 80., aby użyć jej jako danych wejściowych do naszej prognozy modelowania:

Features used for modeling
featlist=['danceability','energy','loudness','speechiness','acousticness','instrumentalness','liveness','tempo']

prince = {}
for k,v in sp.audio_features(tracks=['54X78diSLoUDI3joC2bjMz'])[0].items():
    if k in featlist:
        prince[k]=v

prince = pd.DataFrame(prince.items()).transpose()
prince.columns = prince.iloc[0]
prince=prince[1:]
prince[featlist] = prince[featlist].astype(float)
prince

      
                
from sklearn.neighbors import KNeighborsClassifier knn = KNeighborsClassifier(n_neighbors=3) X = df_allsongs[featlist] y = df_allsongs['playlist'] knn.fit(X,y) # Prediction prediction = knn.predict(prince) print('The song could be included in the playlist: ', prediction[0])

Dziękuje za przeczytanie!

Kontakt : www.linkedin.com/in/ivanroblesurquiza

Twitter :https://twitter.com/urqivo