1

I'm working on a project to make it easier to create flex/shiny dashboards from qualtrics surveys. I'd really like to be able to write a couple functions that would let co-workers who have less experience with R be able to make similar documents without having to know Rmarkdown syntax.

For example, if someone wanted to make a one page dashboard with a scatterplot, I'd like to be able to have them use a couple functions like (make_dashboard, make_page) etc:

make_dashboard(
  title = "Qualtrics Report Dashboard", 
  page 1 = make_page(header = "Page 1", format = "column", render = "plot", 
                     data = survey_data, variables = c("var1", "var2"))
)

which would then create a rmd file with this:

---
title: "Qualtrics Report Dashboard"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: scroll
    runtime: shiny
---

Page 1
=====================================  
renderPlot( {
ggplot(data = survey_data, mapping = aes_string(x = var1,
                                                      y = var2)) +
          geom_point() + 
          labs(x = get_label(get(var1, survey_data)),
               y = get_label(get(var2, survey_data)))
}
)

I haven't gotten very far with trying to write these functions / implement this logic, because I'm not even sure if I'm thinking about it in the right way - is it possible to create rmarkdown chunks with functions like this?

I've looked at other posts 1 and 2 about child documents in knitr, but I don't really want every chunk to be the same, rather have the person be able to change certain aspects (e.g. type of plot, data, etc.).

