From 7afa71cf49507fed9754dffc951099c57eb7d702 Mon Sep 17 00:00:00 2001 From: NICOLERICHTER1989 Date: Fri, 15 Oct 2021 01:36:04 +0200 Subject: [PATCH] [lab-unsupervised-learning-intro] - nicole richter --- .cache | 1 + .ipynb_checkpoints/Solutions-checkpoint.ipynb | 2559 +++++++++++++++++ Solutions.ipynb | 2559 +++++++++++++++++ 3 files changed, 5119 insertions(+) create mode 100644 .cache create mode 100644 .ipynb_checkpoints/Solutions-checkpoint.ipynb create mode 100644 Solutions.ipynb diff --git a/.cache b/.cache new file mode 100644 index 0000000..7097613 --- /dev/null +++ b/.cache @@ -0,0 +1 @@ +{"access_token": "BQDwZZMNCdi4Mf8JdR76f9W7vcOoZe8UUZETEOwqzfMtYEuO0U-p3YJTLGDUS5qamu-Fy7NCEugjvgYuRf0", "token_type": "Bearer", "expires_in": 3600, "expires_at": 1634253769} \ No newline at end of file diff --git a/.ipynb_checkpoints/Solutions-checkpoint.ipynb b/.ipynb_checkpoints/Solutions-checkpoint.ipynb new file mode 100644 index 0000000..cd8e959 --- /dev/null +++ b/.ipynb_checkpoints/Solutions-checkpoint.ipynb @@ -0,0 +1,2559 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Lab | Unsupervised learning intro" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Instructions** \n", + "\n", + "It's the moment to perform clustering on the songs you collected. Remember that the ultimate goal of this little project is to improve the recommendations of artists. Clustering the songs will allow the recommendation system to limit the scope of the recommendations to only songs that belong to the same cluster - songs with similar audio features.\n", + "\n", + "The experiments you did with the Spotify API and the Billboard web scraping will allow you to create a pipeline such that when the user enters a song, you:\n", + "\n", + "- Check whether or not the song is in the Billboard Hot 200.\n", + "- Collect the audio features from the Spotify API.\n", + "\n", + "After that, you want to send the Spotify audio features of the submitted song to the clustering model, which should return a cluster number.\n", + "\n", + "We want to have as many songs as possible to create the clustering model, so we will add the songs you collected to a bigger dataset available on Kaggle containing 160 thousand songs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 249, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn import datasets\n", + "\n", + "from bs4 import BeautifulSoup\n", + "import requests\n", + "\n", + "from spotipy.oauth2 import SpotifyClientCredentials\n", + "from tqdm import tqdm_notebook\n", + "import spotipy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import Data" + ] + }, + { + "cell_type": "code", + "execution_count": 250, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Unnamed: 0artistalbum_namealbum_uritrackrelease_dateidsong_uritrack_hrefanalysis_url...loudnessmodespeechinessacousticnessinstrumentalnesslivenessvalencetempoduration_mstime_signature
63026624PixiesLive from MacEwan Hall, University of Calgary,...spotify:album:3aHWofJ19fdj8C43ZV7NQQIsla De Encanta - Live from MacEwan Hall, Univ...2021-04-195wtiSXLdMJZwkeiVUScQcvspotify:track:5wtiSXLdMJZwkeiVUScQcvhttps://api.spotify.com/v1/tracks/5wtiSXLdMJZw...https://api.spotify.com/v1/audio-analysis/5wti......-9.75010.13400.0031700.0011700.84000.268129.2491130674
34583706Fats DominoMy Blue Heavenspotify:album:2P6OFyXNxpIxMZzNlCbrbVI'm Ready1990-01-017gQzF4ZKsRo7C2DY5jMf8rspotify:track:7gQzF4ZKsRo7C2DY5jMf8rhttps://api.spotify.com/v1/tracks/7gQzF4ZKsRo7...https://api.spotify.com/v1/audio-analysis/7gQz......-10.00710.05160.6820000.0004260.11400.968172.2851250404
15271613Andy MacphersonLive At The Isle Of Wight Festival 1970spotify:album:1FUZJRZ4eOHT4MJuRh5fh8Christmas - Live1996-10-293ISDIWbHRvUutTBc27YmXSspotify:track:3ISDIWbHRvUutTBc27YmXShttps://api.spotify.com/v1/tracks/3ISDIWbHRvUu...https://api.spotify.com/v1/audio-analysis/3ISD......-5.82210.20200.2770000.0000000.62300.312119.0982076274
29583206Missy ElliottThe Evolutionspotify:album:3GHCyn6Wwo3CzUDv5MzC1kPromise20060BC9Uo7Ddsmn3A0IyHidgxspotify:track:0BC9Uo7Ddsmn3A0IyHidgxhttps://api.spotify.com/v1/tracks/0BC9Uo7Ddsmn...https://api.spotify.com/v1/audio-analysis/0BC9......-5.75710.04250.4090000.0000040.08190.439123.2792674134
49035181Martha Reeves & The Vandellas50th Anniversary | The Singles Collection | 19...spotify:album:5NSCsRBb7YxQTzKkb0UfLeJimmy Mack - Single Version (Mono)2013-01-011Naq0juYdFzmytkAuUFa2aspotify:track:1Naq0juYdFzmytkAuUFa2ahttps://api.spotify.com/v1/tracks/1Naq0juYdFzm...https://api.spotify.com/v1/audio-analysis/1Naq......-2.94010.03050.1540000.0048700.06310.900129.3431747874
38024050Al GreenThe Lord Will Make a Wayspotify:album:4NoiA9k1PwQ9M6Om0N29u9Highway to Heaven198027TThjoJrBYrv7lUNXDKjfspotify:track:27TThjoJrBYrv7lUNXDKjfhttps://api.spotify.com/v1/tracks/27TThjoJrBYr...https://api.spotify.com/v1/audio-analysis/27TT......-12.41810.04550.1320000.0247000.35800.962117.4121784805
40284297Al GreenGets Next To Youspotify:album:5xvnozp5wBuAHgFhgfbmzQAre You Lonely for Me, Baby1971-08-146IpK9YZ5pCWqMIZVwNIqCqspotify:track:6IpK9YZ5pCWqMIZVwNIqCqhttps://api.spotify.com/v1/tracks/6IpK9YZ5pCWq...https://api.spotify.com/v1/audio-analysis/6IpK......-12.97810.04010.3500000.0004470.15000.88295.5832385874
329371Tina TurnerChildren Beyondspotify:album:3Y4y50H5GIOBJHFLkkO1sVOm Ah Hum20117KEq86lOihE0l2an8lKHS7spotify:track:7KEq86lOihE0l2an8lKHS7https://api.spotify.com/v1/tracks/7KEq86lOihE0...https://api.spotify.com/v1/audio-analysis/7KEq......-6.13910.02880.5430000.9050000.07660.157100.0062746404
32683516Fats DominoLive From Austin, TXspotify:album:0pDyIBwFjeTxJzttXt1lF3I Want to Walk You Home - Live2006-07-250HC5SabG5bBD52OQZFxpv4spotify:track:0HC5SabG5bBD52OQZFxpv4https://api.spotify.com/v1/tracks/0HC5SabG5bBD...https://api.spotify.com/v1/audio-analysis/0HC5......-9.92910.04890.5170000.0000430.36500.917141.8021646273
21692348Black Sabbath13spotify:album:5WXL9YjbNd4GIqWc9mZOOqAge Of Reason2013-01-010BfFxZfRGcf6FOCpZ75tr0spotify:track:0BfFxZfRGcf6FOCpZ75tr0https://api.spotify.com/v1/tracks/0BfFxZfRGcf6...https://api.spotify.com/v1/audio-analysis/0BfF......-4.02100.04410.0000890.2910000.15200.398127.0714210674
\n", + "

10 rows × 24 columns

\n", + "
" + ], + "text/plain": [ + " Unnamed: 0 artist \\\n", + "6302 6624 Pixies \n", + "3458 3706 Fats Domino \n", + "1527 1613 Andy Macpherson \n", + "2958 3206 Missy Elliott \n", + "4903 5181 Martha Reeves & The Vandellas \n", + "3802 4050 Al Green \n", + "4028 4297 Al Green \n", + "329 371 Tina Turner \n", + "3268 3516 Fats Domino \n", + "2169 2348 Black Sabbath \n", + "\n", + " album_name \\\n", + "6302 Live from MacEwan Hall, University of Calgary,... \n", + "3458 My Blue Heaven \n", + "1527 Live At The Isle Of Wight Festival 1970 \n", + "2958 The Evolution \n", + "4903 50th Anniversary | The Singles Collection | 19... \n", + "3802 The Lord Will Make a Way \n", + "4028 Gets Next To You \n", + "329 Children Beyond \n", + "3268 Live From Austin, TX \n", + "2169 13 \n", + "\n", + " album_uri \\\n", + "6302 spotify:album:3aHWofJ19fdj8C43ZV7NQQ \n", + "3458 spotify:album:2P6OFyXNxpIxMZzNlCbrbV \n", + "1527 spotify:album:1FUZJRZ4eOHT4MJuRh5fh8 \n", + "2958 spotify:album:3GHCyn6Wwo3CzUDv5MzC1k \n", + "4903 spotify:album:5NSCsRBb7YxQTzKkb0UfLe \n", + "3802 spotify:album:4NoiA9k1PwQ9M6Om0N29u9 \n", + "4028 spotify:album:5xvnozp5wBuAHgFhgfbmzQ \n", + "329 spotify:album:3Y4y50H5GIOBJHFLkkO1sV \n", + "3268 spotify:album:0pDyIBwFjeTxJzttXt1lF3 \n", + "2169 spotify:album:5WXL9YjbNd4GIqWc9mZOOq \n", + "\n", + " track release_date \\\n", + "6302 Isla De Encanta - Live from MacEwan Hall, Univ... 2021-04-19 \n", + "3458 I'm Ready 1990-01-01 \n", + "1527 Christmas - Live 1996-10-29 \n", + "2958 Promise 2006 \n", + "4903 Jimmy Mack - Single Version (Mono) 2013-01-01 \n", + "3802 Highway to Heaven 1980 \n", + "4028 Are You Lonely for Me, Baby 1971-08-14 \n", + "329 Om Ah Hum 2011 \n", + "3268 I Want to Walk You Home - Live 2006-07-25 \n", + "2169 Age Of Reason 2013-01-01 \n", + "\n", + " id song_uri \\\n", + "6302 5wtiSXLdMJZwkeiVUScQcv spotify:track:5wtiSXLdMJZwkeiVUScQcv \n", + "3458 7gQzF4ZKsRo7C2DY5jMf8r spotify:track:7gQzF4ZKsRo7C2DY5jMf8r \n", + "1527 3ISDIWbHRvUutTBc27YmXS spotify:track:3ISDIWbHRvUutTBc27YmXS \n", + "2958 0BC9Uo7Ddsmn3A0IyHidgx spotify:track:0BC9Uo7Ddsmn3A0IyHidgx \n", + "4903 1Naq0juYdFzmytkAuUFa2a spotify:track:1Naq0juYdFzmytkAuUFa2a \n", + "3802 27TThjoJrBYrv7lUNXDKjf spotify:track:27TThjoJrBYrv7lUNXDKjf \n", + "4028 6IpK9YZ5pCWqMIZVwNIqCq spotify:track:6IpK9YZ5pCWqMIZVwNIqCq \n", + "329 7KEq86lOihE0l2an8lKHS7 spotify:track:7KEq86lOihE0l2an8lKHS7 \n", + "3268 0HC5SabG5bBD52OQZFxpv4 spotify:track:0HC5SabG5bBD52OQZFxpv4 \n", + "2169 0BfFxZfRGcf6FOCpZ75tr0 spotify:track:0BfFxZfRGcf6FOCpZ75tr0 \n", + "\n", + " track_href \\\n", + "6302 https://api.spotify.com/v1/tracks/5wtiSXLdMJZw... \n", + "3458 https://api.spotify.com/v1/tracks/7gQzF4ZKsRo7... \n", + "1527 https://api.spotify.com/v1/tracks/3ISDIWbHRvUu... \n", + "2958 https://api.spotify.com/v1/tracks/0BC9Uo7Ddsmn... \n", + "4903 https://api.spotify.com/v1/tracks/1Naq0juYdFzm... \n", + "3802 https://api.spotify.com/v1/tracks/27TThjoJrBYr... \n", + "4028 https://api.spotify.com/v1/tracks/6IpK9YZ5pCWq... \n", + "329 https://api.spotify.com/v1/tracks/7KEq86lOihE0... \n", + "3268 https://api.spotify.com/v1/tracks/0HC5SabG5bBD... \n", + "2169 https://api.spotify.com/v1/tracks/0BfFxZfRGcf6... \n", + "\n", + " analysis_url ... loudness mode \\\n", + "6302 https://api.spotify.com/v1/audio-analysis/5wti... ... -9.750 1 \n", + "3458 https://api.spotify.com/v1/audio-analysis/7gQz... ... -10.007 1 \n", + "1527 https://api.spotify.com/v1/audio-analysis/3ISD... ... -5.822 1 \n", + "2958 https://api.spotify.com/v1/audio-analysis/0BC9... ... -5.757 1 \n", + "4903 https://api.spotify.com/v1/audio-analysis/1Naq... ... -2.940 1 \n", + "3802 https://api.spotify.com/v1/audio-analysis/27TT... ... -12.418 1 \n", + "4028 https://api.spotify.com/v1/audio-analysis/6IpK... ... -12.978 1 \n", + "329 https://api.spotify.com/v1/audio-analysis/7KEq... ... -6.139 1 \n", + "3268 https://api.spotify.com/v1/audio-analysis/0HC5... ... -9.929 1 \n", + "2169 https://api.spotify.com/v1/audio-analysis/0BfF... ... -4.021 0 \n", + "\n", + " speechiness acousticness instrumentalness liveness valence tempo \\\n", + "6302 0.1340 0.003170 0.001170 0.8400 0.268 129.249 \n", + "3458 0.0516 0.682000 0.000426 0.1140 0.968 172.285 \n", + "1527 0.2020 0.277000 0.000000 0.6230 0.312 119.098 \n", + "2958 0.0425 0.409000 0.000004 0.0819 0.439 123.279 \n", + "4903 0.0305 0.154000 0.004870 0.0631 0.900 129.343 \n", + "3802 0.0455 0.132000 0.024700 0.3580 0.962 117.412 \n", + "4028 0.0401 0.350000 0.000447 0.1500 0.882 95.583 \n", + "329 0.0288 0.543000 0.905000 0.0766 0.157 100.006 \n", + "3268 0.0489 0.517000 0.000043 0.3650 0.917 141.802 \n", + "2169 0.0441 0.000089 0.291000 0.1520 0.398 127.071 \n", + "\n", + " duration_ms time_signature \n", + "6302 113067 4 \n", + "3458 125040 4 \n", + "1527 207627 4 \n", + "2958 267413 4 \n", + "4903 174787 4 \n", + "3802 178480 5 \n", + "4028 238587 4 \n", + "329 274640 4 \n", + "3268 164627 3 \n", + "2169 421067 4 \n", + "\n", + "[10 rows x 24 columns]" + ] + }, + "execution_count": 250, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data = pd.read_csv(r\"C:\\\\Users\\\\nicol\\\\lab-api-wrappers\\spotify_tracks.csv\")\n", + "data.sample(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 251, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(7002, 24)" + ] + }, + "execution_count": 251, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 252, + "metadata": {}, + "outputs": [], + "source": [ + "num_df = data.select_dtypes(include = np.number)\n", + "cat_df = data.select_dtypes(include = object)" + ] + }, + { + "cell_type": "code", + "execution_count": 253, + "metadata": {}, + "outputs": [], + "source": [ + "num_df = num_df.drop('Unnamed: 0', axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 254, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
danceabilityenergykeyloudnessmodespeechinessacousticnessinstrumentalnesslivenessvalencetempoduration_mstime_signature
00.4000.8967-13.92810.04070.28600.5020000.32300.6330152.5202518934
10.5620.6722-13.60510.05320.41100.0005940.12200.3480141.1602182004
20.3830.8295-12.32210.05970.57600.0000050.19400.7720166.5301722674
30.3390.8585-13.16610.05310.56800.0000010.11400.7150169.8491747734
40.3780.8495-12.91710.06580.51600.0000000.12600.8160168.0261871334
..........................................
69970.6390.4919-13.90600.06160.09750.9020000.10700.3990114.5811840764
69980.9080.4351-8.58310.06760.27900.5000000.08890.9280124.9801269034
69990.4210.3464-9.70000.03590.95100.9110000.10200.035356.2651250495
70000.5460.4750-9.67010.05570.64400.1070000.13100.257075.0061244794
70010.5530.6138-11.71900.05050.10100.7300000.32500.408092.8571393474
\n", + "

7002 rows × 13 columns

\n", + "
" + ], + "text/plain": [ + " danceability energy key loudness mode speechiness acousticness \\\n", + "0 0.400 0.896 7 -13.928 1 0.0407 0.2860 \n", + "1 0.562 0.672 2 -13.605 1 0.0532 0.4110 \n", + "2 0.383 0.829 5 -12.322 1 0.0597 0.5760 \n", + "3 0.339 0.858 5 -13.166 1 0.0531 0.5680 \n", + "4 0.378 0.849 5 -12.917 1 0.0658 0.5160 \n", + "... ... ... ... ... ... ... ... \n", + "6997 0.639 0.491 9 -13.906 0 0.0616 0.0975 \n", + "6998 0.908 0.435 1 -8.583 1 0.0676 0.2790 \n", + "6999 0.421 0.346 4 -9.700 0 0.0359 0.9510 \n", + "7000 0.546 0.475 0 -9.670 1 0.0557 0.6440 \n", + "7001 0.553 0.613 8 -11.719 0 0.0505 0.1010 \n", + "\n", + " instrumentalness liveness valence tempo duration_ms \\\n", + "0 0.502000 0.3230 0.6330 152.520 251893 \n", + "1 0.000594 0.1220 0.3480 141.160 218200 \n", + "2 0.000005 0.1940 0.7720 166.530 172267 \n", + "3 0.000001 0.1140 0.7150 169.849 174773 \n", + "4 0.000000 0.1260 0.8160 168.026 187133 \n", + "... ... ... ... ... ... \n", + "6997 0.902000 0.1070 0.3990 114.581 184076 \n", + "6998 0.500000 0.0889 0.9280 124.980 126903 \n", + "6999 0.911000 0.1020 0.0353 56.265 125049 \n", + "7000 0.107000 0.1310 0.2570 75.006 124479 \n", + "7001 0.730000 0.3250 0.4080 92.857 139347 \n", + "\n", + " time_signature \n", + "0 4 \n", + "1 4 \n", + "2 4 \n", + "3 4 \n", + "4 4 \n", + "... ... \n", + "6997 4 \n", + "6998 4 \n", + "6999 5 \n", + "7000 4 \n", + "7001 4 \n", + "\n", + "[7002 rows x 13 columns]" + ] + }, + "execution_count": 254, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "num_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Skaling" + ] + }, + { + "cell_type": "code", + "execution_count": 255, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
0123456789101112
0-0.5406191.0688080.530273-1.1682640.576581-0.3377090.0820211.900405-0.1030950.4438521.0587190.2589310.2435
10.4127850.036137-0.875517-1.0847890.576581-0.2185200.522632-0.409819-0.760495-0.7300270.658908-0.0490340.2435
2-0.6406670.759928-0.032043-0.7532130.576581-0.1565411.104237-0.412535-0.5250081.0163761.551795-0.4688770.2435
3-0.8996170.893622-0.032043-0.9713350.576581-0.2194731.076038-0.412550-0.7866600.7816001.668606-0.4459710.2435
4-0.6700930.852131-0.032043-0.9069840.576581-0.0983770.892744-0.412556-0.7474131.1976071.604446-0.3329970.2435
\n", + "
" + ], + "text/plain": [ + " 0 1 2 3 4 5 6 \\\n", + "0 -0.540619 1.068808 0.530273 -1.168264 0.576581 -0.337709 0.082021 \n", + "1 0.412785 0.036137 -0.875517 -1.084789 0.576581 -0.218520 0.522632 \n", + "2 -0.640667 0.759928 -0.032043 -0.753213 0.576581 -0.156541 1.104237 \n", + "3 -0.899617 0.893622 -0.032043 -0.971335 0.576581 -0.219473 1.076038 \n", + "4 -0.670093 0.852131 -0.032043 -0.906984 0.576581 -0.098377 0.892744 \n", + "\n", + " 7 8 9 10 11 12 \n", + "0 1.900405 -0.103095 0.443852 1.058719 0.258931 0.2435 \n", + "1 -0.409819 -0.760495 -0.730027 0.658908 -0.049034 0.2435 \n", + "2 -0.412535 -0.525008 1.016376 1.551795 -0.468877 0.2435 \n", + "3 -0.412550 -0.786660 0.781600 1.668606 -0.445971 0.2435 \n", + "4 -0.412556 -0.747413 1.197607 1.604446 -0.332997 0.2435 " + ] + }, + "execution_count": 255, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# K-Means is a distance based algorithm: we need to scale / normalize:\n", + "from sklearn.preprocessing import StandardScaler\n", + "num_df_prep = StandardScaler().fit_transform(num_df)\n", + "\n", + "pd.DataFrame(num_df_prep).head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clustering" + ] + }, + { + "cell_type": "code", + "execution_count": 256, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "KMeans(n_clusters=3, random_state=42)" + ] + }, + "execution_count": 256, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.cluster import KMeans\n", + "\n", + "kmeans = KMeans(n_clusters=3, random_state=42)\n", + "kmeans.fit(num_df_prep)" + ] + }, + { + "cell_type": "code", + "execution_count": 257, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0, 1, 1, ..., 2, 2, 1])" + ] + }, + "execution_count": 257, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Predicting / assigning the clusters:\n", + "clusters = kmeans.predict(num_df_prep)\n", + "clusters" + ] + }, + { + "cell_type": "code", + "execution_count": 258, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 2787\n", + "1 2918\n", + "2 1297\n", + "dtype: int64" + ] + }, + "execution_count": 258, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Check the size of the clusters\n", + "pd.Series(clusters).value_counts().sort_index()" + ] + }, + { + "cell_type": "code", + "execution_count": 260, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
danceabilityenergykeyloudnessmodespeechinessacousticnessinstrumentalnesslivenessvalencetempoduration_mstime_signaturecluster
28050.6390.7557-6.92800.08370.0178000.0000000.24300.360147.86822480041
64160.4870.5756-12.84500.04210.3290000.0105000.96200.469121.56817052140
27220.3320.7699-11.50310.07650.0036700.0041300.03720.525157.37835477440
12210.2190.8880-5.28010.05530.0000100.0000110.68900.245126.82052828040
13930.3670.6404-10.04310.04410.2100000.3540000.11400.287146.69951596040
56260.1800.9440-13.21410.08720.0019500.9280000.93500.124177.34820140040
50540.5140.6240-9.28700.08420.5130000.0051700.30700.755166.72616305341
63110.4070.86011-11.15200.03310.0547000.2220000.94500.871144.00124581440
68610.4130.81311-12.19610.05200.0000540.0055800.09190.729124.17610126731
17130.6180.9087-5.48410.03200.4910000.0002320.09030.705133.29916313341
\n", + "
" + ], + "text/plain": [ + " danceability energy key loudness mode speechiness acousticness \\\n", + "2805 0.639 0.755 7 -6.928 0 0.0837 0.017800 \n", + "6416 0.487 0.575 6 -12.845 0 0.0421 0.329000 \n", + "2722 0.332 0.769 9 -11.503 1 0.0765 0.003670 \n", + "1221 0.219 0.888 0 -5.280 1 0.0553 0.000010 \n", + "1393 0.367 0.640 4 -10.043 1 0.0441 0.210000 \n", + "5626 0.180 0.944 0 -13.214 1 0.0872 0.001950 \n", + "5054 0.514 0.624 0 -9.287 0 0.0842 0.513000 \n", + "6311 0.407 0.860 11 -11.152 0 0.0331 0.054700 \n", + "6861 0.413 0.813 11 -12.196 1 0.0520 0.000054 \n", + "1713 0.618 0.908 7 -5.484 1 0.0320 0.491000 \n", + "\n", + " instrumentalness liveness valence tempo duration_ms \\\n", + "2805 0.000000 0.2430 0.360 147.868 224800 \n", + "6416 0.010500 0.9620 0.469 121.568 170521 \n", + "2722 0.004130 0.0372 0.525 157.378 354774 \n", + "1221 0.000011 0.6890 0.245 126.820 528280 \n", + "1393 0.354000 0.1140 0.287 146.699 515960 \n", + "5626 0.928000 0.9350 0.124 177.348 201400 \n", + "5054 0.005170 0.3070 0.755 166.726 163053 \n", + "6311 0.222000 0.9450 0.871 144.001 245814 \n", + "6861 0.005580 0.0919 0.729 124.176 101267 \n", + "1713 0.000232 0.0903 0.705 133.299 163133 \n", + "\n", + " time_signature cluster \n", + "2805 4 1 \n", + "6416 4 0 \n", + "2722 4 0 \n", + "1221 4 0 \n", + "1393 4 0 \n", + "5626 4 0 \n", + "5054 4 1 \n", + "6311 4 0 \n", + "6861 3 1 \n", + "1713 4 1 " + ] + }, + "execution_count": 260, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Explore the cluster assignment in the original dataset\n", + "clustered_df = pd.DataFrame(num_df)\n", + "clustered_df[\"cluster\"] = clusters\n", + "clustered_df.sample(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 261, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "72394.89422540777" + ] + }, + "execution_count": 261, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# \"performance metric\"\n", + "kmeans.inertia_" + ] + }, + { + "cell_type": "code", + "execution_count": 262, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0, 0, 0], dtype=int64)" + ] + }, + "execution_count": 262, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.metrics import pairwise_distances_argmin_min\n", + "\n", + "new_df = clustered_df.drop('cluster', axis=1).loc[[0]]\n", + "closest, _ = pairwise_distances_argmin_min(kmeans.cluster_centers_, new_df)\n", + "closest" + ] + }, + { + "cell_type": "code", + "execution_count": 264, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
danceabilityenergykeyloudnessmodespeechinessacousticnessinstrumentalnesslivenessvalencetempoduration_mstime_signature
00.40.8967-13.92810.04070.2860.5020.3230.633152.522518934
\n", + "
" + ], + "text/plain": [ + " danceability energy key loudness mode speechiness acousticness \\\n", + "0 0.4 0.896 7 -13.928 1 0.0407 0.286 \n", + "\n", + " instrumentalness liveness valence tempo duration_ms time_signature \n", + "0 0.502 0.323 0.633 152.52 251893 4 " + ] + }, + "execution_count": 264, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Check whether or not the song is in the Billboard Hot 200." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create Soup" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "What URL do you want to use? https://www.billboard.com/charts/billboard-200\n", + "soup created!\n" + ] + } + ], + "source": [ + "url = input(\"What URL do you want to use? \")\n", + "response = requests.get(url)\n", + "\n", + "if response.status_code == 200:\n", + " soup = BeautifulSoup(response.content, \"html.parser\")\n", + " print('soup created!')\n", + " \n", + "else:\n", + " print('ERROR: ',response.status_code)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pick \"Ingredients\" from Soup :)" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": {}, + "outputs": [], + "source": [ + "songs = soup.find_all(\"span\", class_=\"chart-element__information__song text--truncate color--primary\")\n", + "artists = soup.find_all(\"span\", class_=\"chart-element__information__artist text--truncate color--secondary\")\n", + "last_week_ranks = soup.find_all(\"span\", class_= \"chart-element__meta text--center color--secondary text--last\")\n", + "peak_ranks = soup.find_all(\"span\", class_= \"chart-element__meta text--center color--secondary text--peak\")\n", + "weeks_on_chart = soup.find_all(\"span\", class_= \"chart-element__meta text--center color--secondary text--week\") " + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": {}, + "outputs": [], + "source": [ + "# 6. Get the text\n", + "for i in [songs, artists, last_week_ranks, peak_ranks, weeks_on_chart]:\n", + " for j in range(len(i)):\n", + " i[j] = i[j].getText()" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
songartistlast_week_rankpeak_rankweeks_on_chart
52TravellerChris Stapleton541317
1Certified Lover BoyDrake215
161BLAME IT ON BABYDaBaby145177
57YHLQMDLGBad Bunny53284
12Happier Than EverBillie Eilish9110
128MoanaSoundtrack1322252
139Dont Smile At MeBillie Eilish13614198
189IGORTyler, The Creator-1104
22Legends Never DieJuice WRLD24165
179The AnxietyTHE ANXIETY: WILLOW & Tyler Cole1531414
\n", + "
" + ], + "text/plain": [ + " song artist last_week_rank \\\n", + "52 Traveller Chris Stapleton 54 \n", + "1 Certified Lover Boy Drake 2 \n", + "161 BLAME IT ON BABY DaBaby 145 \n", + "57 YHLQMDLG Bad Bunny 53 \n", + "12 Happier Than Ever Billie Eilish 9 \n", + "128 Moana Soundtrack 132 \n", + "139 Dont Smile At Me Billie Eilish 136 \n", + "189 IGOR Tyler, The Creator - \n", + "22 Legends Never Die Juice WRLD 24 \n", + "179 The Anxiety THE ANXIETY: WILLOW & Tyler Cole 153 \n", + "\n", + " peak_rank weeks_on_chart \n", + "52 1 317 \n", + "1 1 5 \n", + "161 1 77 \n", + "57 2 84 \n", + "12 1 10 \n", + "128 2 252 \n", + "139 14 198 \n", + "189 1 104 \n", + "22 1 65 \n", + "179 141 4 " + ] + }, + "execution_count": 130, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 7. Build a dataframe\n", + "\n", + "billboard = pd.DataFrame(\n", + " {\"song\": songs,\n", + " \"artist\": artists,\n", + " \"last_week_rank\": last_week_ranks,\n", + " \"peak_rank\": peak_ranks,\n", + " \"weeks_on_chart\": weeks_on_chart}\n", + ")\n", + "\n", + "billboard.sample(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Check for song in Billboard" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Is there any artist you like? Chris Stapleton\n", + "What song do you like? Traveller\n", + "Here is: Traveller from Chris Stapleton\n" + ] + } + ], + "source": [ + "user_artist = input(\"Is there any artist you like? \")\n", + "user_song = input(\"What song do you like? \")\n", + "\n", + "pos = -1\n", + "for index, song in enumerate(billboard['song'].tolist()):\n", + " if song == user_song:\n", + " pos = index\n", + "\n", + "if pos >=0:\n", + " print('Here is: ',billboard['song'].iloc[pos], 'from', user_artist)\n", + "else:\n", + " print(user_song,' is not a song in the Billboard Hot 200!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Collect the audio features from the Spotify API." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Initialize SpotiPy with user credentials" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "metadata": {}, + "outputs": [], + "source": [ + "client_id = '8d4cb3eae1394f10b8b5f97826cd53ba'\n", + "client_secret = '8b4dd0a7b41c474ba0a986c104160f06'\n", + "client_credentials_manager = SpotifyClientCredentials(client_id=client_id, client_secret=client_secret)\n", + "sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "metadata": {}, + "outputs": [], + "source": [ + "searchResults = sp.search(q=\"artist:\" + user_artist + \" track:\" + user_song, type=\"track\")" + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'tracks': {'href': 'https://api.spotify.com/v1/search?query=artist%3AChris+Stapleton+track%3ATraveller&type=track&offset=0&limit=10',\n", + " 'items': [{'album': {'album_type': 'album',\n", + " 'artists': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/4YLtscXsxbVgi031ovDDdh'},\n", + " 'href': 'https://api.spotify.com/v1/artists/4YLtscXsxbVgi031ovDDdh',\n", + " 'id': '4YLtscXsxbVgi031ovDDdh',\n", + " 'name': 'Chris Stapleton',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:4YLtscXsxbVgi031ovDDdh'}],\n", + " 'available_markets': ['AD',\n", + " 'AE',\n", + " 'AG',\n", + " 'AL',\n", + " 'AM',\n", + " 'AO',\n", + " 'AR',\n", + " 'AT',\n", + " 'AU',\n", + " 'AZ',\n", + " 'BA',\n", + " 'BB',\n", + " 'BD',\n", + " 'BE',\n", + " 'BF',\n", + " 'BG',\n", + " 'BH',\n", + " 'BI',\n", + " 'BJ',\n", + " 'BN',\n", + " 'BO',\n", + " 'BR',\n", + " 'BS',\n", + " 'BT',\n", + " 'BW',\n", + " 'BY',\n", + " 'BZ',\n", + " 'CA',\n", + " 'CH',\n", + " 'CI',\n", + " 'CL',\n", + " 'CM',\n", + " 'CO',\n", + " 'CR',\n", + " 'CV',\n", + " 'CW',\n", + " 'CY',\n", + " 'CZ',\n", + " 'DE',\n", + " 'DJ',\n", + " 'DK',\n", + " 'DM',\n", + " 'DO',\n", + " 'DZ',\n", + " 'EC',\n", + " 'EE',\n", + " 'EG',\n", + " 'ES',\n", + " 'FI',\n", + " 'FJ',\n", + " 'FM',\n", + " 'FR',\n", + " 'GA',\n", + " 'GB',\n", + " 'GD',\n", + " 'GE',\n", + " 'GH',\n", + " 'GM',\n", + " 'GN',\n", + " 'GQ',\n", + " 'GR',\n", + " 'GT',\n", + " 'GW',\n", + " 'GY',\n", + " 'HK',\n", + " 'HN',\n", + " 'HR',\n", + " 'HT',\n", + " 'HU',\n", + " 'ID',\n", + " 'IE',\n", + " 'IL',\n", + " 'IN',\n", + " 'IS',\n", + " 'IT',\n", + " 'JM',\n", + " 'JO',\n", + " 'JP',\n", + " 'KE',\n", + " 'KG',\n", + " 'KH',\n", + " 'KI',\n", + " 'KM',\n", + " 'KN',\n", + " 'KR',\n", + " 'KW',\n", + " 'KZ',\n", + " 'LA',\n", + " 'LB',\n", + " 'LC',\n", + " 'LI',\n", + " 'LK',\n", + " 'LR',\n", + " 'LS',\n", + " 'LT',\n", + " 'LU',\n", + " 'LV',\n", + " 'MA',\n", + " 'MC',\n", + " 'MD',\n", + " 'ME',\n", + " 'MG',\n", + " 'MH',\n", + " 'MK',\n", + " 'ML',\n", + " 'MN',\n", + " 'MO',\n", + " 'MR',\n", + " 'MT',\n", + " 'MU',\n", + " 'MV',\n", + " 'MW',\n", + " 'MX',\n", + " 'MY',\n", + " 'MZ',\n", + " 'NA',\n", + " 'NE',\n", + " 'NG',\n", + " 'NI',\n", + " 'NL',\n", + " 'NO',\n", + " 'NP',\n", + " 'NR',\n", + " 'NZ',\n", + " 'OM',\n", + " 'PA',\n", + " 'PE',\n", + " 'PG',\n", + " 'PH',\n", + " 'PK',\n", + " 'PL',\n", + " 'PS',\n", + " 'PT',\n", + " 'PW',\n", + " 'PY',\n", + " 'QA',\n", + " 'RO',\n", + " 'RS',\n", + " 'RU',\n", + " 'RW',\n", + " 'SA',\n", + " 'SB',\n", + " 'SC',\n", + " 'SE',\n", + " 'SG',\n", + " 'SI',\n", + " 'SK',\n", + " 'SL',\n", + " 'SM',\n", + " 'SN',\n", + " 'SR',\n", + " 'ST',\n", + " 'SV',\n", + " 'SZ',\n", + " 'TD',\n", + " 'TG',\n", + " 'TH',\n", + " 'TL',\n", + " 'TN',\n", + " 'TO',\n", + " 'TR',\n", + " 'TT',\n", + " 'TV',\n", + " 'TW',\n", + " 'TZ',\n", + " 'UA',\n", + " 'UG',\n", + " 'US',\n", + " 'UY',\n", + " 'UZ',\n", + " 'VC',\n", + " 'VN',\n", + " 'VU',\n", + " 'WS',\n", + " 'XK',\n", + " 'ZA',\n", + " 'ZM',\n", + " 'ZW'],\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/album/7lxHnls3yQNl8B9bILmHj7'},\n", + " 'href': 'https://api.spotify.com/v1/albums/7lxHnls3yQNl8B9bILmHj7',\n", + " 'id': '7lxHnls3yQNl8B9bILmHj7',\n", + " 'images': [{'height': 640,\n", + " 'url': 'https://i.scdn.co/image/ab67616d0000b273540fc1d083eac5bcff8dad21',\n", + " 'width': 640},\n", + " {'height': 300,\n", + " 'url': 'https://i.scdn.co/image/ab67616d00001e02540fc1d083eac5bcff8dad21',\n", + " 'width': 300},\n", + " {'height': 64,\n", + " 'url': 'https://i.scdn.co/image/ab67616d00004851540fc1d083eac5bcff8dad21',\n", + " 'width': 64}],\n", + " 'name': 'Traveller',\n", + " 'release_date': '2015-05-04',\n", + " 'release_date_precision': 'day',\n", + " 'total_tracks': 14,\n", + " 'type': 'album',\n", + " 'uri': 'spotify:album:7lxHnls3yQNl8B9bILmHj7'},\n", + " 'artists': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/4YLtscXsxbVgi031ovDDdh'},\n", + " 'href': 'https://api.spotify.com/v1/artists/4YLtscXsxbVgi031ovDDdh',\n", + " 'id': '4YLtscXsxbVgi031ovDDdh',\n", + " 'name': 'Chris Stapleton',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:4YLtscXsxbVgi031ovDDdh'}],\n", + " 'available_markets': ['AD',\n", + " 'AE',\n", + " 'AG',\n", + " 'AL',\n", + " 'AM',\n", + " 'AO',\n", + " 'AR',\n", + " 'AT',\n", + " 'AU',\n", + " 'AZ',\n", + " 'BA',\n", + " 'BB',\n", + " 'BD',\n", + " 'BE',\n", + " 'BF',\n", + " 'BG',\n", + " 'BH',\n", + " 'BI',\n", + " 'BJ',\n", + " 'BN',\n", + " 'BO',\n", + " 'BR',\n", + " 'BS',\n", + " 'BT',\n", + " 'BW',\n", + " 'BY',\n", + " 'BZ',\n", + " 'CA',\n", + " 'CH',\n", + " 'CI',\n", + " 'CL',\n", + " 'CM',\n", + " 'CO',\n", + " 'CR',\n", + " 'CV',\n", + " 'CW',\n", + " 'CY',\n", + " 'CZ',\n", + " 'DE',\n", + " 'DJ',\n", + " 'DK',\n", + " 'DM',\n", + " 'DO',\n", + " 'DZ',\n", + " 'EC',\n", + " 'EE',\n", + " 'EG',\n", + " 'ES',\n", + " 'FI',\n", + " 'FJ',\n", + " 'FM',\n", + " 'FR',\n", + " 'GA',\n", + " 'GB',\n", + " 'GD',\n", + " 'GE',\n", + " 'GH',\n", + " 'GM',\n", + " 'GN',\n", + " 'GQ',\n", + " 'GR',\n", + " 'GT',\n", + " 'GW',\n", + " 'GY',\n", + " 'HK',\n", + " 'HN',\n", + " 'HR',\n", + " 'HT',\n", + " 'HU',\n", + " 'ID',\n", + " 'IE',\n", + " 'IL',\n", + " 'IN',\n", + " 'IS',\n", + " 'IT',\n", + " 'JM',\n", + " 'JO',\n", + " 'JP',\n", + " 'KE',\n", + " 'KG',\n", + " 'KH',\n", + " 'KI',\n", + " 'KM',\n", + " 'KN',\n", + " 'KR',\n", + " 'KW',\n", + " 'KZ',\n", + " 'LA',\n", + " 'LB',\n", + " 'LC',\n", + " 'LI',\n", + " 'LK',\n", + " 'LR',\n", + " 'LS',\n", + " 'LT',\n", + " 'LU',\n", + " 'LV',\n", + " 'MA',\n", + " 'MC',\n", + " 'MD',\n", + " 'ME',\n", + " 'MG',\n", + " 'MH',\n", + " 'MK',\n", + " 'ML',\n", + " 'MN',\n", + " 'MO',\n", + " 'MR',\n", + " 'MT',\n", + " 'MU',\n", + " 'MV',\n", + " 'MW',\n", + " 'MX',\n", + " 'MY',\n", + " 'MZ',\n", + " 'NA',\n", + " 'NE',\n", + " 'NG',\n", + " 'NI',\n", + " 'NL',\n", + " 'NO',\n", + " 'NP',\n", + " 'NR',\n", + " 'NZ',\n", + " 'OM',\n", + " 'PA',\n", + " 'PE',\n", + " 'PG',\n", + " 'PH',\n", + " 'PK',\n", + " 'PL',\n", + " 'PS',\n", + " 'PT',\n", + " 'PW',\n", + " 'PY',\n", + " 'QA',\n", + " 'RO',\n", + " 'RS',\n", + " 'RU',\n", + " 'RW',\n", + " 'SA',\n", + " 'SB',\n", + " 'SC',\n", + " 'SE',\n", + " 'SG',\n", + " 'SI',\n", + " 'SK',\n", + " 'SL',\n", + " 'SM',\n", + " 'SN',\n", + " 'SR',\n", + " 'ST',\n", + " 'SV',\n", + " 'SZ',\n", + " 'TD',\n", + " 'TG',\n", + " 'TH',\n", + " 'TL',\n", + " 'TN',\n", + " 'TO',\n", + " 'TR',\n", + " 'TT',\n", + " 'TV',\n", + " 'TW',\n", + " 'TZ',\n", + " 'UA',\n", + " 'UG',\n", + " 'US',\n", + " 'UY',\n", + " 'UZ',\n", + " 'VC',\n", + " 'VN',\n", + " 'VU',\n", + " 'WS',\n", + " 'XK',\n", + " 'ZA',\n", + " 'ZM',\n", + " 'ZW'],\n", + " 'disc_number': 1,\n", + " 'duration_ms': 222413,\n", + " 'explicit': False,\n", + " 'external_ids': {'isrc': 'USUM71418080'},\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/track/178OI1A3qjROeFeh8lmNwW'},\n", + " 'href': 'https://api.spotify.com/v1/tracks/178OI1A3qjROeFeh8lmNwW',\n", + " 'id': '178OI1A3qjROeFeh8lmNwW',\n", + " 'is_local': False,\n", + " 'name': 'Traveller',\n", + " 'popularity': 66,\n", + " 'preview_url': None,\n", + " 'track_number': 1,\n", + " 'type': 'track',\n", + " 'uri': 'spotify:track:178OI1A3qjROeFeh8lmNwW'},\n", + " {'album': {'album_type': 'album',\n", + " 'artists': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of'},\n", + " 'href': 'https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of',\n", + " 'id': '0LyfQWJT6nXafLPZqxe9Of',\n", + " 'name': 'Various Artists',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:0LyfQWJT6nXafLPZqxe9Of'}],\n", + " 'available_markets': ['US'],\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/album/05vM360htYvHMa6uhgve1v'},\n", + " 'href': 'https://api.spotify.com/v1/albums/05vM360htYvHMa6uhgve1v',\n", + " 'id': '05vM360htYvHMa6uhgve1v',\n", + " 'images': [{'height': 640,\n", + " 'url': 'https://i.scdn.co/image/ab67616d0000b2734317283394a87191a9678d3c',\n", + " 'width': 640},\n", + " {'height': 300,\n", + " 'url': 'https://i.scdn.co/image/ab67616d00001e024317283394a87191a9678d3c',\n", + " 'width': 300},\n", + " {'height': 64,\n", + " 'url': 'https://i.scdn.co/image/ab67616d000048514317283394a87191a9678d3c',\n", + " 'width': 64}],\n", + " 'name': \"NOW That's What I Call Music! Outlaw Country\",\n", + " 'release_date': '2021-03-19',\n", + " 'release_date_precision': 'day',\n", + " 'total_tracks': 18,\n", + " 'type': 'album',\n", + " 'uri': 'spotify:album:05vM360htYvHMa6uhgve1v'},\n", + " 'artists': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/4YLtscXsxbVgi031ovDDdh'},\n", + " 'href': 'https://api.spotify.com/v1/artists/4YLtscXsxbVgi031ovDDdh',\n", + " 'id': '4YLtscXsxbVgi031ovDDdh',\n", + " 'name': 'Chris Stapleton',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:4YLtscXsxbVgi031ovDDdh'}],\n", + " 'available_markets': ['US'],\n", + " 'disc_number': 1,\n", + " 'duration_ms': 217960,\n", + " 'explicit': False,\n", + " 'external_ids': {'isrc': 'USUM71418080'},\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/track/181dawXbdv8rhrvbVhJYDn'},\n", + " 'href': 'https://api.spotify.com/v1/tracks/181dawXbdv8rhrvbVhJYDn',\n", + " 'id': '181dawXbdv8rhrvbVhJYDn',\n", + " 'is_local': False,\n", + " 'name': 'Traveller',\n", + " 'popularity': 5,\n", + " 'preview_url': None,\n", + " 'track_number': 1,\n", + " 'type': 'track',\n", + " 'uri': 'spotify:track:181dawXbdv8rhrvbVhJYDn'}],\n", + " 'limit': 10,\n", + " 'next': None,\n", + " 'offset': 0,\n", + " 'previous': None,\n", + " 'total': 2}}" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "searchResults" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def artist_tracks(artists):\n", + " \n", + " '''\n", + " Takes a list of artist names, iterates through their Spotify albums, checks for \n", + " duplicate albums, then appends all the tracks in those albums to a list of lists\n", + " '''\n", + " \n", + " # Each list in this list will be a track and its features\n", + " tracks = []\n", + " \n", + " for artist in tqdm_notebook(artists):\n", + " \n", + " # Get the artist URI (a unique ID)\n", + " artist_uri = sp.search(artist)['tracks']['items'][0]['artists'][0]['uri']\n", + "\n", + " # Spotify has a lot of duplicate albums, but we'll cross-reference them with this list to avoid extra loops\n", + " album_checker = []\n", + " \n", + " # The starting point of our loop of albums for those artists with more than 50\n", + " n = 0\n", + " \n", + " # Note the album_type = 'album'. This discounts singles, compilations and collaborations\n", + " while len(sp.artist_albums(artist_uri, album_type = 'album', limit=50, offset = n)['items']) > 0:\n", + " \n", + " # Avoid overloading Spotify with requests by assigning the list of album dictionaries to a variable\n", + " dict_list = sp.artist_albums(artist_uri, album_type = 'album', limit=50, offset = n)['items']\n", + " \n", + " for i, album in tqdm_notebook(enumerate(dict_list)):\n", + "\n", + " # Add the featured artists for the album in question to the checklist\n", + " check_this_album = [j['name'] for j in dict_list[i]['artists']]\n", + " # And the album name\n", + " check_this_album.append(dict_list[i]['name'])\n", + " # And its date\n", + " check_this_album.append(dict_list[i]['release_date'])\n", + "\n", + " # Only continue looping if that album isn't in the checklist\n", + " if check_this_album not in album_checker:\n", + " \n", + " # Add this album to the checker\n", + " album_checker.append(check_this_album)\n", + " # For every song on the album, get its descriptors and features in a list and add to the tracklist\n", + " tracks.extend([[artist, album['name'], album['uri'], song['name'],\n", + "\n", + " album['release_date']] + list(sp.audio_features(song['uri'])[0].values()) \n", + " for song in sp.album_tracks(album['uri'])['items']])\n", + " \n", + " # Go through the next 50 albums (otherwise we'll get an infinite while loop)\n", + " n += 50\n", + "\n", + " return tracks" + ] + }, + { + "cell_type": "code", + "execution_count": 224, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":11: TqdmDeprecationWarning: This function will be removed in tqdm==5.0.0\n", + "Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook`\n", + " for artist in tqdm_notebook(artists):\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fdd0d29ba1f4467a824605122e1373df", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=0.0, max=15.0), HTML(value='')))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":28: TqdmDeprecationWarning: This function will be removed in tqdm==5.0.0\n", + "Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook`\n", + " for i, album in tqdm_notebook(enumerate(dict_list)):\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "71c753061a0e4b958aeed0880152312c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a558d587eac34ab999514ea24b401886", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "71e061c60b2c4159af125e98f5e71427", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "22119734e5b640de8720583b7dffeaea", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ceb868fe53784914b47c18b888c69f06", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "HTTP Error for GET to https://api.spotify.com/v1/search with Params: {'q': ' ', 'limit': 10, 'offset': 0, 'type': 'track', 'market': None} returned 404 due to Not found.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n" + ] + }, + { + "ename": "SpotifyException", + "evalue": "http status: 404, code:-1 - https://api.spotify.com/v1/search?q=+&limit=10&offset=0&type=track:\n Not found., reason: None", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mHTTPError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\spotipy\\client.py\u001b[0m in \u001b[0;36m_internal_call\u001b[1;34m(self, method, url, payload, params)\u001b[0m\n\u001b[0;32m 244\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 245\u001b[1;33m \u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mraise_for_status\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 246\u001b[0m \u001b[0mresults\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mjson\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\requests\\models.py\u001b[0m in \u001b[0;36mraise_for_status\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 952\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mhttp_error_msg\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 953\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mHTTPError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mhttp_error_msg\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mresponse\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 954\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mHTTPError\u001b[0m: 404 Client Error: Not Found for url: https://api.spotify.com/v1/search?q=+&limit=10&offset=0&type=track", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[1;31mSpotifyException\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0martist_df\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0martist_tracks\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0muser_artist\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32m\u001b[0m in \u001b[0;36martist_tracks\u001b[1;34m(artists)\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;31m# Get the artist URI (a unique ID)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 14\u001b[1;33m \u001b[0martist_uri\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msearch\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0martist\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'tracks'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'items'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'artists'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'uri'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 15\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 16\u001b[0m \u001b[1;31m# Spotify has a lot of duplicate albums, but we'll cross-reference them with this list to avoid extra loops\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\spotipy\\client.py\u001b[0m in \u001b[0;36msearch\u001b[1;34m(self, q, limit, offset, type, market)\u001b[0m\n\u001b[0;32m 551\u001b[0m \u001b[0mfrom_token\u001b[0m\u001b[1;33m.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 552\u001b[0m \"\"\"\n\u001b[1;32m--> 553\u001b[1;33m return self._get(\n\u001b[0m\u001b[0;32m 554\u001b[0m \u001b[1;34m\"search\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mq\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mq\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlimit\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mlimit\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0moffset\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0moffset\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mtype\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmarket\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mmarket\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 555\u001b[0m )\n", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\spotipy\\client.py\u001b[0m in \u001b[0;36m_get\u001b[1;34m(self, url, args, payload, **kwargs)\u001b[0m\n\u001b[0;32m 295\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 296\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 297\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_internal_call\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"GET\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0murl\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpayload\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 298\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 299\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0m_post\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0murl\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpayload\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\spotipy\\client.py\u001b[0m in \u001b[0;36m_internal_call\u001b[1;34m(self, method, url, payload, params)\u001b[0m\n\u001b[0;32m 265\u001b[0m )\n\u001b[0;32m 266\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 267\u001b[1;33m raise SpotifyException(\n\u001b[0m\u001b[0;32m 268\u001b[0m \u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstatus_code\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 269\u001b[0m \u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mSpotifyException\u001b[0m: http status: 404, code:-1 - https://api.spotify.com/v1/search?q=+&limit=10&offset=0&type=track:\n Not found., reason: None" + ] + } + ], + "source": [ + "artist_df = artist_tracks(user_artist)" + ] + }, + { + "cell_type": "code", + "execution_count": 225, + "metadata": {}, + "outputs": [], + "source": [ + "def df_tracks(tracklist):\n", + " \n", + " '''\n", + " Takes the output of artist_tracks (i.e. a list of lists),\n", + " puts it in a dataframe and formats it.\n", + " '''\n", + "\n", + " df = pd.DataFrame(tracklist, columns=['artist',\n", + " 'album_name',\n", + " 'album_uri',\n", + " 'track',\n", + " 'release_date'] + list(sp.audio_features('7EAqBCOVkDZcbccjxZmgjp')[0].keys()))\n", + "\n", + " df.rename(columns={'uri':'song_uri'}, inplace=True)\n", + "\n", + " df.drop_duplicates(subset=['artist', 'track', 'release_date'], inplace=True)\n", + "\n", + " # Reorder the cols to have identifiers first, auditory features last\n", + " cols = ['artist', 'album_name', 'album_uri', 'track', 'release_date', 'id', 'song_uri', 'track_href',\n", + " 'analysis_url', 'type', 'danceability', 'energy', 'key', 'loudness', 'mode', 'speechiness',\n", + " 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo', 'duration_ms', 'time_signature']\n", + "\n", + " df = df[cols]\n", + " \n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": 226, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'NoneType' object has no attribute 'keys'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msearchresult_df\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdf_tracks\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0martist_df\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32m\u001b[0m in \u001b[0;36mdf_tracks\u001b[1;34m(tracklist)\u001b[0m\n\u001b[0;32m 10\u001b[0m \u001b[1;34m'album_uri'\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;34m'track'\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 12\u001b[1;33m 'release_date'] + list(sp.audio_features('7EAqBCOVkDZcbccjxZmgjp')[0].keys()))\n\u001b[0m\u001b[0;32m 13\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[0mdf\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrename\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcolumns\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m{\u001b[0m\u001b[1;34m'uri'\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;34m'song_uri'\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minplace\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'keys'" + ] + } + ], + "source": [ + "searchresult_df = df_tracks(artist_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 227, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'searchresult_df' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msearchresult_df\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;31mNameError\u001b[0m: name 'searchresult_df' is not defined" + ] + } + ], + "source": [ + "searchresult_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## send the Spotify audio features of the submitted song to the clustering model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/Solutions.ipynb b/Solutions.ipynb new file mode 100644 index 0000000..cd8e959 --- /dev/null +++ b/Solutions.ipynb @@ -0,0 +1,2559 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Lab | Unsupervised learning intro" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Instructions** \n", + "\n", + "It's the moment to perform clustering on the songs you collected. Remember that the ultimate goal of this little project is to improve the recommendations of artists. Clustering the songs will allow the recommendation system to limit the scope of the recommendations to only songs that belong to the same cluster - songs with similar audio features.\n", + "\n", + "The experiments you did with the Spotify API and the Billboard web scraping will allow you to create a pipeline such that when the user enters a song, you:\n", + "\n", + "- Check whether or not the song is in the Billboard Hot 200.\n", + "- Collect the audio features from the Spotify API.\n", + "\n", + "After that, you want to send the Spotify audio features of the submitted song to the clustering model, which should return a cluster number.\n", + "\n", + "We want to have as many songs as possible to create the clustering model, so we will add the songs you collected to a bigger dataset available on Kaggle containing 160 thousand songs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 249, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn import datasets\n", + "\n", + "from bs4 import BeautifulSoup\n", + "import requests\n", + "\n", + "from spotipy.oauth2 import SpotifyClientCredentials\n", + "from tqdm import tqdm_notebook\n", + "import spotipy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import Data" + ] + }, + { + "cell_type": "code", + "execution_count": 250, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Unnamed: 0artistalbum_namealbum_uritrackrelease_dateidsong_uritrack_hrefanalysis_url...loudnessmodespeechinessacousticnessinstrumentalnesslivenessvalencetempoduration_mstime_signature
63026624PixiesLive from MacEwan Hall, University of Calgary,...spotify:album:3aHWofJ19fdj8C43ZV7NQQIsla De Encanta - Live from MacEwan Hall, Univ...2021-04-195wtiSXLdMJZwkeiVUScQcvspotify:track:5wtiSXLdMJZwkeiVUScQcvhttps://api.spotify.com/v1/tracks/5wtiSXLdMJZw...https://api.spotify.com/v1/audio-analysis/5wti......-9.75010.13400.0031700.0011700.84000.268129.2491130674
34583706Fats DominoMy Blue Heavenspotify:album:2P6OFyXNxpIxMZzNlCbrbVI'm Ready1990-01-017gQzF4ZKsRo7C2DY5jMf8rspotify:track:7gQzF4ZKsRo7C2DY5jMf8rhttps://api.spotify.com/v1/tracks/7gQzF4ZKsRo7...https://api.spotify.com/v1/audio-analysis/7gQz......-10.00710.05160.6820000.0004260.11400.968172.2851250404
15271613Andy MacphersonLive At The Isle Of Wight Festival 1970spotify:album:1FUZJRZ4eOHT4MJuRh5fh8Christmas - Live1996-10-293ISDIWbHRvUutTBc27YmXSspotify:track:3ISDIWbHRvUutTBc27YmXShttps://api.spotify.com/v1/tracks/3ISDIWbHRvUu...https://api.spotify.com/v1/audio-analysis/3ISD......-5.82210.20200.2770000.0000000.62300.312119.0982076274
29583206Missy ElliottThe Evolutionspotify:album:3GHCyn6Wwo3CzUDv5MzC1kPromise20060BC9Uo7Ddsmn3A0IyHidgxspotify:track:0BC9Uo7Ddsmn3A0IyHidgxhttps://api.spotify.com/v1/tracks/0BC9Uo7Ddsmn...https://api.spotify.com/v1/audio-analysis/0BC9......-5.75710.04250.4090000.0000040.08190.439123.2792674134
49035181Martha Reeves & The Vandellas50th Anniversary | The Singles Collection | 19...spotify:album:5NSCsRBb7YxQTzKkb0UfLeJimmy Mack - Single Version (Mono)2013-01-011Naq0juYdFzmytkAuUFa2aspotify:track:1Naq0juYdFzmytkAuUFa2ahttps://api.spotify.com/v1/tracks/1Naq0juYdFzm...https://api.spotify.com/v1/audio-analysis/1Naq......-2.94010.03050.1540000.0048700.06310.900129.3431747874
38024050Al GreenThe Lord Will Make a Wayspotify:album:4NoiA9k1PwQ9M6Om0N29u9Highway to Heaven198027TThjoJrBYrv7lUNXDKjfspotify:track:27TThjoJrBYrv7lUNXDKjfhttps://api.spotify.com/v1/tracks/27TThjoJrBYr...https://api.spotify.com/v1/audio-analysis/27TT......-12.41810.04550.1320000.0247000.35800.962117.4121784805
40284297Al GreenGets Next To Youspotify:album:5xvnozp5wBuAHgFhgfbmzQAre You Lonely for Me, Baby1971-08-146IpK9YZ5pCWqMIZVwNIqCqspotify:track:6IpK9YZ5pCWqMIZVwNIqCqhttps://api.spotify.com/v1/tracks/6IpK9YZ5pCWq...https://api.spotify.com/v1/audio-analysis/6IpK......-12.97810.04010.3500000.0004470.15000.88295.5832385874
329371Tina TurnerChildren Beyondspotify:album:3Y4y50H5GIOBJHFLkkO1sVOm Ah Hum20117KEq86lOihE0l2an8lKHS7spotify:track:7KEq86lOihE0l2an8lKHS7https://api.spotify.com/v1/tracks/7KEq86lOihE0...https://api.spotify.com/v1/audio-analysis/7KEq......-6.13910.02880.5430000.9050000.07660.157100.0062746404
32683516Fats DominoLive From Austin, TXspotify:album:0pDyIBwFjeTxJzttXt1lF3I Want to Walk You Home - Live2006-07-250HC5SabG5bBD52OQZFxpv4spotify:track:0HC5SabG5bBD52OQZFxpv4https://api.spotify.com/v1/tracks/0HC5SabG5bBD...https://api.spotify.com/v1/audio-analysis/0HC5......-9.92910.04890.5170000.0000430.36500.917141.8021646273
21692348Black Sabbath13spotify:album:5WXL9YjbNd4GIqWc9mZOOqAge Of Reason2013-01-010BfFxZfRGcf6FOCpZ75tr0spotify:track:0BfFxZfRGcf6FOCpZ75tr0https://api.spotify.com/v1/tracks/0BfFxZfRGcf6...https://api.spotify.com/v1/audio-analysis/0BfF......-4.02100.04410.0000890.2910000.15200.398127.0714210674
\n", + "

10 rows × 24 columns

\n", + "
" + ], + "text/plain": [ + " Unnamed: 0 artist \\\n", + "6302 6624 Pixies \n", + "3458 3706 Fats Domino \n", + "1527 1613 Andy Macpherson \n", + "2958 3206 Missy Elliott \n", + "4903 5181 Martha Reeves & The Vandellas \n", + "3802 4050 Al Green \n", + "4028 4297 Al Green \n", + "329 371 Tina Turner \n", + "3268 3516 Fats Domino \n", + "2169 2348 Black Sabbath \n", + "\n", + " album_name \\\n", + "6302 Live from MacEwan Hall, University of Calgary,... \n", + "3458 My Blue Heaven \n", + "1527 Live At The Isle Of Wight Festival 1970 \n", + "2958 The Evolution \n", + "4903 50th Anniversary | The Singles Collection | 19... \n", + "3802 The Lord Will Make a Way \n", + "4028 Gets Next To You \n", + "329 Children Beyond \n", + "3268 Live From Austin, TX \n", + "2169 13 \n", + "\n", + " album_uri \\\n", + "6302 spotify:album:3aHWofJ19fdj8C43ZV7NQQ \n", + "3458 spotify:album:2P6OFyXNxpIxMZzNlCbrbV \n", + "1527 spotify:album:1FUZJRZ4eOHT4MJuRh5fh8 \n", + "2958 spotify:album:3GHCyn6Wwo3CzUDv5MzC1k \n", + "4903 spotify:album:5NSCsRBb7YxQTzKkb0UfLe \n", + "3802 spotify:album:4NoiA9k1PwQ9M6Om0N29u9 \n", + "4028 spotify:album:5xvnozp5wBuAHgFhgfbmzQ \n", + "329 spotify:album:3Y4y50H5GIOBJHFLkkO1sV \n", + "3268 spotify:album:0pDyIBwFjeTxJzttXt1lF3 \n", + "2169 spotify:album:5WXL9YjbNd4GIqWc9mZOOq \n", + "\n", + " track release_date \\\n", + "6302 Isla De Encanta - Live from MacEwan Hall, Univ... 2021-04-19 \n", + "3458 I'm Ready 1990-01-01 \n", + "1527 Christmas - Live 1996-10-29 \n", + "2958 Promise 2006 \n", + "4903 Jimmy Mack - Single Version (Mono) 2013-01-01 \n", + "3802 Highway to Heaven 1980 \n", + "4028 Are You Lonely for Me, Baby 1971-08-14 \n", + "329 Om Ah Hum 2011 \n", + "3268 I Want to Walk You Home - Live 2006-07-25 \n", + "2169 Age Of Reason 2013-01-01 \n", + "\n", + " id song_uri \\\n", + "6302 5wtiSXLdMJZwkeiVUScQcv spotify:track:5wtiSXLdMJZwkeiVUScQcv \n", + "3458 7gQzF4ZKsRo7C2DY5jMf8r spotify:track:7gQzF4ZKsRo7C2DY5jMf8r \n", + "1527 3ISDIWbHRvUutTBc27YmXS spotify:track:3ISDIWbHRvUutTBc27YmXS \n", + "2958 0BC9Uo7Ddsmn3A0IyHidgx spotify:track:0BC9Uo7Ddsmn3A0IyHidgx \n", + "4903 1Naq0juYdFzmytkAuUFa2a spotify:track:1Naq0juYdFzmytkAuUFa2a \n", + "3802 27TThjoJrBYrv7lUNXDKjf spotify:track:27TThjoJrBYrv7lUNXDKjf \n", + "4028 6IpK9YZ5pCWqMIZVwNIqCq spotify:track:6IpK9YZ5pCWqMIZVwNIqCq \n", + "329 7KEq86lOihE0l2an8lKHS7 spotify:track:7KEq86lOihE0l2an8lKHS7 \n", + "3268 0HC5SabG5bBD52OQZFxpv4 spotify:track:0HC5SabG5bBD52OQZFxpv4 \n", + "2169 0BfFxZfRGcf6FOCpZ75tr0 spotify:track:0BfFxZfRGcf6FOCpZ75tr0 \n", + "\n", + " track_href \\\n", + "6302 https://api.spotify.com/v1/tracks/5wtiSXLdMJZw... \n", + "3458 https://api.spotify.com/v1/tracks/7gQzF4ZKsRo7... \n", + "1527 https://api.spotify.com/v1/tracks/3ISDIWbHRvUu... \n", + "2958 https://api.spotify.com/v1/tracks/0BC9Uo7Ddsmn... \n", + "4903 https://api.spotify.com/v1/tracks/1Naq0juYdFzm... \n", + "3802 https://api.spotify.com/v1/tracks/27TThjoJrBYr... \n", + "4028 https://api.spotify.com/v1/tracks/6IpK9YZ5pCWq... \n", + "329 https://api.spotify.com/v1/tracks/7KEq86lOihE0... \n", + "3268 https://api.spotify.com/v1/tracks/0HC5SabG5bBD... \n", + "2169 https://api.spotify.com/v1/tracks/0BfFxZfRGcf6... \n", + "\n", + " analysis_url ... loudness mode \\\n", + "6302 https://api.spotify.com/v1/audio-analysis/5wti... ... -9.750 1 \n", + "3458 https://api.spotify.com/v1/audio-analysis/7gQz... ... -10.007 1 \n", + "1527 https://api.spotify.com/v1/audio-analysis/3ISD... ... -5.822 1 \n", + "2958 https://api.spotify.com/v1/audio-analysis/0BC9... ... -5.757 1 \n", + "4903 https://api.spotify.com/v1/audio-analysis/1Naq... ... -2.940 1 \n", + "3802 https://api.spotify.com/v1/audio-analysis/27TT... ... -12.418 1 \n", + "4028 https://api.spotify.com/v1/audio-analysis/6IpK... ... -12.978 1 \n", + "329 https://api.spotify.com/v1/audio-analysis/7KEq... ... -6.139 1 \n", + "3268 https://api.spotify.com/v1/audio-analysis/0HC5... ... -9.929 1 \n", + "2169 https://api.spotify.com/v1/audio-analysis/0BfF... ... -4.021 0 \n", + "\n", + " speechiness acousticness instrumentalness liveness valence tempo \\\n", + "6302 0.1340 0.003170 0.001170 0.8400 0.268 129.249 \n", + "3458 0.0516 0.682000 0.000426 0.1140 0.968 172.285 \n", + "1527 0.2020 0.277000 0.000000 0.6230 0.312 119.098 \n", + "2958 0.0425 0.409000 0.000004 0.0819 0.439 123.279 \n", + "4903 0.0305 0.154000 0.004870 0.0631 0.900 129.343 \n", + "3802 0.0455 0.132000 0.024700 0.3580 0.962 117.412 \n", + "4028 0.0401 0.350000 0.000447 0.1500 0.882 95.583 \n", + "329 0.0288 0.543000 0.905000 0.0766 0.157 100.006 \n", + "3268 0.0489 0.517000 0.000043 0.3650 0.917 141.802 \n", + "2169 0.0441 0.000089 0.291000 0.1520 0.398 127.071 \n", + "\n", + " duration_ms time_signature \n", + "6302 113067 4 \n", + "3458 125040 4 \n", + "1527 207627 4 \n", + "2958 267413 4 \n", + "4903 174787 4 \n", + "3802 178480 5 \n", + "4028 238587 4 \n", + "329 274640 4 \n", + "3268 164627 3 \n", + "2169 421067 4 \n", + "\n", + "[10 rows x 24 columns]" + ] + }, + "execution_count": 250, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data = pd.read_csv(r\"C:\\\\Users\\\\nicol\\\\lab-api-wrappers\\spotify_tracks.csv\")\n", + "data.sample(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 251, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(7002, 24)" + ] + }, + "execution_count": 251, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 252, + "metadata": {}, + "outputs": [], + "source": [ + "num_df = data.select_dtypes(include = np.number)\n", + "cat_df = data.select_dtypes(include = object)" + ] + }, + { + "cell_type": "code", + "execution_count": 253, + "metadata": {}, + "outputs": [], + "source": [ + "num_df = num_df.drop('Unnamed: 0', axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 254, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
danceabilityenergykeyloudnessmodespeechinessacousticnessinstrumentalnesslivenessvalencetempoduration_mstime_signature
00.4000.8967-13.92810.04070.28600.5020000.32300.6330152.5202518934
10.5620.6722-13.60510.05320.41100.0005940.12200.3480141.1602182004
20.3830.8295-12.32210.05970.57600.0000050.19400.7720166.5301722674
30.3390.8585-13.16610.05310.56800.0000010.11400.7150169.8491747734
40.3780.8495-12.91710.06580.51600.0000000.12600.8160168.0261871334
..........................................
69970.6390.4919-13.90600.06160.09750.9020000.10700.3990114.5811840764
69980.9080.4351-8.58310.06760.27900.5000000.08890.9280124.9801269034
69990.4210.3464-9.70000.03590.95100.9110000.10200.035356.2651250495
70000.5460.4750-9.67010.05570.64400.1070000.13100.257075.0061244794
70010.5530.6138-11.71900.05050.10100.7300000.32500.408092.8571393474
\n", + "

7002 rows × 13 columns

\n", + "
" + ], + "text/plain": [ + " danceability energy key loudness mode speechiness acousticness \\\n", + "0 0.400 0.896 7 -13.928 1 0.0407 0.2860 \n", + "1 0.562 0.672 2 -13.605 1 0.0532 0.4110 \n", + "2 0.383 0.829 5 -12.322 1 0.0597 0.5760 \n", + "3 0.339 0.858 5 -13.166 1 0.0531 0.5680 \n", + "4 0.378 0.849 5 -12.917 1 0.0658 0.5160 \n", + "... ... ... ... ... ... ... ... \n", + "6997 0.639 0.491 9 -13.906 0 0.0616 0.0975 \n", + "6998 0.908 0.435 1 -8.583 1 0.0676 0.2790 \n", + "6999 0.421 0.346 4 -9.700 0 0.0359 0.9510 \n", + "7000 0.546 0.475 0 -9.670 1 0.0557 0.6440 \n", + "7001 0.553 0.613 8 -11.719 0 0.0505 0.1010 \n", + "\n", + " instrumentalness liveness valence tempo duration_ms \\\n", + "0 0.502000 0.3230 0.6330 152.520 251893 \n", + "1 0.000594 0.1220 0.3480 141.160 218200 \n", + "2 0.000005 0.1940 0.7720 166.530 172267 \n", + "3 0.000001 0.1140 0.7150 169.849 174773 \n", + "4 0.000000 0.1260 0.8160 168.026 187133 \n", + "... ... ... ... ... ... \n", + "6997 0.902000 0.1070 0.3990 114.581 184076 \n", + "6998 0.500000 0.0889 0.9280 124.980 126903 \n", + "6999 0.911000 0.1020 0.0353 56.265 125049 \n", + "7000 0.107000 0.1310 0.2570 75.006 124479 \n", + "7001 0.730000 0.3250 0.4080 92.857 139347 \n", + "\n", + " time_signature \n", + "0 4 \n", + "1 4 \n", + "2 4 \n", + "3 4 \n", + "4 4 \n", + "... ... \n", + "6997 4 \n", + "6998 4 \n", + "6999 5 \n", + "7000 4 \n", + "7001 4 \n", + "\n", + "[7002 rows x 13 columns]" + ] + }, + "execution_count": 254, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "num_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Skaling" + ] + }, + { + "cell_type": "code", + "execution_count": 255, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
0123456789101112
0-0.5406191.0688080.530273-1.1682640.576581-0.3377090.0820211.900405-0.1030950.4438521.0587190.2589310.2435
10.4127850.036137-0.875517-1.0847890.576581-0.2185200.522632-0.409819-0.760495-0.7300270.658908-0.0490340.2435
2-0.6406670.759928-0.032043-0.7532130.576581-0.1565411.104237-0.412535-0.5250081.0163761.551795-0.4688770.2435
3-0.8996170.893622-0.032043-0.9713350.576581-0.2194731.076038-0.412550-0.7866600.7816001.668606-0.4459710.2435
4-0.6700930.852131-0.032043-0.9069840.576581-0.0983770.892744-0.412556-0.7474131.1976071.604446-0.3329970.2435
\n", + "
" + ], + "text/plain": [ + " 0 1 2 3 4 5 6 \\\n", + "0 -0.540619 1.068808 0.530273 -1.168264 0.576581 -0.337709 0.082021 \n", + "1 0.412785 0.036137 -0.875517 -1.084789 0.576581 -0.218520 0.522632 \n", + "2 -0.640667 0.759928 -0.032043 -0.753213 0.576581 -0.156541 1.104237 \n", + "3 -0.899617 0.893622 -0.032043 -0.971335 0.576581 -0.219473 1.076038 \n", + "4 -0.670093 0.852131 -0.032043 -0.906984 0.576581 -0.098377 0.892744 \n", + "\n", + " 7 8 9 10 11 12 \n", + "0 1.900405 -0.103095 0.443852 1.058719 0.258931 0.2435 \n", + "1 -0.409819 -0.760495 -0.730027 0.658908 -0.049034 0.2435 \n", + "2 -0.412535 -0.525008 1.016376 1.551795 -0.468877 0.2435 \n", + "3 -0.412550 -0.786660 0.781600 1.668606 -0.445971 0.2435 \n", + "4 -0.412556 -0.747413 1.197607 1.604446 -0.332997 0.2435 " + ] + }, + "execution_count": 255, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# K-Means is a distance based algorithm: we need to scale / normalize:\n", + "from sklearn.preprocessing import StandardScaler\n", + "num_df_prep = StandardScaler().fit_transform(num_df)\n", + "\n", + "pd.DataFrame(num_df_prep).head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clustering" + ] + }, + { + "cell_type": "code", + "execution_count": 256, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "KMeans(n_clusters=3, random_state=42)" + ] + }, + "execution_count": 256, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.cluster import KMeans\n", + "\n", + "kmeans = KMeans(n_clusters=3, random_state=42)\n", + "kmeans.fit(num_df_prep)" + ] + }, + { + "cell_type": "code", + "execution_count": 257, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0, 1, 1, ..., 2, 2, 1])" + ] + }, + "execution_count": 257, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Predicting / assigning the clusters:\n", + "clusters = kmeans.predict(num_df_prep)\n", + "clusters" + ] + }, + { + "cell_type": "code", + "execution_count": 258, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 2787\n", + "1 2918\n", + "2 1297\n", + "dtype: int64" + ] + }, + "execution_count": 258, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Check the size of the clusters\n", + "pd.Series(clusters).value_counts().sort_index()" + ] + }, + { + "cell_type": "code", + "execution_count": 260, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
danceabilityenergykeyloudnessmodespeechinessacousticnessinstrumentalnesslivenessvalencetempoduration_mstime_signaturecluster
28050.6390.7557-6.92800.08370.0178000.0000000.24300.360147.86822480041
64160.4870.5756-12.84500.04210.3290000.0105000.96200.469121.56817052140
27220.3320.7699-11.50310.07650.0036700.0041300.03720.525157.37835477440
12210.2190.8880-5.28010.05530.0000100.0000110.68900.245126.82052828040
13930.3670.6404-10.04310.04410.2100000.3540000.11400.287146.69951596040
56260.1800.9440-13.21410.08720.0019500.9280000.93500.124177.34820140040
50540.5140.6240-9.28700.08420.5130000.0051700.30700.755166.72616305341
63110.4070.86011-11.15200.03310.0547000.2220000.94500.871144.00124581440
68610.4130.81311-12.19610.05200.0000540.0055800.09190.729124.17610126731
17130.6180.9087-5.48410.03200.4910000.0002320.09030.705133.29916313341
\n", + "
" + ], + "text/plain": [ + " danceability energy key loudness mode speechiness acousticness \\\n", + "2805 0.639 0.755 7 -6.928 0 0.0837 0.017800 \n", + "6416 0.487 0.575 6 -12.845 0 0.0421 0.329000 \n", + "2722 0.332 0.769 9 -11.503 1 0.0765 0.003670 \n", + "1221 0.219 0.888 0 -5.280 1 0.0553 0.000010 \n", + "1393 0.367 0.640 4 -10.043 1 0.0441 0.210000 \n", + "5626 0.180 0.944 0 -13.214 1 0.0872 0.001950 \n", + "5054 0.514 0.624 0 -9.287 0 0.0842 0.513000 \n", + "6311 0.407 0.860 11 -11.152 0 0.0331 0.054700 \n", + "6861 0.413 0.813 11 -12.196 1 0.0520 0.000054 \n", + "1713 0.618 0.908 7 -5.484 1 0.0320 0.491000 \n", + "\n", + " instrumentalness liveness valence tempo duration_ms \\\n", + "2805 0.000000 0.2430 0.360 147.868 224800 \n", + "6416 0.010500 0.9620 0.469 121.568 170521 \n", + "2722 0.004130 0.0372 0.525 157.378 354774 \n", + "1221 0.000011 0.6890 0.245 126.820 528280 \n", + "1393 0.354000 0.1140 0.287 146.699 515960 \n", + "5626 0.928000 0.9350 0.124 177.348 201400 \n", + "5054 0.005170 0.3070 0.755 166.726 163053 \n", + "6311 0.222000 0.9450 0.871 144.001 245814 \n", + "6861 0.005580 0.0919 0.729 124.176 101267 \n", + "1713 0.000232 0.0903 0.705 133.299 163133 \n", + "\n", + " time_signature cluster \n", + "2805 4 1 \n", + "6416 4 0 \n", + "2722 4 0 \n", + "1221 4 0 \n", + "1393 4 0 \n", + "5626 4 0 \n", + "5054 4 1 \n", + "6311 4 0 \n", + "6861 3 1 \n", + "1713 4 1 " + ] + }, + "execution_count": 260, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Explore the cluster assignment in the original dataset\n", + "clustered_df = pd.DataFrame(num_df)\n", + "clustered_df[\"cluster\"] = clusters\n", + "clustered_df.sample(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 261, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "72394.89422540777" + ] + }, + "execution_count": 261, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# \"performance metric\"\n", + "kmeans.inertia_" + ] + }, + { + "cell_type": "code", + "execution_count": 262, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0, 0, 0], dtype=int64)" + ] + }, + "execution_count": 262, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.metrics import pairwise_distances_argmin_min\n", + "\n", + "new_df = clustered_df.drop('cluster', axis=1).loc[[0]]\n", + "closest, _ = pairwise_distances_argmin_min(kmeans.cluster_centers_, new_df)\n", + "closest" + ] + }, + { + "cell_type": "code", + "execution_count": 264, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
danceabilityenergykeyloudnessmodespeechinessacousticnessinstrumentalnesslivenessvalencetempoduration_mstime_signature
00.40.8967-13.92810.04070.2860.5020.3230.633152.522518934
\n", + "
" + ], + "text/plain": [ + " danceability energy key loudness mode speechiness acousticness \\\n", + "0 0.4 0.896 7 -13.928 1 0.0407 0.286 \n", + "\n", + " instrumentalness liveness valence tempo duration_ms time_signature \n", + "0 0.502 0.323 0.633 152.52 251893 4 " + ] + }, + "execution_count": 264, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Check whether or not the song is in the Billboard Hot 200." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create Soup" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "What URL do you want to use? https://www.billboard.com/charts/billboard-200\n", + "soup created!\n" + ] + } + ], + "source": [ + "url = input(\"What URL do you want to use? \")\n", + "response = requests.get(url)\n", + "\n", + "if response.status_code == 200:\n", + " soup = BeautifulSoup(response.content, \"html.parser\")\n", + " print('soup created!')\n", + " \n", + "else:\n", + " print('ERROR: ',response.status_code)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pick \"Ingredients\" from Soup :)" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": {}, + "outputs": [], + "source": [ + "songs = soup.find_all(\"span\", class_=\"chart-element__information__song text--truncate color--primary\")\n", + "artists = soup.find_all(\"span\", class_=\"chart-element__information__artist text--truncate color--secondary\")\n", + "last_week_ranks = soup.find_all(\"span\", class_= \"chart-element__meta text--center color--secondary text--last\")\n", + "peak_ranks = soup.find_all(\"span\", class_= \"chart-element__meta text--center color--secondary text--peak\")\n", + "weeks_on_chart = soup.find_all(\"span\", class_= \"chart-element__meta text--center color--secondary text--week\") " + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": {}, + "outputs": [], + "source": [ + "# 6. Get the text\n", + "for i in [songs, artists, last_week_ranks, peak_ranks, weeks_on_chart]:\n", + " for j in range(len(i)):\n", + " i[j] = i[j].getText()" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
songartistlast_week_rankpeak_rankweeks_on_chart
52TravellerChris Stapleton541317
1Certified Lover BoyDrake215
161BLAME IT ON BABYDaBaby145177
57YHLQMDLGBad Bunny53284
12Happier Than EverBillie Eilish9110
128MoanaSoundtrack1322252
139Dont Smile At MeBillie Eilish13614198
189IGORTyler, The Creator-1104
22Legends Never DieJuice WRLD24165
179The AnxietyTHE ANXIETY: WILLOW & Tyler Cole1531414
\n", + "
" + ], + "text/plain": [ + " song artist last_week_rank \\\n", + "52 Traveller Chris Stapleton 54 \n", + "1 Certified Lover Boy Drake 2 \n", + "161 BLAME IT ON BABY DaBaby 145 \n", + "57 YHLQMDLG Bad Bunny 53 \n", + "12 Happier Than Ever Billie Eilish 9 \n", + "128 Moana Soundtrack 132 \n", + "139 Dont Smile At Me Billie Eilish 136 \n", + "189 IGOR Tyler, The Creator - \n", + "22 Legends Never Die Juice WRLD 24 \n", + "179 The Anxiety THE ANXIETY: WILLOW & Tyler Cole 153 \n", + "\n", + " peak_rank weeks_on_chart \n", + "52 1 317 \n", + "1 1 5 \n", + "161 1 77 \n", + "57 2 84 \n", + "12 1 10 \n", + "128 2 252 \n", + "139 14 198 \n", + "189 1 104 \n", + "22 1 65 \n", + "179 141 4 " + ] + }, + "execution_count": 130, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 7. Build a dataframe\n", + "\n", + "billboard = pd.DataFrame(\n", + " {\"song\": songs,\n", + " \"artist\": artists,\n", + " \"last_week_rank\": last_week_ranks,\n", + " \"peak_rank\": peak_ranks,\n", + " \"weeks_on_chart\": weeks_on_chart}\n", + ")\n", + "\n", + "billboard.sample(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Check for song in Billboard" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Is there any artist you like? Chris Stapleton\n", + "What song do you like? Traveller\n", + "Here is: Traveller from Chris Stapleton\n" + ] + } + ], + "source": [ + "user_artist = input(\"Is there any artist you like? \")\n", + "user_song = input(\"What song do you like? \")\n", + "\n", + "pos = -1\n", + "for index, song in enumerate(billboard['song'].tolist()):\n", + " if song == user_song:\n", + " pos = index\n", + "\n", + "if pos >=0:\n", + " print('Here is: ',billboard['song'].iloc[pos], 'from', user_artist)\n", + "else:\n", + " print(user_song,' is not a song in the Billboard Hot 200!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Collect the audio features from the Spotify API." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Initialize SpotiPy with user credentials" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "metadata": {}, + "outputs": [], + "source": [ + "client_id = '8d4cb3eae1394f10b8b5f97826cd53ba'\n", + "client_secret = '8b4dd0a7b41c474ba0a986c104160f06'\n", + "client_credentials_manager = SpotifyClientCredentials(client_id=client_id, client_secret=client_secret)\n", + "sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "metadata": {}, + "outputs": [], + "source": [ + "searchResults = sp.search(q=\"artist:\" + user_artist + \" track:\" + user_song, type=\"track\")" + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'tracks': {'href': 'https://api.spotify.com/v1/search?query=artist%3AChris+Stapleton+track%3ATraveller&type=track&offset=0&limit=10',\n", + " 'items': [{'album': {'album_type': 'album',\n", + " 'artists': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/4YLtscXsxbVgi031ovDDdh'},\n", + " 'href': 'https://api.spotify.com/v1/artists/4YLtscXsxbVgi031ovDDdh',\n", + " 'id': '4YLtscXsxbVgi031ovDDdh',\n", + " 'name': 'Chris Stapleton',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:4YLtscXsxbVgi031ovDDdh'}],\n", + " 'available_markets': ['AD',\n", + " 'AE',\n", + " 'AG',\n", + " 'AL',\n", + " 'AM',\n", + " 'AO',\n", + " 'AR',\n", + " 'AT',\n", + " 'AU',\n", + " 'AZ',\n", + " 'BA',\n", + " 'BB',\n", + " 'BD',\n", + " 'BE',\n", + " 'BF',\n", + " 'BG',\n", + " 'BH',\n", + " 'BI',\n", + " 'BJ',\n", + " 'BN',\n", + " 'BO',\n", + " 'BR',\n", + " 'BS',\n", + " 'BT',\n", + " 'BW',\n", + " 'BY',\n", + " 'BZ',\n", + " 'CA',\n", + " 'CH',\n", + " 'CI',\n", + " 'CL',\n", + " 'CM',\n", + " 'CO',\n", + " 'CR',\n", + " 'CV',\n", + " 'CW',\n", + " 'CY',\n", + " 'CZ',\n", + " 'DE',\n", + " 'DJ',\n", + " 'DK',\n", + " 'DM',\n", + " 'DO',\n", + " 'DZ',\n", + " 'EC',\n", + " 'EE',\n", + " 'EG',\n", + " 'ES',\n", + " 'FI',\n", + " 'FJ',\n", + " 'FM',\n", + " 'FR',\n", + " 'GA',\n", + " 'GB',\n", + " 'GD',\n", + " 'GE',\n", + " 'GH',\n", + " 'GM',\n", + " 'GN',\n", + " 'GQ',\n", + " 'GR',\n", + " 'GT',\n", + " 'GW',\n", + " 'GY',\n", + " 'HK',\n", + " 'HN',\n", + " 'HR',\n", + " 'HT',\n", + " 'HU',\n", + " 'ID',\n", + " 'IE',\n", + " 'IL',\n", + " 'IN',\n", + " 'IS',\n", + " 'IT',\n", + " 'JM',\n", + " 'JO',\n", + " 'JP',\n", + " 'KE',\n", + " 'KG',\n", + " 'KH',\n", + " 'KI',\n", + " 'KM',\n", + " 'KN',\n", + " 'KR',\n", + " 'KW',\n", + " 'KZ',\n", + " 'LA',\n", + " 'LB',\n", + " 'LC',\n", + " 'LI',\n", + " 'LK',\n", + " 'LR',\n", + " 'LS',\n", + " 'LT',\n", + " 'LU',\n", + " 'LV',\n", + " 'MA',\n", + " 'MC',\n", + " 'MD',\n", + " 'ME',\n", + " 'MG',\n", + " 'MH',\n", + " 'MK',\n", + " 'ML',\n", + " 'MN',\n", + " 'MO',\n", + " 'MR',\n", + " 'MT',\n", + " 'MU',\n", + " 'MV',\n", + " 'MW',\n", + " 'MX',\n", + " 'MY',\n", + " 'MZ',\n", + " 'NA',\n", + " 'NE',\n", + " 'NG',\n", + " 'NI',\n", + " 'NL',\n", + " 'NO',\n", + " 'NP',\n", + " 'NR',\n", + " 'NZ',\n", + " 'OM',\n", + " 'PA',\n", + " 'PE',\n", + " 'PG',\n", + " 'PH',\n", + " 'PK',\n", + " 'PL',\n", + " 'PS',\n", + " 'PT',\n", + " 'PW',\n", + " 'PY',\n", + " 'QA',\n", + " 'RO',\n", + " 'RS',\n", + " 'RU',\n", + " 'RW',\n", + " 'SA',\n", + " 'SB',\n", + " 'SC',\n", + " 'SE',\n", + " 'SG',\n", + " 'SI',\n", + " 'SK',\n", + " 'SL',\n", + " 'SM',\n", + " 'SN',\n", + " 'SR',\n", + " 'ST',\n", + " 'SV',\n", + " 'SZ',\n", + " 'TD',\n", + " 'TG',\n", + " 'TH',\n", + " 'TL',\n", + " 'TN',\n", + " 'TO',\n", + " 'TR',\n", + " 'TT',\n", + " 'TV',\n", + " 'TW',\n", + " 'TZ',\n", + " 'UA',\n", + " 'UG',\n", + " 'US',\n", + " 'UY',\n", + " 'UZ',\n", + " 'VC',\n", + " 'VN',\n", + " 'VU',\n", + " 'WS',\n", + " 'XK',\n", + " 'ZA',\n", + " 'ZM',\n", + " 'ZW'],\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/album/7lxHnls3yQNl8B9bILmHj7'},\n", + " 'href': 'https://api.spotify.com/v1/albums/7lxHnls3yQNl8B9bILmHj7',\n", + " 'id': '7lxHnls3yQNl8B9bILmHj7',\n", + " 'images': [{'height': 640,\n", + " 'url': 'https://i.scdn.co/image/ab67616d0000b273540fc1d083eac5bcff8dad21',\n", + " 'width': 640},\n", + " {'height': 300,\n", + " 'url': 'https://i.scdn.co/image/ab67616d00001e02540fc1d083eac5bcff8dad21',\n", + " 'width': 300},\n", + " {'height': 64,\n", + " 'url': 'https://i.scdn.co/image/ab67616d00004851540fc1d083eac5bcff8dad21',\n", + " 'width': 64}],\n", + " 'name': 'Traveller',\n", + " 'release_date': '2015-05-04',\n", + " 'release_date_precision': 'day',\n", + " 'total_tracks': 14,\n", + " 'type': 'album',\n", + " 'uri': 'spotify:album:7lxHnls3yQNl8B9bILmHj7'},\n", + " 'artists': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/4YLtscXsxbVgi031ovDDdh'},\n", + " 'href': 'https://api.spotify.com/v1/artists/4YLtscXsxbVgi031ovDDdh',\n", + " 'id': '4YLtscXsxbVgi031ovDDdh',\n", + " 'name': 'Chris Stapleton',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:4YLtscXsxbVgi031ovDDdh'}],\n", + " 'available_markets': ['AD',\n", + " 'AE',\n", + " 'AG',\n", + " 'AL',\n", + " 'AM',\n", + " 'AO',\n", + " 'AR',\n", + " 'AT',\n", + " 'AU',\n", + " 'AZ',\n", + " 'BA',\n", + " 'BB',\n", + " 'BD',\n", + " 'BE',\n", + " 'BF',\n", + " 'BG',\n", + " 'BH',\n", + " 'BI',\n", + " 'BJ',\n", + " 'BN',\n", + " 'BO',\n", + " 'BR',\n", + " 'BS',\n", + " 'BT',\n", + " 'BW',\n", + " 'BY',\n", + " 'BZ',\n", + " 'CA',\n", + " 'CH',\n", + " 'CI',\n", + " 'CL',\n", + " 'CM',\n", + " 'CO',\n", + " 'CR',\n", + " 'CV',\n", + " 'CW',\n", + " 'CY',\n", + " 'CZ',\n", + " 'DE',\n", + " 'DJ',\n", + " 'DK',\n", + " 'DM',\n", + " 'DO',\n", + " 'DZ',\n", + " 'EC',\n", + " 'EE',\n", + " 'EG',\n", + " 'ES',\n", + " 'FI',\n", + " 'FJ',\n", + " 'FM',\n", + " 'FR',\n", + " 'GA',\n", + " 'GB',\n", + " 'GD',\n", + " 'GE',\n", + " 'GH',\n", + " 'GM',\n", + " 'GN',\n", + " 'GQ',\n", + " 'GR',\n", + " 'GT',\n", + " 'GW',\n", + " 'GY',\n", + " 'HK',\n", + " 'HN',\n", + " 'HR',\n", + " 'HT',\n", + " 'HU',\n", + " 'ID',\n", + " 'IE',\n", + " 'IL',\n", + " 'IN',\n", + " 'IS',\n", + " 'IT',\n", + " 'JM',\n", + " 'JO',\n", + " 'JP',\n", + " 'KE',\n", + " 'KG',\n", + " 'KH',\n", + " 'KI',\n", + " 'KM',\n", + " 'KN',\n", + " 'KR',\n", + " 'KW',\n", + " 'KZ',\n", + " 'LA',\n", + " 'LB',\n", + " 'LC',\n", + " 'LI',\n", + " 'LK',\n", + " 'LR',\n", + " 'LS',\n", + " 'LT',\n", + " 'LU',\n", + " 'LV',\n", + " 'MA',\n", + " 'MC',\n", + " 'MD',\n", + " 'ME',\n", + " 'MG',\n", + " 'MH',\n", + " 'MK',\n", + " 'ML',\n", + " 'MN',\n", + " 'MO',\n", + " 'MR',\n", + " 'MT',\n", + " 'MU',\n", + " 'MV',\n", + " 'MW',\n", + " 'MX',\n", + " 'MY',\n", + " 'MZ',\n", + " 'NA',\n", + " 'NE',\n", + " 'NG',\n", + " 'NI',\n", + " 'NL',\n", + " 'NO',\n", + " 'NP',\n", + " 'NR',\n", + " 'NZ',\n", + " 'OM',\n", + " 'PA',\n", + " 'PE',\n", + " 'PG',\n", + " 'PH',\n", + " 'PK',\n", + " 'PL',\n", + " 'PS',\n", + " 'PT',\n", + " 'PW',\n", + " 'PY',\n", + " 'QA',\n", + " 'RO',\n", + " 'RS',\n", + " 'RU',\n", + " 'RW',\n", + " 'SA',\n", + " 'SB',\n", + " 'SC',\n", + " 'SE',\n", + " 'SG',\n", + " 'SI',\n", + " 'SK',\n", + " 'SL',\n", + " 'SM',\n", + " 'SN',\n", + " 'SR',\n", + " 'ST',\n", + " 'SV',\n", + " 'SZ',\n", + " 'TD',\n", + " 'TG',\n", + " 'TH',\n", + " 'TL',\n", + " 'TN',\n", + " 'TO',\n", + " 'TR',\n", + " 'TT',\n", + " 'TV',\n", + " 'TW',\n", + " 'TZ',\n", + " 'UA',\n", + " 'UG',\n", + " 'US',\n", + " 'UY',\n", + " 'UZ',\n", + " 'VC',\n", + " 'VN',\n", + " 'VU',\n", + " 'WS',\n", + " 'XK',\n", + " 'ZA',\n", + " 'ZM',\n", + " 'ZW'],\n", + " 'disc_number': 1,\n", + " 'duration_ms': 222413,\n", + " 'explicit': False,\n", + " 'external_ids': {'isrc': 'USUM71418080'},\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/track/178OI1A3qjROeFeh8lmNwW'},\n", + " 'href': 'https://api.spotify.com/v1/tracks/178OI1A3qjROeFeh8lmNwW',\n", + " 'id': '178OI1A3qjROeFeh8lmNwW',\n", + " 'is_local': False,\n", + " 'name': 'Traveller',\n", + " 'popularity': 66,\n", + " 'preview_url': None,\n", + " 'track_number': 1,\n", + " 'type': 'track',\n", + " 'uri': 'spotify:track:178OI1A3qjROeFeh8lmNwW'},\n", + " {'album': {'album_type': 'album',\n", + " 'artists': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of'},\n", + " 'href': 'https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of',\n", + " 'id': '0LyfQWJT6nXafLPZqxe9Of',\n", + " 'name': 'Various Artists',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:0LyfQWJT6nXafLPZqxe9Of'}],\n", + " 'available_markets': ['US'],\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/album/05vM360htYvHMa6uhgve1v'},\n", + " 'href': 'https://api.spotify.com/v1/albums/05vM360htYvHMa6uhgve1v',\n", + " 'id': '05vM360htYvHMa6uhgve1v',\n", + " 'images': [{'height': 640,\n", + " 'url': 'https://i.scdn.co/image/ab67616d0000b2734317283394a87191a9678d3c',\n", + " 'width': 640},\n", + " {'height': 300,\n", + " 'url': 'https://i.scdn.co/image/ab67616d00001e024317283394a87191a9678d3c',\n", + " 'width': 300},\n", + " {'height': 64,\n", + " 'url': 'https://i.scdn.co/image/ab67616d000048514317283394a87191a9678d3c',\n", + " 'width': 64}],\n", + " 'name': \"NOW That's What I Call Music! Outlaw Country\",\n", + " 'release_date': '2021-03-19',\n", + " 'release_date_precision': 'day',\n", + " 'total_tracks': 18,\n", + " 'type': 'album',\n", + " 'uri': 'spotify:album:05vM360htYvHMa6uhgve1v'},\n", + " 'artists': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/4YLtscXsxbVgi031ovDDdh'},\n", + " 'href': 'https://api.spotify.com/v1/artists/4YLtscXsxbVgi031ovDDdh',\n", + " 'id': '4YLtscXsxbVgi031ovDDdh',\n", + " 'name': 'Chris Stapleton',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:4YLtscXsxbVgi031ovDDdh'}],\n", + " 'available_markets': ['US'],\n", + " 'disc_number': 1,\n", + " 'duration_ms': 217960,\n", + " 'explicit': False,\n", + " 'external_ids': {'isrc': 'USUM71418080'},\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/track/181dawXbdv8rhrvbVhJYDn'},\n", + " 'href': 'https://api.spotify.com/v1/tracks/181dawXbdv8rhrvbVhJYDn',\n", + " 'id': '181dawXbdv8rhrvbVhJYDn',\n", + " 'is_local': False,\n", + " 'name': 'Traveller',\n", + " 'popularity': 5,\n", + " 'preview_url': None,\n", + " 'track_number': 1,\n", + " 'type': 'track',\n", + " 'uri': 'spotify:track:181dawXbdv8rhrvbVhJYDn'}],\n", + " 'limit': 10,\n", + " 'next': None,\n", + " 'offset': 0,\n", + " 'previous': None,\n", + " 'total': 2}}" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "searchResults" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def artist_tracks(artists):\n", + " \n", + " '''\n", + " Takes a list of artist names, iterates through their Spotify albums, checks for \n", + " duplicate albums, then appends all the tracks in those albums to a list of lists\n", + " '''\n", + " \n", + " # Each list in this list will be a track and its features\n", + " tracks = []\n", + " \n", + " for artist in tqdm_notebook(artists):\n", + " \n", + " # Get the artist URI (a unique ID)\n", + " artist_uri = sp.search(artist)['tracks']['items'][0]['artists'][0]['uri']\n", + "\n", + " # Spotify has a lot of duplicate albums, but we'll cross-reference them with this list to avoid extra loops\n", + " album_checker = []\n", + " \n", + " # The starting point of our loop of albums for those artists with more than 50\n", + " n = 0\n", + " \n", + " # Note the album_type = 'album'. This discounts singles, compilations and collaborations\n", + " while len(sp.artist_albums(artist_uri, album_type = 'album', limit=50, offset = n)['items']) > 0:\n", + " \n", + " # Avoid overloading Spotify with requests by assigning the list of album dictionaries to a variable\n", + " dict_list = sp.artist_albums(artist_uri, album_type = 'album', limit=50, offset = n)['items']\n", + " \n", + " for i, album in tqdm_notebook(enumerate(dict_list)):\n", + "\n", + " # Add the featured artists for the album in question to the checklist\n", + " check_this_album = [j['name'] for j in dict_list[i]['artists']]\n", + " # And the album name\n", + " check_this_album.append(dict_list[i]['name'])\n", + " # And its date\n", + " check_this_album.append(dict_list[i]['release_date'])\n", + "\n", + " # Only continue looping if that album isn't in the checklist\n", + " if check_this_album not in album_checker:\n", + " \n", + " # Add this album to the checker\n", + " album_checker.append(check_this_album)\n", + " # For every song on the album, get its descriptors and features in a list and add to the tracklist\n", + " tracks.extend([[artist, album['name'], album['uri'], song['name'],\n", + "\n", + " album['release_date']] + list(sp.audio_features(song['uri'])[0].values()) \n", + " for song in sp.album_tracks(album['uri'])['items']])\n", + " \n", + " # Go through the next 50 albums (otherwise we'll get an infinite while loop)\n", + " n += 50\n", + "\n", + " return tracks" + ] + }, + { + "cell_type": "code", + "execution_count": 224, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":11: TqdmDeprecationWarning: This function will be removed in tqdm==5.0.0\n", + "Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook`\n", + " for artist in tqdm_notebook(artists):\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fdd0d29ba1f4467a824605122e1373df", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=0.0, max=15.0), HTML(value='')))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":28: TqdmDeprecationWarning: This function will be removed in tqdm==5.0.0\n", + "Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook`\n", + " for i, album in tqdm_notebook(enumerate(dict_list)):\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "71c753061a0e4b958aeed0880152312c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a558d587eac34ab999514ea24b401886", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "71e061c60b2c4159af125e98f5e71427", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "22119734e5b640de8720583b7dffeaea", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ceb868fe53784914b47c18b888c69f06", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=''), FloatProgress(value=1.0, bar_style='info', layout=Layout(width='20px'), max=1.0…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "HTTP Error for GET to https://api.spotify.com/v1/search with Params: {'q': ' ', 'limit': 10, 'offset': 0, 'type': 'track', 'market': None} returned 404 due to Not found.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n" + ] + }, + { + "ename": "SpotifyException", + "evalue": "http status: 404, code:-1 - https://api.spotify.com/v1/search?q=+&limit=10&offset=0&type=track:\n Not found., reason: None", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mHTTPError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\spotipy\\client.py\u001b[0m in \u001b[0;36m_internal_call\u001b[1;34m(self, method, url, payload, params)\u001b[0m\n\u001b[0;32m 244\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 245\u001b[1;33m \u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mraise_for_status\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 246\u001b[0m \u001b[0mresults\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mjson\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\requests\\models.py\u001b[0m in \u001b[0;36mraise_for_status\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 952\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mhttp_error_msg\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 953\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mHTTPError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mhttp_error_msg\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mresponse\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 954\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mHTTPError\u001b[0m: 404 Client Error: Not Found for url: https://api.spotify.com/v1/search?q=+&limit=10&offset=0&type=track", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[1;31mSpotifyException\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0martist_df\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0martist_tracks\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0muser_artist\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32m\u001b[0m in \u001b[0;36martist_tracks\u001b[1;34m(artists)\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;31m# Get the artist URI (a unique ID)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 14\u001b[1;33m \u001b[0martist_uri\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msearch\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0martist\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'tracks'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'items'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'artists'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'uri'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 15\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 16\u001b[0m \u001b[1;31m# Spotify has a lot of duplicate albums, but we'll cross-reference them with this list to avoid extra loops\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\spotipy\\client.py\u001b[0m in \u001b[0;36msearch\u001b[1;34m(self, q, limit, offset, type, market)\u001b[0m\n\u001b[0;32m 551\u001b[0m \u001b[0mfrom_token\u001b[0m\u001b[1;33m.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 552\u001b[0m \"\"\"\n\u001b[1;32m--> 553\u001b[1;33m return self._get(\n\u001b[0m\u001b[0;32m 554\u001b[0m \u001b[1;34m\"search\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mq\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mq\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlimit\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mlimit\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0moffset\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0moffset\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mtype\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmarket\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mmarket\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 555\u001b[0m )\n", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\spotipy\\client.py\u001b[0m in \u001b[0;36m_get\u001b[1;34m(self, url, args, payload, **kwargs)\u001b[0m\n\u001b[0;32m 295\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 296\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 297\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_internal_call\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"GET\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0murl\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpayload\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 298\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 299\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0m_post\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0murl\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpayload\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\anaconda3\\lib\\site-packages\\spotipy\\client.py\u001b[0m in \u001b[0;36m_internal_call\u001b[1;34m(self, method, url, payload, params)\u001b[0m\n\u001b[0;32m 265\u001b[0m )\n\u001b[0;32m 266\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 267\u001b[1;33m raise SpotifyException(\n\u001b[0m\u001b[0;32m 268\u001b[0m \u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstatus_code\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 269\u001b[0m \u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mSpotifyException\u001b[0m: http status: 404, code:-1 - https://api.spotify.com/v1/search?q=+&limit=10&offset=0&type=track:\n Not found., reason: None" + ] + } + ], + "source": [ + "artist_df = artist_tracks(user_artist)" + ] + }, + { + "cell_type": "code", + "execution_count": 225, + "metadata": {}, + "outputs": [], + "source": [ + "def df_tracks(tracklist):\n", + " \n", + " '''\n", + " Takes the output of artist_tracks (i.e. a list of lists),\n", + " puts it in a dataframe and formats it.\n", + " '''\n", + "\n", + " df = pd.DataFrame(tracklist, columns=['artist',\n", + " 'album_name',\n", + " 'album_uri',\n", + " 'track',\n", + " 'release_date'] + list(sp.audio_features('7EAqBCOVkDZcbccjxZmgjp')[0].keys()))\n", + "\n", + " df.rename(columns={'uri':'song_uri'}, inplace=True)\n", + "\n", + " df.drop_duplicates(subset=['artist', 'track', 'release_date'], inplace=True)\n", + "\n", + " # Reorder the cols to have identifiers first, auditory features last\n", + " cols = ['artist', 'album_name', 'album_uri', 'track', 'release_date', 'id', 'song_uri', 'track_href',\n", + " 'analysis_url', 'type', 'danceability', 'energy', 'key', 'loudness', 'mode', 'speechiness',\n", + " 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo', 'duration_ms', 'time_signature']\n", + "\n", + " df = df[cols]\n", + " \n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": 226, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'NoneType' object has no attribute 'keys'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msearchresult_df\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdf_tracks\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0martist_df\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32m\u001b[0m in \u001b[0;36mdf_tracks\u001b[1;34m(tracklist)\u001b[0m\n\u001b[0;32m 10\u001b[0m \u001b[1;34m'album_uri'\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;34m'track'\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 12\u001b[1;33m 'release_date'] + list(sp.audio_features('7EAqBCOVkDZcbccjxZmgjp')[0].keys()))\n\u001b[0m\u001b[0;32m 13\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[0mdf\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrename\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcolumns\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m{\u001b[0m\u001b[1;34m'uri'\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;34m'song_uri'\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minplace\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'keys'" + ] + } + ], + "source": [ + "searchresult_df = df_tracks(artist_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 227, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'searchresult_df' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msearchresult_df\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;31mNameError\u001b[0m: name 'searchresult_df' is not defined" + ] + } + ], + "source": [ + "searchresult_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## send the Spotify audio features of the submitted song to the clustering model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}