Katherine Anderson

By Katherine Anderson

Extension of Previous Tabsets Tutorial

A previous tutorial, “How to use tabsets in R Markdown”, introduced tabsets for grouping figures together into an interactive instance. In an R markdown notebook, users can define the tab labels with markdown code and write R code blocks between the labels for the figures that are displayed under each label.

This can be cumbersome if there are many tabs desired and code blocks
that need to be written under each label. It is also difficult if the desired tab labels come from data or parameters calculated within the notebook, and the user would need to manually figure out the labels and write them in markdown code.

Dynamic tabs can be programmatically generated, which allows users to group figures as defined by the data and reduce code clutter within the R markdown notebook as only one code block is needed rather than a separate code block for each tab. Using the diamonds dataset from the R package ggplot2, this post will show how to dynamically create tabs in an R markdown notebook.

Example of Tabs Created Manually

Suppose you would like to separate your data into groups and create similar figures for each group to display in a notebook. This could be done by manually figuring out the groups by looking at the data, then writing markdown tabs and code blocks for each group. The manual markdown tabs do not allow for the notebook to run potential new data with any new groups that appear. Below is an example of this issue where the notebook plots diamond price per carat for each cut group using tabsets.

Comparing Diamond Prices by Carat Across Different

Cut Qualities

Fair

Good

Very Good

Premium

Ideal

Code for Example Above:

The code blocks with “#” are written as standard markdown text in an R markdown notebook, and the code blocks with “ggplot” are written in an R code block. Notice how there is a lot of code that has been copied with the only change being the diamond cut quality value.

### Comparing Diamond Prices by Carat Across Different Cut Qualities {.tabset} 
#### Fair
ggplot(diamonds %>% filter(cut == 'Fair'), aes(x = carat)) +

  geom_point(aes(y = price, col = color)) +

  labs(x = '\nCarat',

       y = 'Price (USD $)\n',

       col = 'Color') +

  theme_bw()
#### Good
ggplot(diamonds %>% filter(cut == 'Good'), aes(x = carat)) +

  geom_point(aes(y = price, col = color)) +

  labs(x = '\nCarat',

       y = 'Price (USD $)\n',

       col = 'Color') +

  theme_bw()
#### Very Good
ggplot(diamonds %>% filter(cut == 'Very Good'), aes(x = carat)) +

  geom_point(aes(y = price, col = color)) +

  labs(x = '\nCarat',

       y = 'Price (USD $)\n',

       col = 'Color') +

  theme_bw()
#### Premium
ggplot(diamonds %>% filter(cut == 'Premium'), aes(x = carat)) +

  geom_point(aes(y = price, col = color)) +

  labs(x = '\nCarat',

       y = 'Price (USD $)\n',

       col = 'Color') +

  theme_bw()
#### Ideal
ggplot(diamonds %>% filter(cut == 'Ideal'), aes(x = carat)) +

  geom_point(aes(y = price, col = color)) +

  labs(x = '\nCarat',

       y = 'Price (USD $)\n',

       col = 'Color') + theme_bw()

Cleaner Code Using Data to Programmatically Generate Tabs

The same set of figures can be created in one R code block, looping through each diamond cut quality value and creating the necessary tabs programmatically.

### Comparing Diamond Prices by Carat Across Different Cut Qualities {.tabset} 

R code block header: {r, results='asis'}

tabs <- sort(unique(diamonds$cut))



for(tab in tabs) {

  cat('\n')

  cat('#### ', tab, '   \n')

  cat('\n')



  plot_values <- diamonds %>% filter(cut == tab)



  print(ggplot(plot_values, aes(x = carat)) +

          geom_point(aes(y = price, col = color)) +

          labs(x = '\nCarat',

               y = 'Price (USD $)\n',

               col = 'Color') +

          theme_bw())



  cat('\n')

}

There are a few things to note in how these tabs are generated and how other code interacts with it. As before, the parent heading (Comparing Diamond Prices by Carat Across Different Cut Qualities) is a level 3 heading, and each tab must be set as a level 4 heading using #### and concatenating it with the current tab value. The R code block header must include results='asis' so that the markdown lines are rendered properly. When the code block is configured this way, plots made using R code will only appear if explicitly printed with print(). Also, no new lines are printed unless generated with \n. Pretty much every plot or heading needs a new line after it to render the next figure correctly, so it is best to just include cat('\n') after every figure and heading. If you are looking to create nested tabs where you can select from another set of tabs beneath the parent tab, this can also be done in a code block.

Comparing Diamond Prices by Carat Across Different

Cut Qualities and Color

Fair

D

E

F

G

H

I

J

Good

D

E

F

G

H

I

J

Very Good

D

E

F

G

H

I

J

Premium

D

E

F

G

H

I

J

Ideal

D

E

F

G

H

I

J

Code for Example Above:

### Comparing Diamond Prices by Carat Across Different Cut Qualities and Color {.tabset} 

R code block header: {r, results='asis'}

tabs <- sort(unique(diamonds$cut))



for(tab in tabs) {

  cat('\n')

  cat('#### ', tab, '{.tabset}   \n')

  cat('\n')



  sub_values <- diamonds %>% filter(cut == tab)



  subtabs <- sort(unique(sub_values$color))



  for(subtab in subtabs) {

    cat('\n')

    cat('##### ', subtab, '   \n')

    cat('\n')

    plot_values <- sub_values %>% filter(color == subtab)



    print(ggplot(plot_values, aes(x = carat)) +

          geom_point(aes(y = price, col = clarity)) +

          labs(x = '\nCarat',

               y = 'Price (USD $)\n',

               col = 'Clarity') +

          theme_bw())



    cat('\n')

  }



}

These useful approaches for dynamically creating tabs in an R

markdown notebook can help in writing cleaner code and in improving data

understanding with different visualization techniques.