Writing about visualization, demographics, dashboards, and spatial data science.

Interested in learning more? Hire me for a workshop or to consult on your next project. See the Services page for more details.

Dropdown menus in R Markdown with bsselectR

· by Kyle Walker · Read in about 5 min · (920 Words)
R presentations R Markdown

I strongly believe that interactive reports, presentations, and scholarly articles are going to become much more prominent in the years ahead. Whereas a PDF article or presentation can often only show a limited aspect of a research project, interactive documents can allow a reader or presenter to explore project content in a much broader sense.

For dynamic research documents, an excellent option is to combine Shiny with R Markdown to generate a report that can execute R code from a Shiny server. However, not all interactive documents need the full power of Shiny. For example, I’m currently writing a report in which I’d like readers to be able to use drop-down menus to select charts to view. As I produced the charts in Python and saved them all to a folder, I don’t need Shiny to dynamically create the charts in the report.

To create the drop-down menu, I came across the bootstrap-select jQuery plugin. bootstrap-select adds Bootstrap’s dropdown.js and Bootstrap styling to re-style HTML <select> menus, which makes it a nice fit for R Markdown documents that default to this styling. While writing out the raw HTML and JavaScript to accomplish this worked in R Markdown, I found this tedious, so I decided to author bsselectR, an htmlwidget to do this automatically with R code. Many, many thanks are due to the developers of bootstrap-select for their hard work and excellent documentation.

bsselectR is not on CRAN, but you can install the development version from GitHub with devtools::install_github("walkerke/bsselectR"). View the source code at https://github.com/walkerke/bsselectR.

At the moment, there is one main function in bsselectR, bsselect, which will generate a bootstrap-select dropdown menu that allows you to interactively display text, images, or HTML documents as iframes. Pass a named vector of text or paths to images/HTML documents and specify the type as "text", "img", or "iframe", and bsselect will return a drop-down menu. On a basic level, bsselect can work outside of R Markdown, using the default Bootstrap styling. Let’s try a simple example:

library(bsselectR)

quotes <- c("Look deep into nature, and then you will understand everything better.", 
            "A fool thinks himself to be wise, but a wise man knows himself to be a fool.", 
            "My mission in life is not merely to survive, but to thrive; and to do so with some passion, some compassion, some humor, and some style.")

names(quotes) <- c("Einstein", "Shakespeare", "Angelou")

bsselect(quotes, type = "text")

Use the menu to flip through the three quotes.

A more practical example, however, would be the use of bsselect in an R Markdown document where the user could flip through a number of plots that are stored in a subdirectory. Let’s take a recent, applied example: scatterplots of the relationship between the percentage vote for Donald Trump and the percentage of county residents with at least a bachelor’s degree by state.

We’ll get the election data from Mike Kearney’s open presidential election dataset (updated December 9, so there may be some undercounts) and education data from the ACS using my personal acs14lite package. The following code will generate identical plots for each state, and save them in a subdirectory of my working directory named plots.

library(tidyverse)
library(scales)
library(acs14lite)

votes16 <- read_csv("https://raw.githubusercontent.com/mkearney/presidential_election_county_results_2016/master/pres.elect16.results.dec9.csv")

vars_to_get <- c("B15002_001E", "B15002_015E", "B15002_016E",  "B15002_017E",  "B15002_018E",
                 "B15002_032E",  "B15002_033E",  "B15002_034E",  "B15002_035E", "B01001_001E")

educ <- acs14(api_key = "Your key goes here", 
              geography = "county", variable = vars_to_get)

educ <- educ %>%
  mutate(pctba = (B15002_015E + B15002_016E + B15002_017E + B15002_018E + B15002_032E + 
                    B15002_033E + B15002_034E + B15002_035E)  / B15002_001E, 
         fips = paste0(state, county))

state_match <- data.frame(state_name = state.name, st = state.abb)

df <- votes16 %>%
  mutate(fips = stringr::str_pad(fips, 5, "left", "0")) %>%
  filter(cand == "Donald Trump") %>%
  inner_join(educ, by = "fips") %>%
  left_join(state_match, by = "st")

states <- as.character(sort(unique(df$state_name)))
states <- states[!is.na(states)]
states <- states[!states %in% c("District of Columbia", "Alaska")]

plots <- lapply(states, function(x) {
  
  state = filter(df, state_name == x)
  
  p <- ggplot(state, aes(x = pctba, 
              y = pct)) +
    geom_point(aes(size = B01001_001E), alpha = 0.5, color = "blue") + 
    geom_smooth(method = "lm") + 
    scale_y_continuous(labels = percent_format()) + 
    scale_x_continuous(labels = percent_format()) + 
    scale_size_continuous(labels = comma_format()) + 
    theme_minimal() + 
    labs(title = x, 
         x = "Percent over age 25 with at least a bachelor's degree", 
         y = "Percent voting Trump", 
         size = "Total population", 
         caption = "Data sources: Mike Kearney, 2010-2014 ACS")
  
  p
  
})

paths <- paste0(states, ".png")

pwalk(list(paths, plots), ggsave, path = "plots", dpi = 300)

Now, the following code in an R Markdown document knit from my working directory will get me interactivity without Shiny! I’ve also set the selected option to “Oregon”, and the parameters live_search and show_tick to TRUE, which allows for interactive searching of the drop-down menu and shows a check mark next to the selected option. The bootstrap-select dropdown adopts the theme of the R Markdown document, which in this case is spacelab.

library(stringr)
library(bsselectR)

state_plots <- paste0(list.files("plots", full.names = TRUE))
names(state_plots) <- str_replace_all(state_plots, 
                                      c("\\.png" = "", 
                                        "plots/" = ""))

bsselect(state_plots, type = "img", selected = "Oregon", 
         live_search = TRUE, show_tick = TRUE)

Publish your directory to your hosting service of choice, and you’ll have an interactive report to share like the one above.

bsselect also works in other R Markdown documents, such as presentations! The example below shows a bsselectR dropdown in a xaringan remark.js presentation. To avoid CSS/JS conflicts, I’ve wrapped the call to bsselect in bsselectR’s as_iframe function, which will save out the widget and embed it as an iframe.

I’d welcome feedback and pull requests; I’ll be adding features to the package bit by bit in the time ahead.