# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Random Forest Challenge - Spotify Dataset

# Developing a high accuracy model based on spotify songs (labelled 1 for 'like'  or 0 for 'dislike') to predict whether this particular user will like or dislike a song he/she has never heard.

# ## Checklist
# 1. Import Dataset
# 2. EDA to visualize data and observe structure
# 3. Train a classifier
# 4. Predict target using the trained classifier

# ## Imports

# +
import pandas as pd
import numpy as np

import scipy
import matplotlib.pyplot as plt

from sklearn.model_selection import cross_val_score
from sklearn import neighbors
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn import datasets
from sklearn import ensemble

import time

import seaborn as sns

# %matplotlib inline
# -

spotify = pd.read_csv('spotify.csv')

type(spotify)

# ## Understanding the Data

spotify.info()

spotify.describe()

spotify.head()

# ## EDA

# +
pos_tempo = spotify[spotify['target'] == 1]['tempo']
neg_tempo = spotify[spotify['target'] == 0]['tempo']

pos_dance = spotify[spotify['target'] == 1]['danceability']
neg_dance = spotify[spotify['target'] == 0]['danceability']

pos_duration = spotify[spotify['target'] == 1]['duration_ms']
neg_duration = spotify[spotify['target'] == 0]['duration_ms']

pos_loudness = spotify[spotify['target'] == 1]['loudness']
neg_loudness = spotify[spotify['target'] == 0]['loudness']

pos_speechiness = spotify[spotify['target'] == 1]['speechiness']
neg_speechiness = spotify[spotify['target'] == 0]['speechiness']

pos_valence = spotify[spotify['target'] == 1]['valence']
neg_valence = spotify[spotify['target'] == 0]['valence']

pos_energy = spotify[spotify['target'] == 1]['energy']
neg_energy = spotify[spotify['target'] == 0]['energy']

pos_acousticness = spotify[spotify['target'] == 1]['acousticness']
neg_acousticness = spotify[spotify['target'] == 0]['acousticness']

pos_key = spotify[spotify['target'] == 1]['key']
neg_key = spotify[spotify['target'] == 0]['key']

pos_instrumentalness = spotify[spotify['target'] == 1]['instrumentalness']
neg_instrumentalness = spotify[spotify['target'] == 0]['instrumentalness']
# -

fig = plt.figure(figsize=(12,8))
plt.title('Song Tempo Like / Dislike Distribution')
pos_tempo.hist(alpha = 0.7, bins = 30, label = 'positive')
neg_tempo.hist(alpha = 0.7, bins = 30, label = 'negative')
plt.legend(loc= 'upper right')

# Not too much to extract from what we're seeing.

# +
fig2 = plt.figure(figsize= (15, 15))

# Danceability
ax3 = fig2.add_subplot(331)
ax3.set_xlabel('Danceability')
ax3.set_ylabel('Count')
ax3.set_title('Song Danceability Like Distribution')
pos_dance.hist(alpha = 0.5, bins = 30)
ax4 = fig2.add_subplot(331)
neg_dance.hist(alpha = 0.5, bins=30)

# Duration
ax5 = fig2.add_subplot(332)
pos_duration.hist(alpha=0.5, bins=30)
ax5.set_xlabel('Duration (ms)')
ax5.set_ylabel('Count')
ax5.set_title("Song Duration Like Distribution")
ax6 = fig2.add_subplot (332)
neg_duration.hist(alpha=0.5, bins=30)

# Loudness
ax7 = fig2.add_subplot (333)
pos_loudness.hist (alpha=0.5, bins=30)
ax7.set_xlabel('Loudness')
ax7.set_ylabel('Count')
ax7.set_title("Song Loudness Like Distribution")

ax8 = fig2.add_subplot(333)
neg_loudness.hist(alpha=0.5, bins=30)
               
# Speechiness
ax9 = fig2.add_subplot (334)
pos_speechiness.hist(alpha=0.5, bins=30)
ax9.set_xlabel('Speechiness')
ax9.set_ylabel('Count')
ax9.set_title("Song Speechiness Like Distribution")
ax10 = fig2.add_subplot (334)
neg_speechiness.hist(alpha=0.5, bins=30)

