Anima Mundi

Analyser ses goûts musicaux grâce à l'API Spotify

Bonjour !

J'ai demandé par curiosité il y a quelques mois l'accès à mes données Spotify.

J'utilise ce service depuis plusieurs années, et j'étais curieux d'analyser mon écoute pour voir si j'y trouve des patterns intéressants. Ce fut l'occasion pour moi de découvrir le package R spotifyr qui fait que les tâches de récupération de données deviennent un jeu d'enfant ! je vais parler de ce super package ici, en illustrant les possibilités avec des graphiques interactifs générés avec plotly et ggplot2.

Je simplifie les traitements de données grâce aux packages du tidyverse, absolument incontournables et extrêmement puissants & flexibles.

I. Deux manières complémentaires de récupérer ses données

1. Fichiers d'historique Spotify

La demande d'accès est simple et rapide:

On reçoit ensuite un zip avec des tas de fichiers JSON qu'on peut alors lire et analyser avec l'outil de son choix.
La réception peut prendre jusqu'à 30j. En pratique, c'est plutôt une semaine.

2. Accès à l'API

Nécessite l'obtention de deux "clés":

Concrètement, il faut créer une nouvelle app à cet endroit.
Plus d'explications par ici

L'accès API permet d'accéder à une masse de données assez incroyable sur son écoute, mais également sur tout artiste, style, ou morceau de musique présent sur la plateforme Spotify.

II. Récupération & analyse:

On commence par charger les libraires dont on aura besoin pour la suite :

# pacman permet simplement de loader plusieurs paquets d'un coup :)
if (!require("pacman")) install.packages("pacman")
pacman::p_load(
  jsonlite,
  lubridate, gghighlight,spotifyr,tidyverse,knitr,ggplot2,plotly, dplyr)

1. Analyse à partir de l'historique Spotify (zip)

Je lis les fichiers JSON StreamingHistory (source fichier zip fourni par Spotify, préalablement téléchargé et décompressé), qui contiennent mon historique d'écoute que je concatène :

hist0 <- fromJSON("/Users/anas/Downloads/MyData/StreamingHistory0.json", flatten = TRUE)
hist1 <- fromJSON("/Users/anas/Downloads/MyData/StreamingHistory1.json", flatten = TRUE)
hist2 <- fromJSON("/Users/anas/Downloads/MyData/StreamingHistory2.json", flatten = TRUE)

streamHistory  <- rbind(rbind(hist0,hist1),hist2)

Le fichier comprend r dim(streamHistory)[1] lignes et ressemble à cela:

head(streamHistory) %>%
  kable()

à partir de là, beaucoup de tâches exploratoires sont possibles:

En sommant le temps passé à écouter chaque artiste, je peux par exemple facilement savoir lesquels ont été les plus écoutés

# MOST LISTENED ARTISTS (MORE THAN 3 HOURS)
minutesMostListened <- mySpotify %>%
  group_by(artistName) %>%
  summarize(minutesListened = sum(minutes)) %>%
  filter(minutesListened >= 360) %>%
  ggplot(aes(x = artistName, y = minutesListened)) +
  geom_col(aes(fill = minutesListened)) +
  scale_fill_gradient(low = "red", high = "green") +
  labs(x= "Artiste", y= "Nb de minutes d'écoute") +
  ggtitle("Artistes les plus écoutés") +
  theme(axis.text.x = element_text(angle = 90))
ggplotly(minutesMostListened)

Connaître les heures d'écoute intensive:

# PLAYBACK ACTIVITY BY TIME OF THE DAY
hoursDay <- mySpotify %>%
  filter(date >= "2021-01-01") %>%
  group_by(date, hour = hour(endTime), weekday = wday(date, label = TRUE)) %>%
  summarize(minutesListened = sum(minutes))

p <- hoursDay %>%
  ggplot(aes(x = hour, y = minutesListened, group = date)) +
  geom_col(fill = "#ff6600") +
  labs(x= "Heure de la journée", y= "Nombre de minutes d'écoute (sur 2021)") +
  ggtitle("Durée d'écoute par heure de la journée")  