Emily
  • 77
  • 8
  • 1
    You can probably use [`knit_expand`](https://bookdown.org/yihui/rmarkdown-cookbook/knit-expand.html) to do this (though I'm not sure if that's necessarily the only or best way). There is a [vignette](https://cran.r-project.org/web/packages/knitr/vignettes/knit_expand.html) with some examples. There are also number of SO questions with `knit_expand` examples. – eipi10 Dec 21 '20 at 19:06
  • @eipi10 thank you! this is super helpful - I didn't realize you could pass variables/info into knit_expand knit_child combinations – Emily Dec 21 '20 at 19:44

1 Answers1

1

Not sure if this will be useful to anyone else, but I ended up using whisker (https://github.com/edwindj/whisker), which can render strings into documents to construct an Rmd in the style of flexdashboard.

TLDR: Essentially I made functions that create strings of text matching the building blocks of flexdashboard. With whisker, you can pass in variables by encasing words in the string with two bracket parentheses and then assigning their values with a list of var_name = value for each variable in the string, e.g.

template <- "My name is {{name}}."
d <- list(name = "Emily")
cat(whisker.render(template, data = d))
print(d)

My name is Emily

I used a combination of this and the str_c from stringr to construct strings for different elements of the flexdashboard, allowing the user to input variables like title, variable for plots, etc. that then could be rendered into the string using whisker. Then, I joined all of those strings together and render it into an Rmd file. Honestly, I am not sure this is actually easier for people who don't know R to use, and I'll probably end up doing something different, but I wanted to share in case anyone is thinking about this.

Example: running the chunk below creates a file called "test_dashboard.Rmd" with the text format for a flexdashboard with a 1 input sidebar and a single page with one plot.

```
make_dashboard(title = "Test Dashboard",
               sidebar = make_sidebar(sidebar_title = "here is the input",
                                      input_type = "multi-select",
                                      input_name = "Interesting Var #1"),
               page1 = make_page(page_title = "Cool Plots!",
                                 element_one = make_plot(plot_title = "this is my plot",
                                                         type = "bivariate",
                                                         vars = c("cool_var1",
                                                                  "cool_var2"))),
               fn = "test_dashboard")
```

OUTPUT:

```
---
title: Test Dashboard
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: scroll
    runtime: shiny
---

\```{r setup, include=FALSE}
library(flexdashboard)
library(tidytext)
library(tidyverse)
library(janitor)
library(DT)
library(gghighlight)
library(knitr)
library(shiny)
library(qualtRics)
library(curl)
library(sjlabelled)
library(naniar)
library(scales)
library(lme4)
library(MASS)
library(snakecase)

\```

\```{r global, include=FALSE}
#setting global options for table scrolling and plot theme
options(DT.options = list(scrollY="100vh"))
theme_set(theme_minimal())

#this fetches all of your survey info 
surveys <- all_surveys() 

#this saves the survey responses into 
docusign_survey <- fetch_survey(surveyID = surveys$id[1], 
                                verbose = TRUE,
                                label = TRUE,
                                breakout_sets = TRUE,
                                force_request = TRUE)

#this saves the question text into a dataframe 
questions <- survey_questions(surveyID = surveys$id[1])

rename_df <- rename_variables(docusign_survey)

#this renames all of the variables
docusign_survey <- docusign_survey %>% 
                   rename_at(as.vector(rename_df$old_name), ~ as.vector(rename_df$new_labels))

#new variables
new_var <- rename_df$new_labels

#which are multi_select?
multi_select <- rename_df %>% 
                filter(ms == 1) %>% 
                dplyr::select(new_labels)
                
#relabel those NAs as No 
docusign_survey <- docusign_survey %>% 
                   purrr::modify_at(multi_select$new_labels, na_to_y)
\```

Sidebar {.sidebar}
=====================================


here is the input

\```{r}

selectInput("p_var_1", label = "Interesting Var #1",
              choices = new_var,
              multiple = TRUE)

\```

Cool Plots!
=====================================

Column {.tabset}
-------------------------------------

### this is my plot
\```{r}
renderPlot( {

make_bivariate_plot(docusign_survey, input$cool_var1, input$cool_var2)

})
\```

```

Functions

make_dashboard()

I saved the parts that will repeat every time, probably will want to make them editable for changes in scrolling, etc. but just trying to make proof of concept at the moment.

```
make_dashboard <- function(title, sidebar, page1, fn){
  load("data/top_matter.rda")
  load("data/libraries.rda")
  load("data/main_chunk.rda")

  initial_bit <- stringr::str_c(top_matter, libraries, main_chunk, sep = "\n\n")

  intermediate <- stringr::str_c(initial_bit, sidebar, sep = "\n\n")

  total <- stringr::str_c(intermediate, page1, sep = "\n\n")

  data <- list(title = title)

  out_fn <- paste0("./", fn, ".Rmd")
  writeLines(whisker.render(total, data), con = out_fn)
}
```

make_sidebar()

```
make_sidebar <- function(sidebar_title, input_type, input_name){
top_sidebar <-
'Sidebar {.sidebar}
=====================================
'

sidebar_text <- str_c(top_sidebar, sidebar_title, sep = "\n\n")

if(input_type == "multi-select"){
  ms <- "TRUE"
} else {
  ms <- "FALSE"
}

input_one <- make_select_input(input_name, ms)

sidebar_total <- str_c(sidebar_text, "```{r}", input_one, "```", sep = "\n\n")

return(sidebar_total)

}
```

make_page()

```
make_page <- function(page_title, element_one){
top_page <-
'{{page_title}}
=====================================

Column {.tabset}
-------------------------------------'

add_element <- stringr::str_c(top_page, element_one, sep = "\n\n")

data <- list(page_title = page_title)

page <- whisker.render(add_element, data = data)

return(page)
}
```

make_plot()

```
make_plot <- function(plot_title, type = c("univariate", "bivariate"), vars){
top_plot_piece <-' {{plot_title}}
\```{r}
renderPlot( {
'

if(type == "univariate"){
  plot_piece <-
'make_univariate_plot(docusign_survey, input${{vars}})

})

\```'
  total_plot <- stringr::str_c(top_plot_piece, plot_piece, sep = "\n\n")

  data <- list(plot_title = plot_title,
               vars = vars)
  plot_chunk <- whisker.render(total_plot, data = data)

} else{
  plot_piece <-
'make_bivariate_plot(docusign_survey, input${{var_1}}, input${{var_2}})

})
\```'

  total_plot <- stringr::str_c(top_plot_piece, plot_piece, sep = "\n\n")

  data <- list(plot_title = plot_title,
               var_1 = vars[1],
               var_2 = vars[2])
  plot_chunk <- whisker.render(total_plot, data = data)
}

return(plot_chunk)
}
```
Emily
  • 77
  • 8