diff --git a/solutions.ipynb b/solutions.ipynb new file mode 100644 index 0000000..f714237 --- /dev/null +++ b/solutions.ipynb @@ -0,0 +1,437 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Instructions\n", + "\n", + "To move forward with the project, you need to create a collection of songs with their audio features - as large as possible!\n", + "\n", + "These are the songs that we will cluster. And, later, when the user inputs a song, we will find the cluster to which the song belongs and recommend a song from the same cluster. The more songs you have, the more accurate and diverse recommendations you'll be able to give. Although... you might want to make sure the collected songs are \"curated\" in a certain way. Try to find playlists of songs that are diverse, but also that meet certain standards.\n", + "\n", + "The process of sending hundreds or thousands of requests can take some time - it's normal if you have to wait a few minutes (or, if you're ambitious, even hours) to get all the data you need.\n", + "\n", + "An idea for collecting as many songs as possible is to start with all the songs of a big, diverse playlist and then go to every artist present in the playlist and grab every song of every album of that artist. The amount of songs you'll be collecting per playlist will grow exponentially!" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# import libraries\n", + "import spotipy\n", + "from spotipy.oauth2 import SpotifyClientCredentials\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id=\"da55f67d57504befa94a4c8a982c7f77\",\n", + " client_secret=\"3b08e6c5c38744eeb726342990f814bc\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [], + "source": [ + "# found this function on the internet and changed it so it can return more than 100 results at a time\n", + "\n", + "def playlist_df(playlist_id):\n", + " \n", + " # Create empty dataframe\n", + " playlist_features_list = [\"artist\",\"album\",\"track_name\", \"track_id\",\"danceability\",\"energy\",\"key\",\"loudness\",\"mode\", \"speechiness\",\"instrumentalness\",\"liveness\",\"valence\",\"tempo\", \"duration_ms\",\"time_signature\"]\n", + " \n", + " playlist_df = pd.DataFrame(columns = playlist_features_list)\n", + " \n", + " # Loop through every track in the playlist, extract features and append the features to the playlist df\n", + "\n", + " results = sp.user_playlist_tracks(\"spotify\", playlist_id)\n", + " tracks = results['items']\n", + "\n", + " for oset in range(100,results['total'],100):\n", + " results = sp.user_playlist_tracks(\"spotify\", playlist_id, offset=oset)\n", + " tracks += results['items']\n", + "\n", + " \n", + " for track in tracks:\n", + " # Create empty dict\n", + " playlist_features = {}\n", + " # Get metadata\n", + " playlist_features[\"artist\"] = track[\"track\"][\"album\"][\"artists\"][0][\"name\"]\n", + " playlist_features[\"album\"] = track[\"track\"][\"album\"][\"name\"]\n", + " playlist_features[\"track_name\"] = track[\"track\"][\"name\"]\n", + " playlist_features[\"track_id\"] = track[\"track\"][\"id\"]\n", + " \n", + " # Get audio features\n", + " audio_features = sp.audio_features(playlist_features[\"track_id\"])[0]\n", + " for feature in playlist_features_list[4:]:\n", + " playlist_features[feature] = audio_features[feature]\n", + " \n", + " # Concat the dfs\n", + " track_df = pd.DataFrame(playlist_features, index = [0])\n", + " playlist_df = pd.concat([playlist_df, track_df], ignore_index = True)\n", + " \n", + " return playlist_df" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
| \n", + " | artist | \n", + "album | \n", + "track_name | \n", + "track_id | \n", + "danceability | \n", + "energy | \n", + "key | \n", + "loudness | \n", + "mode | \n", + "speechiness | \n", + "instrumentalness | \n", + "liveness | \n", + "valence | \n", + "tempo | \n", + "duration_ms | \n", + "time_signature | \n", + "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", + "Hozier | \n", + "Hozier (Deluxe) | \n", + "Take Me To Church | \n", + "7dS5EaCoMnN7DzlpT6aRn2 | \n", + "0.566 | \n", + "0.664 | \n", + "4 | \n", + "-5.303 | \n", + "0 | \n", + "0.0464 | \n", + "0 | \n", + "0.116 | \n", + "0.437 | \n", + "128.945 | \n", + "241688 | \n", + "4 | \n", + "
| 1 | \n", + "Mike Posner | \n", + "31 Minutes to Takeoff | \n", + "Cooler Than Me - Single Mix | \n", + "2V4bv1fNWfTcyRJKmej6Sj | \n", + "0.768 | \n", + "0.820 | \n", + "7 | \n", + "-4.630 | \n", + "0 | \n", + "0.0474 | \n", + "0 | \n", + "0.689 | \n", + "0.625 | \n", + "129.965 | \n", + "213293 | \n", + "4 | \n", + "
| 2 | \n", + "Tyler, The Creator | \n", + "Flower Boy | \n", + "See You Again (feat. Kali Uchis) | \n", + "7KA4W4McWYRpgf0fWsJZWB | \n", + "0.558 | \n", + "0.559 | \n", + "6 | \n", + "-9.222 | \n", + "1 | \n", + "0.0959 | \n", + "0.000007 | \n", + "0.109 | \n", + "0.620 | \n", + "78.558 | \n", + "180387 | \n", + "4 | \n", + "
| 3 | \n", + "Bastille | \n", + "Bad Blood | \n", + "Pompeii | \n", + "3gbBpTdY8lnQwqxNCcf795 | \n", + "0.679 | \n", + "0.715 | \n", + "9 | \n", + "-6.383 | \n", + "1 | \n", + "0.0407 | \n", + "0 | \n", + "0.271 | \n", + "0.571 | \n", + "127.435 | \n", + "214148 | \n", + "4 | \n", + "
| 4 | \n", + "Shakira | \n", + "Oral Fixation, Vol. 2 (Expanded Edition) | \n", + "Hips Don't Lie (feat. Wyclef Jean) | \n", + "3ZFTkvIE7kyPt6Nu3PEa7V | \n", + "0.778 | \n", + "0.824 | \n", + "10 | \n", + "-5.892 | \n", + "0 | \n", + "0.0707 | \n", + "0 | \n", + "0.405 | \n", + "0.758 | \n", + "100.024 | \n", + "218093 | \n", + "4 | \n", + "
| ... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "
| 5290 | \n", + "MARINA | \n", + "The Family Jewels | \n", + "Hermit the Frog | \n", + "4Zcz6saEkOII3PlXd9gN3o | \n", + "0.609 | \n", + "0.679 | \n", + "0 | \n", + "-4.545 | \n", + "1 | \n", + "0.0312 | \n", + "0 | \n", + "0.199 | \n", + "0.487 | \n", + "122.034 | \n", + "215960 | \n", + "4 | \n", + "
| 5291 | \n", + "Olivia Rodrigo | \n", + "deja vu | \n", + "deja vu | \n", + "61KpQadow081I2AsbeLcsb | \n", + "0.439 | \n", + "0.610 | \n", + "9 | \n", + "-7.236 | \n", + "1 | \n", + "0.1160 | \n", + "0.000011 | \n", + "0.341 | \n", + "0.172 | \n", + "181.088 | \n", + "215508 | \n", + "4 | \n", + "
| 5292 | \n", + "BIA | \n", + "FOR CERTAIN | \n", + "WHOLE LOTTA MONEY | \n", + "5yorXJWdBan1Vlh116ZtQ7 | \n", + "0.897 | \n", + "0.371 | \n", + "1 | \n", + "-5.019 | \n", + "1 | \n", + "0.3680 | \n", + "0 | \n", + "0.325 | \n", + "0.441 | \n", + "81.008 | \n", + "156005 | \n", + "4 | \n", + "
| 5293 | \n", + "Ashnikko | \n", + "DEMIDEVIL | \n", + "Slumber Party (feat. Princess Nokia) | \n", + "11ZulcYY4lowvcQm4oe3VJ | \n", + "0.964 | \n", + "0.398 | \n", + "11 | \n", + "-8.981 | \n", + "0 | \n", + "0.0795 | \n", + "0.000039 | \n", + "0.101 | \n", + "0.563 | \n", + "105.012 | \n", + "178405 | \n", + "4 | \n", + "
| 5294 | \n", + "Kali Uchis | \n", + "Sin Miedo (del Amor y Otros Demonios) ∞ | \n", + "telepatía | \n", + "6tDDoYIxWvMLTdKpjFkc1B | \n", + "0.653 | \n", + "0.524 | \n", + "11 | \n", + "-9.016 | \n", + "0 | \n", + "0.0502 | \n", + "0 | \n", + "0.203 | \n", + "0.553 | \n", + "83.970 | \n", + "160191 | \n", + "4 | \n", + "
5295 rows × 16 columns
\n", + "