ggplotly(p)

Même choses, en ajoutant la dimension "jour de la semaine":

# PLAYBACK ACTIVITY BY TIME OF THE DAY AND WEEKDAY
p <- hoursDay %>%
  group_by(weekday, hour) %>%
  summarize(minutes = sum(minutesListened)) %>%
  ggplot(aes(x = hour, weekday, fill = minutes)) +
  geom_tile() +
  scale_fill_gradient(low = "yellow", high = "red") +
  labs(x= "Heure de la journée", y= "Jour de la semaine") +
  ggtitle("Activité d'écoute par jour de la semaine")
 
ggplotly(p)

Vu autrement:

# PLAYBACK ACTIVITY BY TIME OF THE DAY AND WEEKDAY - LINE CHART
weekDay <- hoursDay %>%
  group_by(weekday, hour) %>%
  summarize(minutes = sum(minutesListened)) %>%
  ggplot(aes(x = hour, y = minutes, color = weekday)) +
  geom_line() +
  labs(x= "Heure de la journée", y= "Nombre de minutes d'écoute") +
  ggtitle("Activité par jour et heure")
ggplotly(weekDay)

2. Regardons à présent ce que peut nous offrir l'API Spotify

La première étape est de s'authentifier auprès de l'API pour être ensuite capable de l'interroger. Pour ce faire, il faut créer des variables d'environnement content SPOTIFY_CLIENT_ID et SPOTIFY_CLIENT_SECRET que tu as récupéré dans ton compte Spotify, puis utiliser la fonction get_spotify_access_token() pour établir une connexion:

Sys.setenv(SPOTIFY_CLIENT_ID = 'ton_token_personnel')
Sys.setenv(SPOTIFY_CLIENT_SECRET = 'ton_token_personnel')
access_token <- get_spotify_access_token()
Sys.setenv(SPOTIFY_CLIENT_ID = '56d1a42fe89b430b9f36f80334a5e946')
Sys.setenv(SPOTIFY_CLIENT_SECRET = '90e9272ad10c41d7bd4a0bb03ffebec5')
access_token <- get_spotify_access_token()

A partir de là, nous avons accès à une palette de données assez incroyable, qui donne un bon aperçu de la qualité et de la puissance de l'infrastructure data de Spotify.

Pour moi, la grande richesse de cette API réside dans le fait qu'elle donne accès aux audio features. En effet, tout morceau présent sur Spotify est décrit et représenté par un tas de métriques numériques le décrivant comme par exemple: Danceability, Valence, Energy, Tempo, Loudness, Speechiness, Instrumentalness, Liveness, Acousticness. Ces features numériques permettant de calculer des distances entre morceaux, elle ouvre un large spectre d'analyses. Un autre aspect est la "générosité" de cette API. Elle est en effet extrêmement riche en données, rapide et très réactive.

Voici une petite sélection de fonctions que j'ai trouvé amusantes à utiliser, et tu trouveras l'intégralité des fonctions documentées ICI :

1. get_my_top_artists_or_tracks() : récupérer ses articles ou titres les plus écoutés

Exemples d'utilisation:

get_my_top_artists_or_tracks(type = 'artists',
                             time_range = 'medium_term',
                             limit = 30) %>%
    sample_n(5) %>%
    select(.data$name, .data$genres, .data$popularity,.data$followers.total ) %>%
    rowwise %>%
    mutate(genres = paste(.data$genres, collapse = ', ')) %>%
    ungroup %>%
    kable()
get_my_top_artists_or_tracks(type = 'tracks',
                             time_range = 'medium_term',
                             limit = 50) %>%
    sample_n(10) %>%
    mutate(
        artist.name = map_chr(artists, function(x) x$name[1])
        ) %>%
    select(name, artist.name, album.name) %>%
    kable()

2. get_artist_audio_features() : récupérer les features audio de tous les morceaux d'un artiste

Regardons quels sont les 10 morceaux les plus "dansables" d'un groupe:

dance <- get_artist_audio_features("Salut C'est Cool")

