What the Buck?

Buck talk

Sam Albers

7 minute read

I recently appeared on my pal Morgan Tams’ radio program on Cortes Island Radio. The idea is to appear weekly and talk about a single artist/band for 15 minutes. Not terribly ambitious but I thought it would be fun to explore the spotify API and generate some summaries of who we are talking about. For our first installment, Morgan and I chatted about the legendary Buck Owens. Darn—I just lost 10 minutes of my life searching for Buck Owens gifs. Oh well. Here is a still of the man:

Packages

Thankfully there is the R package — spotifyr — that makes requesting data from the spotify API very easy. Since spotifyr is on CRAN we can install it like usual.

install.packages('spotifyr')

For this post I am also using the following packages which you will need to install.

install.packages('dplyr')
install.packages('usethis')
install.packages('ggplot2')
install.packages('tidyr')

and load:

library(spotifyr)
library(dplyr, warn.conflicts = FALSE)
library(usethis)
library(ggplot2)
library(tidyr)

There is some hocus-pocus to set up your credentials to access the spotify data which does require a paid spotify account. The spotifyr developer provides some nice instructions here and the spotify developer guide provides a few more details. Probably the most important thing to note here is that you want to save your spotify credentials in your .Renviron file. If you’ve never dealt with environment variables in R before, Efficient R programming provides a succinct description. In a nutshell our .Renviron file is a way for us to provide the value of a variable consistently across sessions and outside of a script. I always edit it with the usethis package:

edit_r_environ()

Setting up your credentials as environment variables is a one-time thing. After that, functions in the spotifyr package will just work as they all call get_spotify_access_token() by default. Now that I have all credential sorted out let’s try and see what we can find out about Buck from the spotify API.

buck_raw <- get_artist_audio_features('buck owens')

dim(buck_raw)
## [1] 1436   39

This is lots of information (39 columns just on Buck!). With these types of nested JSON data, dplyr’s glimpse function provides a nice clean way of looking at the data.

glimpse(buck_raw)
## Observations: 1,436
## Variables: 39
## $ artist_name                  <chr> "Buck Owens", "Buck Owens", "Buck...
## $ artist_id                    <chr> "2FMZn5P3WATd7Il6FgPJNu", "2FMZn5...
## $ album_id                     <chr> "4I2eUTxQVMHYWLyb3v5loL", "4I2eUT...
## $ album_type                   <chr> "album", "album", "album", "album...
## $ album_images                 <list> [<data.frame[3 x 3]>, <data.fram...
## $ album_release_date           <chr> "2019-11-08", "2019-11-08", "2019...
## $ album_release_year           <dbl> 2019, 2019, 2019, 2019, 2019, 201...
## $ album_release_date_precision <chr> "day", "day", "day", "day", "day"...
## $ danceability                 <dbl> 0.710, 0.555, 0.588, 0.728, 0.623...
## $ energy                       <dbl> 0.473, 0.367, 0.388, 0.437, 0.636...
## $ key                          <int> 5, 5, 4, 9, 9, 4, 3, 0, 9, 0, 3, ...
## $ loudness                     <dbl> -8.676, -8.055, -9.248, -7.907, -...
## $ mode                         <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
## $ speechiness                  <dbl> 0.0569, 0.0291, 0.0376, 0.0449, 0...
## $ acousticness                 <dbl> 0.8680, 0.8730, 0.8700, 0.7740, 0...
## $ instrumentalness             <dbl> 1.12e-04, 0.00e+00, 0.00e+00, 9.1...
## $ liveness                     <dbl> 0.2060, 0.2350, 0.1220, 0.0274, 0...
## $ valence                      <dbl> 0.878, 0.781, 0.723, 0.906, 0.611...
## $ tempo                        <dbl> 138.341, 130.753, 126.781, 137.12...
## $ track_id                     <chr> "0XNGYgnlIu45rsA8uF5ezp", "1yN9nS...
## $ analysis_url                 <chr> "https://api.spotify.com/v1/audio...
## $ time_signature               <int> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, ...
## $ artists                      <list> [<data.frame[1 x 6]>, <data.fram...
## $ available_markets            <list> [<"AD", "AE", "AR", "AT", "BE", ...
## $ disc_number                  <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
## $ duration_ms                  <int> 142813, 135373, 167373, 141560, 1...
## $ explicit                     <lgl> FALSE, FALSE, FALSE, FALSE, FALSE...
## $ track_href                   <chr> "https://api.spotify.com/v1/track...
## $ is_local                     <lgl> FALSE, FALSE, FALSE, FALSE, FALSE...
## $ track_name                   <chr> "Down On The Corner Of Love", "It...
## $ track_preview_url            <chr> "https://p.scdn.co/mp3-preview/f5...
## $ track_number                 <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11...
## $ type                         <chr> "track", "track", "track", "track...
## $ track_uri                    <chr> "spotify:track:0XNGYgnlIu45rsA8uF...
## $ external_urls.spotify        <chr> "https://open.spotify.com/track/0...
## $ album_name                   <chr> "Find Myself", "Find Myself", "Fi...
## $ key_name                     <chr> "F", "F", "E", "A", "A", "E", "D#...
## $ mode_name                    <chr> "major", "major", "major", "major...
## $ key_mode                     <chr> "F major", "F major", "E major", ...

