tl;dr
I used {trelliscopejs} to make an interactive ‘small multiples’ display for The Mountain Goats discography.
You can interact with an embedded version below or click here to open full screen.
Small multiples
The {trelliscopejs} R package by Ryan Hafen harnesses the power of his trelliscopejs-lib JavaScript library.
What does it do? It provides an interactive interface for visualising, organising and exploring data visualisations in small multiples.
What are ‘small multiples’? Rather than over-plotting data for multiple levels of some variable, you can facet by them into separate ‘panels’ and display the outputs side by side for easy comparison.
Ryan has written documentation, an introductory post and has created some trelliscopes using using gapminder and Pokemon data, for example.1 His package is relatively simple to use and does a lot of legwork to provide a nice interface for your data.
Goat discography
In a previous post I used the {spotifyr}, {genius} and {markovifyR} packages to generate new lyrics for the band The Mountain Goats.
The data from Spotify is interesting. It has musical information like key and tempo, but also audio features like ‘danceability’ and ‘acousticness’ scaled from 0 to 1. We also got links to album art in that data set.
I’m going to use these data in this post to provide a trelliscope example. Each panel will be a track; the visualisation will be album artwork rather than a plot; and audio features will be available to sort and filter data.
Ready the data
We’ll load {trelliscopejs} and some tidyverse packages to help us out.
library(trelliscopejs)
library(dplyr) # manipulate data
library(readr) # read data
library(tidyr) # get data into tidy form
Get data and simplify
You can follow the instructions in the previous post to get the data. I’m going to load those data from a pre-prepared RDS file.
# First to download the file
download.file(
"https://www.rostrum.blog/datasets/goat_discography.RDS", # data hosted here
"~/Documents/goat_discography.RDS" # where to put file locally
)
# Read the file from local source and check number of columns
raw_goat <- read_rds("~/Documents/goat_discography.RDS")
length(raw_goat)
## [1] 41
There’s 41 variables, so let’s simplify.
small_goat <- raw_goat %>%
unnest(available_markets) %>% # unnest character vector
filter(available_markets == "US") %>% # releases from one country only
select(
track_name, album_name, track_n, album_release_year, # track detail
duration_ms, key_mode, time_signature, # musical info
danceability, energy, speechiness, acousticness, # audio features
instrumentalness, liveness, valence, loudness # audio features
) %>%
arrange(desc(energy)) # order by 'energy' audio feature
glimpse(small_goat)
## Observations: 313
## Variables: 15
## $ track_name <chr> "Choked Out", "Pure Intentions", "If You See …
## $ album_name <chr> "Beat the Champ", "Bitter Melon Farm", "Get L…
## $ track_n <int> 5, 18, 10, 16, 14, 20, 17, 8, 21, 4, 2, 13, 3…
## $ album_release_year <dbl> 2015, 2002, 2006, 2011, 1996, 2012, 2002, 200…
## $ duration_ms <int> 102653, 134333, 118013, 167120, 234600, 16584…
## $ key_mode <chr> "D major", "A major", "C major", "B minor", "…
## $ time_signature <int> 4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, …
## $ danceability <dbl> 0.565, 0.648, 0.566, 0.422, 0.281, 0.681, 0.5…
## $ energy <dbl> 0.951, 0.934, 0.927, 0.903, 0.899, 0.895, 0.8…
## $ speechiness <dbl> 0.0808, 0.0989, 0.0642, 0.0682, 0.0526, 0.025…
## $ acousticness <dbl> 0.000239, 0.646000, 0.002930, 0.000620, 0.262…
## $ instrumentalness <dbl> 2.78e-05, 2.14e-01, 8.52e-01, 1.01e-02, 2.72e…
## $ liveness <dbl> 0.1320, 0.3890, 0.1990, 0.3420, 0.2080, 0.139…
## $ valence <dbl> 0.726, 0.424, 0.324, 0.860, 0.350, 0.970, 0.5…
## $ loudness <dbl> -3.970, -15.557, -8.047, -8.562, -9.177, -6.9…
Note that I’ve ordered by ‘energy’. The trelliscope output being created will be sorted in this variable.
Album artwork
Unnesting the available_markets
character vector removed the album_image
variable, which is a nested data frame with URLs to different-sized album artwork. We can grab unique album image URLs and join them back to our data set.
goat_pics <- raw_goat %>%
unnest(album_images) %>% # unnest dataframe fo URLs
filter(width == 640) %>% # just the largest images
select(album_name, url) %>% # simplify dataset
distinct(album_name, .keep_all = TRUE) # one unique entry per album
glimpse(goat_pics)
## Observations: 21
## Variables: 2
## $ album_name <chr> "In League with Dragons", "Goths", "Beat the Champ", …
## $ url <chr> "https://i.scdn.co/image/3896e2b47b548a33d0c9e9f66201…
And now to join the album image URLs back to our simplified data set.
small_goat_pics <- left_join(
x = small_goat,
y = goat_pics,
by = "album_name"
)
glimpse(small_goat_pics)
## Observations: 313
## Variables: 16
## $ track_name <chr> "Choked Out", "Pure Intentions", "If You See …
## $ album_name <chr> "Beat the Champ", "Bitter Melon Farm", "Get L…
## $ track_n <int> 5, 18, 10, 16, 14, 20, 17, 8, 21, 4, 2, 13, 3…
## $ album_release_year <dbl> 2015, 2002, 2006, 2011, 1996, 2012, 2002, 200…
## $ duration_ms <int> 102653, 134333, 118013, 167120, 234600, 16584…
## $ key_mode <chr> "D major", "A major", "C major", "B minor", "…
## $ time_signature <int> 4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, …
## $ danceability <dbl> 0.565, 0.648, 0.566, 0.422, 0.281, 0.681, 0.5…
## $ energy <dbl> 0.951, 0.934, 0.927, 0.903, 0.899, 0.895, 0.8…
## $ speechiness <dbl> 0.0808, 0.0989, 0.0642, 0.0682, 0.0526, 0.025…
## $ acousticness <dbl> 0.000239, 0.646000, 0.002930, 0.000620, 0.262…
## $ instrumentalness <dbl> 2.78e-05, 2.14e-01, 8.52e-01, 1.01e-02, 2.72e…
## $ liveness <dbl> 0.1320, 0.3890, 0.1990, 0.3420, 0.2080, 0.139…
## $ valence <dbl> 0.726, 0.424, 0.324, 0.860, 0.350, 0.970, 0.5…
## $ loudness <dbl> -3.970, -15.557, -8.047, -8.562, -9.177, -6.9…
## $ url <chr> "https://i.scdn.co/image/ecf370a7190fa0673b0a…
So the album artwork URL has been added to the url
column.
Prep for trelliscope
Now we have a nice tidy data frame, but I’m going to make a couple more changes to prepare the data for trelliscoping2. First, I need to use the img_panel()
function to declare that the album images should be the thing being visualised in each panel. Then I can rename the variables to make them look nicer when displayed.
prepared_goat <- small_goat_pics %>%
mutate(panel = img_panel(url)) %>% # identify as viz for panel
rename_all(tools::toTitleCase) %>% # first letter capitalised
rename(
Track = Track_name,
Album = Album_name,
`Track #` = Track_n,
Year = Album_release_year,
`Duration (ms)` = Duration_ms,
`Key mode` = Key_mode,
`Time sig` = Time_signature
) %>%
select(-Url) # discard unneeded variable
Generate trelliscope
Now we’re ready. The call to trelliscope()
takes the data set and then a bunch of other arguments like a title and subtitle and the default state for the number of rows and columns of panels and the default data to show on the panel under the visualisation.
trelliscope(
prepared_goat,
name = "The Mountain Goats discography",
desc = "Explore the Mountain Goats backcatalogue and filter and sort by audio features",
md_desc = "[The Mountain Goats](http://www.mountain-goats.com/) are a band. Data were collected from [Genius](https://genius.com/) and [Spotify](https://www.spotify.com/) APIs using the [{genius}](https://github.com/josiahparry/genius) and [{spotifyr}](https://www.rcharlie.com/spotifyr/) R packages, respectively.",
nrow = 2, ncol = 5, # arrangement of panels
state = list(labels = c("Track", "Album", "Track #", "Year", "Energy")), # display on panels
)
I’ve embedded the trelliscope below, but I recommend you click here to open it full screen.
Explore the data by altering the defaults in the grid, labels, filter and sort buttons in the left-hand navigation panel. Cycle through the panels with the arrows in the upper right. Hit the ‘?’ button in the upper right to get more information on trelliscope and its shortcuts.
Host your trelliscope
Use the argument path = "<file path to save to>"
in the trelliscope()
function to save the files (I learnt this from a GitHub issue). You can then host the folder’s contents somewhere. I put mine in the static/
directory of this site’s structure so it’s hosted on its own page at https://www.rostrum.blog/output/goat_scope/ (see Tim Mastny’s post).
Energy
I’ve ordered the panels of the trelliscope by the ‘energy’ of the tracks; most energetic first. The top track for energy is one of my favourites: ‘Choked Out’ from ‘Beat the Champ’. Here’s an embedded Spotify snippet.
Session info
## [1] "Last updated 2019-09-08"
## R version 3.5.2 (2018-12-20)
## Platform: x86_64-apple-darwin15.6.0 (64-bit)
## Running under: macOS High Sierra 10.13.6
##
## Locale: en_GB.UTF-8 / en_GB.UTF-8 / en_GB.UTF-8 / C / en_GB.UTF-8 / en_GB.UTF-8
##
## Package version:
## assertthat_0.2.1 autocogs_0.1.2
## backports_1.1.4 base64enc_0.1-3
## BH_1.69.0.1 blogdown_0.11
## bookdown_0.9 broom_0.5.2
## callr_3.2.0 checkmate_1.9.1
## cli_1.1.0 clipr_0.6.0
## colorspace_1.4-1 compiler_3.5.2
## crayon_1.3.4 digest_0.6.19
## diptest_0.75.7 DistributionUtils_0.6-0
## dplyr_0.8.1 evaluate_0.14
## fansi_0.4.0 generics_0.0.2
## ggplot2_3.1.1 glue_1.3.1
## graphics_3.5.2 grDevices_3.5.2
## grid_3.5.2 gtable_0.3.0
## hexbin_1.27.2 highr_0.8
## hms_0.4.2 htmltools_0.3.6
## htmlwidgets_1.3 httpuv_1.5.1
## jsonlite_1.6 knitr_1.23
## labeling_0.3 later_0.8.0
## lattice_0.20.38 lazyeval_0.2.2
## lubridate_1.7.4 magrittr_1.5
## markdown_1.0 MASS_7.3.51.1
## Matrix_1.2.16 mclust_5.4.1
## methods_3.5.2 mgcv_1.8.27
## mime_0.7 moments_0.14
## munsell_0.5.0 nlme_3.1.137
## pillar_1.4.1 pkgconfig_2.0.2
## plogr_0.2.0 plyr_1.8.4
## prettyunits_1.0.2 processx_3.3.1
## progress_1.2.2 promises_1.0.1
## ps_1.3.0 purrr_0.3.2
## R6_2.4.0 RColorBrewer_1.1.2
## Rcpp_1.0.2 readr_1.3.1
## reshape2_1.4.3 rlang_0.4.0
## rmarkdown_1.13 scales_1.0.0
## servr_0.13 splines_3.5.2
## stats_3.5.2 stringi_1.4.3
## stringr_1.4.0 tibble_2.1.3
## tidyr_0.8.3 tidyselect_0.2.5
## tinytex_0.13 tools_3.5.2
## trelliscopejs_0.1.18 utf8_1.1.4
## utils_3.5.2 vctrs_0.1.0
## viridisLite_0.3.0 webshot_0.5.1
## withr_2.1.2 xfun_0.7
## yaml_2.2.0 zeallot_0.1.0
I also once explored the use of Trelliscope for UK education data and have been meaning to write about it ever since.↩
Definitely a real verb.↩