# Valence
ax11 = fig2.add_subplot(335)
pos_valence. hist(alpha=0.5, bins=30)
ax11.set_xlabel("Valence")
ax11.set_ylabel('Count')
ax11.set_title("Song Valence Like Distribution")
ax12 = fig2.add_subplot(335)
neg_valence.hist(alpha=0.5, bins=30)

# Energy
ax13 = fig2.add_subplot (336)
pos_energy. hist(alpha=0.5, bins=32)
ax13.set_xlabel('Energy')
ax13.set_ylabel('Count')
ax13.set_title("Song Energy Like Distribution")
ax14 = fig2.add_subplot (336)
neg_energy. hist(alpha=0.5, bins=30)

# Key
ax15 = fig2.add_subplot (337)
pos_key.hist(alpha=0.5, bins=30)
ax15.set_xlabel("Key")
ax15.set_ylabel('Count')
ax15.set_title("Song Key Like Distribution")
ax15 = fig2.add_subplot (337)
neg_key. hist(alpha=0.5, bins=30)
                
# Acousticness
ax16 = fig2.add_subplot (338)
pos_acousticness.hist(alpha=0.5, bins=30)
ax16.set_xlabel('Acousticness')
ax16.set_ylabel('Count')
ax16.set_title("Song Acousticness Like Distribution")
ax16 = fig2.add_subplot (338)
neg_acousticness.hist(alpha=0.5, bins=30)

# Instrumentalness
ax17 = fig2.add_subplot (339)
pos_instrumentalness.hist(alpha=0.5, bins=30)
ax17.set_xlabel('Instrumentalness')
ax17.set_ylabel('Count')
ax17.set_title("Song Instrumentalness Like Distribution")
ax17 = fig2.add_subplot (339)
neg_instrumentalness.hist(alpha=0.5, bins=30)
# -

# Key maps to standard musical notation, 0 is key of C

# - E flat or D sharp has the lowest number of observations as well as the highest relative distribution of dislike to like.
# - Slight bias towards preferring a positive valence
# - Slight preference for slightly lower energy songs relative to higher energy songs but a large number of disliked songs on the lower end of the energy spectrum
# - Not much structure for Acousticness or instrumentalness. Most of the songs listened to are at the very extreme low end. Same thing goes for speechiness.
# - Strong dislike for songs on the louder end of the spectrum.

# ## Modeling

# ### Decision Tree Implementation

# +
# This is the model we'll be using.
from sklearn import tree

# A convenience for displaying visualizations.
from IPython.display import Image

# Packages for rendering our tree.
import pydotplus
import graphviz

# +
Y = spotify['target']

X = spotify[['tempo', 'danceability', 'loudness', 'valence', 'energy',
             'instrumentalness', 'acousticness', 'key',
             'speechiness', 'duration_ms']]
# -

X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=465)

from sklearn.metrics import accuracy_score

print(len(y_test), len(y_pred))

# +
# Initialize and train our tree.
start_time = time.time()

decision_tree = tree.DecisionTreeClassifier(
    criterion='entropy',
    max_features=1,
    max_depth=4
)

dt = decision_tree.fit(X_train, y_train)

# Render our tree.
dot_data = tree.export_graphviz(
    decision_tree, out_file=None,
    feature_names= X_train.columns,
    class_names=['Liked', 'Disliked'],
    filled=True
)
graph = pydotplus.graph_from_dot_data(dot_data)

print("--- %s seconds ---" % (time.time() - start_time))

Image(graph.create_png())
# -

y_pred = dt.predict(X_test)

pred

accuracy_score(y_test, y_pred)

# ### Random Forest

# Without dummies:

# +
start_time = time.time()

print("--- %s seconds ---" % (time.time() - start_time))

rfc = ensemble.RandomForestClassifier()
cross_val_score(rfc, X, Y, cv=5)
# -

# With dummies:

# +
start_time = time.time()

rfc = ensemble.RandomForestClassifier()
X_dummies = pd.get_dummies(X)

print("--- %s seconds ---" % (time.time() - start_time))

cross_val_score(rfc, X_dummies, Y, cv=5)
# -

# ## Final Thoughts

# Very good predictive accuracy to begin with. could improve through feature engineering and hyperparameter tuning.

StarcoderdataJupyterScriptsDedupFiltered