This R Markdown document is a quick example of how interactive html output can be automated and organized. I’ll use the spotifyr package to pull in some simple data and make quick charts as an example for creating tabs and pills, using child docs, and utilizing Markdown to generate content programmatically.
The addition of {.tabset} tells markdown to apply the
predefined tabset class to downstream sub-headers. In this example we
start with a blank level one header (#) above. The
following level two headers (##) are then interpreted as
tabs.
# {.tabset}
## Creating Tabs
## Adding Pills
## Using Child Docs
## Programmatic Approach
Because the output of this R Markdown document is an
.html file, css (and html) can be used to customize any
part of the report. For example, I’ve added some css to customize the
appearance of the tabs in this example. The stylesheet is saved as
styles.css in the extras/ folder of this
project and applied very simply in the options.
---
title: "Automating Interactive R Markdown Reports"
output:
html_document:
css: extras/styles.css
---
Time to add some pills. Let’s pull in the first five songs from the Jagged Little Pill album by Alanis Morissette as an example.
tracks <- spotifyr::get_artist_audio_features('alanis morissette') %>%
filter(album_name == 'Jagged Little Pill') %>%
select(track_name) %>%
unique() %>%
head(5) %>%
pull()
Creating pills works very similar to tabs. Adding
{.tabset .tabset-pills} instead of {.tabset}
to a header will prompt markdown to apply to default tabset-pills class
to downstream headers. Here’s what it looks like in this example.
### `r paste(tracks[1])`
The 1st track of the Jagged Little Pill album is: `r paste(tracks[1])`
### `r paste(tracks[2])`
The 2nd track of the Jagged Little Pill album is: `r paste(tracks[2])`
### `r paste(tracks[3])`
The 3rd track of the Jagged Little Pill album is: `r paste(tracks[3])`
### `r paste(tracks[4])`
The 4th track of the Jagged Little Pill album is: `r paste(tracks[4])`
### `r paste(tracks[5])`
The 5th track of the Jagged Little Pill album is: `r paste(tracks[5])`
Which produces:
The 1st track of the Jagged Little Pill album is: All I Really Want
The 2nd track of the Jagged Little Pill album is: You Oughta Know
The 3rd track of the Jagged Little Pill album is: Perfect
The 4th track of the Jagged Little Pill album is: Hand in My Pocket
The 5th track of the Jagged Little Pill album is: Right Through You
Another method to keep all your code organized is to use child
documents. Here is how the parent .Rmd file of this markdown document is
set up. Notice that the {.tabset} called in the parent .Rmd
flows downstream to child .Rmd files.
---
title: "Automating Interactive R Markdown Reports"
output:
html_document:
css: extras/styles.css
includes:
in_header: extras/header-logo.html
---
```{r, include=FALSE, echo=FALSE}
sys.source("extras/setup.R", envir = knitr::knit_global())
```
This R Markdown document is a quick example of how interactive html output can be automated and organized. I'll use the spotifyr package to pull in some simple data and make quick charts as an example for creating tabs and pills, using child docs, and utilizing Markdown to generate content programmatically.
***
# {.tabset}
```{r, child=c('tab-1-tabs/tabs.Rmd', 'tab-2-pills/pills.Rmd', 'tab-3-child/child.Rmd', 'tab-4-auto/auto.Rmd', 'tab-5-demo/demo.Rmd')}
```
Also notice the
sys.source("extras/setup.R", envir = knitr::knit_global())
command. This can be used to load packages, authenticate the spotifyr
api connection, set knitr options, etc. all in one place and applied
globally.
library(tidyverse)
library(ggplot2)
library(patchwork)
library(spotifyr)
library(ggthemes)
library(flextable)
options(knitr.duplicate.label = "allow")
access_token <- get_spotify_access_token()
For repetitive tasks cat() can be used with
results='asis' to generate text that will be evaluated and
knit as markdown code. For example, looking at the Jagged Little Pill
album names again…
tracks <- spotifyr::get_artist_audio_features('alanis morissette') %>%
filter(album_name == 'Jagged Little Pill') %>%
select(track_name) %>%
unique() %>%
head(5) %>%
pull()
for (i in 1:length(tracks)) {
cat('\n\n### ', tracks[[i]], '\n\n')
print(paste0('The ', scales::ordinal(i), ' track of the Jagged Little Pill album is:', tracks[[i]]))
}
Produces:
The 1st track of the Jagged Little Pill album is: All I Really Want
The 2nd track of the Jagged Little Pill album is: You Oughta Know
The 3rd track of the Jagged Little Pill album is: Perfect
The 4th track of the Jagged Little Pill album is: Hand in My Pocket
The 5th track of the Jagged Little Pill album is: Right Through You
studio_albums <- c("Parachutes",
"A Rush of Blood to the Head",
"X&Y",
"Viva La Vida or Death and All His Friends",
"Mylo Xyloto",
"Ghost Stories",
"A Head Full of Dreams",
"Everyday Life",
"Music of the Spheres")
df <- get_artist_audio_features('coldplay') %>%
filter(album_name %in% studio_albums == TRUE)
artist_df <- df %>%
mutate(level = paste0("Artist: ", artist_name),
year = 01) %>%
group_by(level) %>%
transmute_at(c("year",
"tempo",
"valence",
"energy",
"danceability",
"loudness",
"liveness",
"instrumentalness",
"acousticness",
"speechiness"),
mean, na.rm = TRUE) %>%
ungroup() %>%
unique()
album_df <- df %>%
mutate(level = paste0("Album: ", album_name, " (", album_release_year, ")"),
year = album_release_year) %>%
group_by(level,year) %>%
transmute_at(c("tempo",
"valence",
"energy",
"danceability",
"loudness",
"liveness",
"instrumentalness",
"acousticness",
"speechiness"),
mean, na.rm = TRUE) %>%
ungroup() %>%
unique()
yellow_df <- df %>%
filter(track_name == "Yellow") %>%
mutate(level = paste0("Track: ", track_name),
year = 00) %>%
group_by(level, year) %>%
transmute_at(c("tempo",
"valence",
"energy",
"danceability",
"loudness",
"liveness",
"instrumentalness",
"acousticness",
"speechiness"),
mean, na.rm = TRUE) %>%
ungroup() %>%
unique()
df <- yellow_df %>%
union(artist_df) %>%
union(album_df) %>%
mutate(level = fct_reorder(level, desc(as.numeric(year))))
df %>%
mutate(across(where(is.numeric),
number_format)) %>%
flextable() %>%
fontsize(size = 8, part = "all")
level | year | tempo | valence | energy | danceability | loudness | liveness | instrumentalness | acousticness | speechiness |
Track: Yellow | 0 | 173 | 0.285 | 0.661 | 0.429 | -7.23 | 0.234 | 0.000 | 0.002 | 0.028 |
Artist: Coldplay | 1 | 124 | 0.258 | 0.530 | 0.410 | -9.99 | 0.217 | 0.297 | 0.341 | 0.042 |
Album: Everyday Life (2019) | 2019 | 126 | 0.299 | 0.427 | 0.459 | -12.06 | 0.196 | 0.359 | 0.605 | 0.053 |
Album: A Head Full of Dreams (2015) | 2015 | 124 | 0.230 | 0.638 | 0.464 | -8.32 | 0.178 | 0.252 | 0.282 | 0.041 |
Album: Ghost Stories (2014) | 2014 | 116 | 0.214 | 0.481 | 0.509 | -10.22 | 0.123 | 0.398 | 0.285 | 0.032 |
Album: Mylo Xyloto (2011) | 2011 | 112 | 0.250 | 0.520 | 0.388 | -10.47 | 0.320 | 0.245 | 0.274 | 0.037 |
Album: Viva La Vida or Death and All His Friends (2008) | 2008 | 126 | 0.261 | 0.651 | 0.320 | -8.36 | 0.207 | 0.429 | 0.132 | 0.038 |
Album: X&Y (2005) | 2005 | 132 | 0.217 | 0.601 | 0.401 | -8.26 | 0.221 | 0.015 | 0.164 | 0.037 |
Album: A Rush of Blood to the Head (2002) | 2002 | 138 | 0.174 | 0.516 | 0.463 | -7.43 | 0.166 | 0.083 | 0.347 | 0.029 |
Album: Parachutes (2000) | 2000 | 137 | 0.229 | 0.492 | 0.453 | -9.12 | 0.128 | 0.007 | 0.311 | 0.035 |
plotVar <- function(var_string) {
ggplot(df, aes(x = level, y = .data[[var_string]], fill = level)) +
geom_bar(stat = "identity") +
coord_flip() +
ggtitle(var_string) +
ggthemes::theme_fivethirtyeight(base_size = 10) +
theme(panel.background = element_rect(fill = "white")) +
theme(plot.background = element_rect(fill = "white")) +
theme(legend.position="none")
}
features <- colnames(df)[!colnames(df) %in% c("level","year")]
pp <- features %>%
map(~ plotVar(.x))
With the chunk option results = 'asis', you can write
out text as raw Markdown content, which can also be mixed with
plots.
for (i in 1:length(features)) {
cat('\n\n#### ', features[[i]], ' \n\n')
print(pp[[i]])
}