This is too many columns for now. Let’s narrow our focus to make it easier to work with.

buck <- buck_raw %>% 
  select(album_release_date, album_release_year, danceability:tempo, time_signature, 
         duration_ms, track_name, album_name, key_mode) 

Summaries of Buck

With this data in hand I’ll make some rapid fire summaries of Buck Owens. These summaries turned out not to be particularly compelling radio material but I’m not going to let that deter me. Taking cue from the spotifyr package, what is Buck’s most common key?

buck %>% 
    count(key_mode, sort = TRUE)
## # A tibble: 22 x 2
##    key_mode     n
##    <chr>    <int>
##  1 G# major   237
##  2 A major    231
##  3 D# major   150
##  4 G major    121
##  5 E major    117
##  6 A# major    96
##  7 F major     90
##  8 C major     83
##  9 D major     75
## 10 F# major    75
## # ... with 12 more rows

The man loved G#/Ab major. It is a bit of unusual key and you can readily find some speculation online about why Buck might have tuned down a half step. And not much in the minor keys. I guess country finds sadness another way. How about time signature?

buck %>% 
    count(time_signature, sort = TRUE)
## # A tibble: 5 x 2
##   time_signature     n
##            <int> <int>
## 1              4  1269
## 2              3   149
## 3              5    15
## 4              1     2
## 5              0     1

A few suspect data points (zero time signature?) but overall Buck made a career of keep things pretty straight forward. Mostly 4/4 with the occasional waltz.

What about Buck’s album output. Let’s plot his cumulative albums over time:

cumulative_albums <- buck %>% 
  select(album_release_year, album_name) %>% 
  distinct(.keep_all = TRUE) %>% 
  count(album_release_year) %>% 
  arrange(album_release_year) %>% 
  mutate(albums = cumsum(n))

ggplot(cumulative_albums, aes(x = album_release_year, y = albums)) +
  geom_line()

Ok so this data isn’t particularly good. Likely what would be help is an original_release_date column. Buck was most active in the sixties while the data suggests his output was highest during the mid-nineties. Presumably these are re-issue dates. Still good to know — can’t rely on that year data.

The audio features available through the spotify api are very interesting numeric summaries of songs and will be fun to play around with. I won’t go into descriptions of each audio feature but we will calculate histograms of all Buck’s songs for each feature. Most features range between 0 and 1 so the distributions can give us a sense of Buck’s music tendencies.

buck %>% 
  select(danceability:tempo, duration_ms) %>% 
  gather() %>% 
  mutate(key = tools::toTitleCase(key)) %>% 
  ggplot() +
  geom_histogram(aes(x = value), fill = "blue") +
  facet_wrap(~key, scales = "free")

I really like looking at these distributions. Quite what they represent (or how they are derived) is something that I haven’t quite wrapped my brain around. However they do offer us some high level assessment of an artist’s catalogue. If the album release date info was better we could do some interesting retrospectives. In another post I’ll try to find a better example. Buck’s songs are reasonably dancy, don’t vary much in length and are very positive. This conflicts with my prior of country music being sad and is also likely an interesting hypothesis to further test in a future post.

Lastly let’s have a look and see if danceability is related to tempo.

cor.test(buck$danceability, buck$tempo, method = "spearman")
## 
##  Spearman's rank correlation rho
## 
## data:  buck$danceability and buck$tempo
## S = 589582638, p-value = 1.005e-13
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##        rho 
## -0.1946284
buck %>% 
  filter(danceability != 0) %>% 
  ggplot(aes(x = tempo, y = danceability)) +
  geom_point() +
  geom_smooth(method = "lm") 

There appears to be a very slight and negative relationship with danceability and tempo. If you are really dancing, you probably want that song to be short. We all only have so much stamina.

This has been a short usecase of using the spotify API and in particular the spotifyr package. It is actually pretty exciting to have so much music info at your fingertips.