🎵

世界のSpotify「週間トップ50」をPythonで分析

2021/09/09に公開

Spotify Web APIについて

「Spotipy」は、Spotify Web APIのための軽量なPythonライブラリです。
今回はSpotipyを用いて、世界のSpotify「週間トップ50」をPythonで分析します。
https://spotipy.readthedocs.io/en/2.19.0/

Spotifyで得られる各楽曲の特徴量(Audio Features)

https://developer.spotify.com/documentation/web-api/reference/#object-audiofeaturesobject

AudioFeatures 概要
acousticness アコースティック感
danceability 踊りやすさ
duration_ms 曲の長さ
energy エネルギッシュさ
instrumentalness ボーカルが含まれていないことの確信度
liveness ライブ感
loudness 音の大きさ
speechiness スピーチ感
tempo テンポ
valence ポジティブさ

Spotipyの準備

①APIの登録を行う

https://developer.spotify.com/dashboard/
APIを使用するためには、「Client ID」 と「Client Secret」が必要です。
※これらのトークンは流出させないよう注意してくだいさい。

②Python環境にspotipyパッケージをpip Installする

https://pypi.org/project/spotipy/

pip install spotipy

各国の週間トップ50曲を収集し、国ごとの特徴を分析

ソースコード一覧(Jupyter Notebook)は⇓で公開しています。
https://github.com/kubokoHappy/analytics_spopify_top_50

前準備

import yaml
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import yaml
from pprint import pprint
from tqdm.notebook import tqdm
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
with open('./secret.yaml') as f:
    secret = yaml.safe_load(f)
sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials(
                                                    client_id=secret['SPOTIFY_API_CLIENT_ID'], 
                                                    client_secret=secret['SPOTIFY_API_CLIENT_SECRET']), 
                    language='ja')

週間トップ50曲を取得

weekly_top_playlist_ids_dict = dict(
    Global='37i9dQZEVXbNG2KDcFcKOF',  # グローバル
    Japan='37i9dQZEVXbKqiTGXuCOsB',  # 日本
    Korea='37i9dQZEVXbJZGli0rRP3r',  # 韓国
    America='37i9dQZEVXbLp5XoPON0wI',  # アメリカ
    Italy='37i9dQZEVXbJUPkgaWZcWG',  # イタリア
    India='37i9dQZEVXbMWDif5SCBJq',  # インド
    Brazil='37i9dQZEVXbKzoK95AbRy9',  # ブラジル
)
def get_playlist_tracks(playlist_ids:dict, playlist_len:int):

    tracks_df = pd.DataFrame(np.zeros((playlist_len*len(playlist_ids.keys()), 5)), \
                             columns=['country', 'weekly_rank', 'artist', 'title', 'uri'])
    for i, country in enumerate(tqdm(playlist_ids.keys())):
        playlist = sp.playlist(playlist_id=playlist_ids[country], market='JP')
        print(playlist['description'])
        tracks = playlist['tracks']['items']
        for j, track in enumerate(tracks):
            idx = (i*50) + j
            track_info = track['track']
            tracks_df.loc[idx, 'country'] = country
            tracks_df.loc[idx, 'weekly_rank'] = int(j+1)
            tracks_df.loc[idx, 'artist'] = track_info['artists'][0]['name']
            tracks_df.loc[idx, 'title'] = track_info['name']
            tracks_df.loc[idx, 'uri'] = track_info['uri']
    return tracks_df
    
tracks_df = get_playlist_tracks(playlist_ids=weekly_top_playlist_ids_dict, playlist_len=50)

各曲の特徴量を取得

def get_playlist_features(tracks_df, use_features_list:list, chunk_n=100):

    tracks_df_idx_list = tracks_df.index.tolist()
    tracks_df_idx_chunk_list = [tracks_df_idx_list[i:i+chunk_n] for i in range(0, len(tracks_df_idx_list), chunk_n)]
    print(f"All:{len(tracks_df_idx_list)} -> ChunkSize:{chunk_n} | ChunkLength:{len(tracks_df_idx_chunk_list)}")

    for idx_chunk in tqdm(tracks_df_idx_chunk_list):
        tmp_df = tracks_df.loc[idx_chunk]
        uri_list = tmp_df.uri.tolist()
        sp_features_list = sp.audio_features(tracks=uri_list)
        for idx, sp_features in zip(idx_chunk, sp_features_list):
            for feature_name in use_features_list:
                tracks_df.loc[idx, feature_name] = sp_features[feature_name]
    return tracks_df

use_features_list = ['danceability', 'energy', 'speechiness', 'acousticness', 
                     'valence', 'tempo', 'liveness', 'duration_ms']
tracks_df = get_playlist_features(tracks_df=tracks_df, use_features_list=use_features_list, chunk_n=100)

プロットするための前準備

# 標準化
tracks_s_df = tracks_df.copy()
tracks_s_df[use_features_list] = tracks_df[use_features_list].apply(lambda x: (x-x.mean())/x.std(), axis=0)
tracks_s_df.head()

# Featuresを縦に結合(SeabornでBoxPlotを描写するため)
features_s_concat_df = pd.DataFrame()
for column_n in use_features_list:
    pick_col_list = [*tracks_s_df.columns.tolist()[0:5], column_n]
    col_df = tracks_s_df.loc[:, pick_col_list]
    col_df.rename(columns={column_n: 'value'}, inplace=True)
    col_df['feature_name'] = column_n
    features_s_concat_df = pd.concat([features_s_concat_df, col_df], axis=0)

各国のトップ50曲の特徴をプロット

fig = plt.figure(figsize=(3*len(weekly_top_playlist_ids_dict), 10))
ax = fig.add_subplot(111)
ax.grid()
ax.set_title('Comparison of the top 50 Spotify weekly rankings in each country.')
ax = sns.boxplot(x='country', y="value", hue='feature_name',  data=features_s_concat_df, palette="Set2", whis=np.inf, linewidth=2)
fig.savefig('./top_50_comparison.pdf')
fig.savefig('./top_50_comparison.jpg')

各国のトップ50曲の特徴について考察

Japan(日本)

他国と比べると、エネルギッシュさ「energy」が高く、楽曲の長さ「duration_ms」が比較的長いことが読み取れる。

Korea(韓国)

上記のグラフだと平均値0の周辺に各特徴量が固まっており、バランスがよい。

America(アメリカ)

各特徴量に大きなばらつきがあり、多種多様な楽曲がランクインしていると推測できる。

Italy(イタリア)

「danceability」、「valence」が高く、「acousticness」が低くなっている。ポップで明るく、陽気な曲が多いことが読み取れる。

India(インド)

曲によってかなりばらつきが見られるが、リズミカルだが比較的落ち着いた曲が多いのではないか。

Brazil(ブラジル)

「energy」「acousticness」の相反する特徴量が高くなっており、特徴量同士の関係を詳細に見ないと曲の特徴をつかめないと痛感した。

Discussion