dance %>%
    arrange(-danceability) %>%
    select(.data$track_name, .data$danceability) %>%
    head(10) %>%
    unique() %>%
    kable()

3. get_my_followed_artists() : récupérer les artistes auxquels on est abonné

get_my_followed_artists() %>%
    head() %>%
    kable()

4. get_my_playlists() : lister toutes ses playlists

Ce qui nous intéresse aussi, ce sont les URI de playlists, ressemblant à cela: spotify:playlist:3CnZy12b7GPl4aKy5vMfym. C'est surtout la dernière partie qui nous sera utile (après les deuxièmes ":")

5. get_playlist_audio_features() : récupérer les audio features des morceaux d'une playlist (dont on a récupéré l'identifiant grâce à la précédente fonction)

playlist_username <- 'Username Spotify du propriétaire de la playlist'
playlist_uri <- c("Identifiant issu de l'URI de la playlist récupéré précédemment")
playlist_AudioFeatures <- get_playlist_audio_features(playlist_username, playlist_uri)
playlist_username <- 'anselk'
playlist_uris <- c('7EIH3ec4xAgKrDW6gex1O6')
playlist_AudioFeatures <- get_playlist_audio_features(playlist_username, playlist_uris)

Morceaux peu populaires de la playlist:

plot <- playlist_AudioFeatures %>% sample_n(50) %>%
  group_by(track.popularity) %>%
  filter(track.popularity <= 5) %>%
  ggplot(aes(x = track.name, y = track.popularity)) +
  geom_col() +
  labs(x= "Morceau", y= "Popularité") +
  ggtitle("Morceaux peu populaires") + theme(axis.text.x = element_text(angle = 90))

ggplotly(plot)

6. get_my_saved_tracks() : récupérer les données des morceaux de la playlist "Titres likés"

myFavoriteTracks <- ceiling(get_my_saved_tracks(include_meta_info = TRUE)[['total']] / 50) %>%
  seq() %>%
  map(function(x) {
    get_my_saved_tracks(limit = 50, offset = (x - 1) * 50)
  }) %>%
  reduce(rbind) 

Je peux ensuite afficher les artistes les plus présents:

# GET TOP ARTISTS BASED ON LIKED TRACKS
favTracksArtist <- myFavoriteTracks %>%
  select(track.artists) %>%
  reduce(rbind) %>%
  reduce(rbind) %>%
  select(id, name)

trackNumArtist <- favTracksArtist %>%
  count(id, sort = TRUE) %>%
  left_join(favTracksArtist, by = 'id',.) %>%
  unique() %>%
  select(-id) %>%
  top_n(10, n)

# PLOT TOP 10 ARTISTS BASED ON LIKED TRACKS
plotMyFavs <- trackNumArtist %>%
  mutate(freq = case_when(n > 100 ~ '> 100 tracks',
      between(n, 50, 99) ~ '50-99 tracks',
      between(n, 20, 49) ~ '20-49 tracks',
      TRUE ~ '< 20 tracks')) %>%
  mutate(freq = factor(freq, levels = c('> 100 tracks', '50-99 tracks', '20-49 tracks', '< 20 tracks'))) %>%
  ggplot(mapping = aes(x = reorder(name, -n), y = n, fill = freq)) +
  geom_col() +
  scale_fill_brewer(palette="Dark2") +
  labs(x= "Artiste", y= "Nb de pistes", fill = NULL) +
  ggtitle("Top 10 des artistes les plus fréquents") +
  theme(axis.text.x = element_text(angle = 90))

ggplotly(plotMyFavs)

Voici pour la petite démo. Il est possible d'aller beaucoup plus loin, en faisant une classification non supervisée des morceaux qu'on aime pour essayer de dégager des thématiques correspondant à un "mood" ou à un moment de la journée. Les méthodes d'analyse en composante principale peuvent également être utilisées pour creuser le sujet de manière super intéressante.

Pour aller plus loin:

Enjoy !

Merci pour votre lecture ! 😊👋

~ Anas EL KHALOUI

#AI #Philosophy #Tech #TechPolicy #french