Chapter 14 Functions
Exercise 14.4.1
The following app plots user selected variables from the msleep dataset for three different types of mammals (carnivores, omnivores, and herbivores), with one tab for each type of mammal. Remove the redundancy in the selectInput()
definitions with the use of functions.
library(tidyverse)
<- fluidPage(
ui selectInput(inputId = "x",
label = "X-axis:",
choices = c("sleep_total", "sleep_rem", "sleep_cycle",
"awake", "brainwt", "bodywt"),
selected = "sleep_rem"),
selectInput(inputId = "y",
label = "Y-axis:",
choices = c("sleep_total", "sleep_rem", "sleep_cycle",
"awake", "brainwt", "bodywt"),
selected = "sleep_total"),
tabsetPanel(id = "vore",
tabPanel("Carnivore",
plotOutput("plot_carni")),
tabPanel("Omnivore",
plotOutput("plot_omni")),
tabPanel("Herbivore",
plotOutput("plot_herbi")))
)
<- function(input, output, session) {
server
# make subsets
<- reactive( filter(msleep, vore == "carni") )
carni <- reactive( filter(msleep, vore == "omni") )
omni <- reactive( filter(msleep, vore == "herbi") )
herbi
# make plots
$plot_carni <- renderPlot({
outputggplot(data = carni(), aes_string(x = input$x, y = input$y)) +
geom_point()
res = 96)
}, $plot_omni <- renderPlot({
outputggplot(data = omni(), aes_string(x = input$x, y = input$y)) +
geom_point()
res = 96)
}, $plot_herbi <- renderPlot({
outputggplot(data = herbi(), aes_string(x = input$x, y = input$y)) +
geom_point()
res = 96)
},
}
shinyApp(ui = ui, server = server)
Solution.
Solution
We can see a pattern here where we are creating the same type of plot for each tabset panel, with the only variable changing being the vore
argument. We can reduce everything we see in triplicate to functions! We can use map
to create a single create_panels
function which will create a tab for each of our species
. On the server side, the data is filtered three times, and the plots are created three times. We can create a single rendering function that given the correct string it will filter the data, create the correct plot, and assign it to the correct output.
library(tidyverse)
# use a vector for function inputs
<- c("Carnivore", "Omnivore", "Herbivore")
species
# educe to a single UI function
# Marly: this didn't work!
<- function(id) {
create_panels tabPanel(id, plotOutput(paste0("plot_", id)))
}
<- fluidPage(
ui selectInput(inputId = "x",
label = "X-axis:",
choices = c("sleep_total", "sleep_rem", "sleep_cycle",
"awake", "brainwt", "bodywt"),
selected = "sleep_rem"),
selectInput(inputId = "y",
label = "Y-axis:",
choices = c("sleep_total", "sleep_rem", "sleep_cycle",
"awake", "brainwt", "bodywt"),
selected = "sleep_total"),
tabsetPanel(
tabPanel("Carnivore", plotOutput("plot_Carnivore")),
tabPanel("Omnivore", plotOutput("plot_Omnivore")),
tabPanel("Herbivore", plotOutput("plot_Herbivore"))
)# this works without the tabsetPanel function - why?!
# purrr::map(species, create_panels)
)
<- function(input, output, session) {
server
# rendering plot function for each panel
<- function(id) {
render_outputs paste0("plot_", id)]] <- renderPlot({
output[[%>%
msleep filter(vore == tolower(stringr::str_remove(id, "vore")) %>%
ggplot() +
aes_string(x = input$x, y = input$y) +
geom_point()
)
})
}
# apply to the species vector using map
::map(species, render_outputs)
purrr
}
shinyApp(ui = ui, server = server)
Exercise 14.4.2
Continue working with the same app from the previous exercise, and further remove redundancy in the code by modularizing how subsets and plots are created.