shsh
shsh

Reputation: 727

Dynamic Plots Based on One or More Dropdown Values

I am trying to make multiple dynamic plots (one does not know how many plots will output) based on one or more selected dropdown value(s) (species in this case).

I did succeed making plots based on the dropdown. For instance, two plots are displayed if a user selected two values/species from the dropdown list, one plot is displayed if one value/species is selected.

Although the number of plots match the number of dropdown values, the plots show a duplicate if two or more dropdown values/species are selected (it only works if exactly one value is selected). Any advice would be of great help.

The below code uses the iris dataset in R.

image1 image2

library(shiny)
library(shinyWidgets)
library(ggplot2)
library(tidyverse)
library(shinydashboard)

species = c("setosa", "versicolor", "virginica")

ui <- dashboardPage(
  dashboardHeader(title = "ddPCR"),
  
  sidebar <- dashboardSidebar(
    sidebarMenu(
      menuItem("General Overview", tabName = "tab1", icon = icon("dashboard"))
    )
  ),
  
  body <- dashboardBody(
    tabItems(
      tabItem(
        tabName = "tab1",
        uiOutput("species_dropdown"),
        DT::dataTableOutput("table1"),
        uiOutput("plots")
      )
    )
  )
)

server <- function(input, output) {
  
  output$species_dropdown <- renderUI({
    pickerInput(
      "var1",
      "Species:",
      choices = species,
      options = pickerOptions(
        actionsBox = T,
        header = "Close",
        liveSearch = T
      ),
      multiple = T
    )
  })
  
  filtered_data <- reactive({
    iris %>% 
      filter(Species %in% input$var1)
  })
  
  output$table1 <- DT::renderDataTable({
    req(input$var1)
    filtered_data()
  })
  
  # Insert the right number of plot output objects into the web page
  output$plots <- renderUI({
    req(input$var1)
    
    plot_output_list <- lapply(1:length(input$var1), function(i) {
      plotname <- paste("plot", i, sep="")
      plotOutput(plotname, height = 280, width = 250)
    })
    
    do.call(tagList, plot_output_list)
  })
  
  for (i in 1:length(species)) {
    local({
      plotname <- paste("plot", i, sep="")

      output[[plotname]] <- renderPlot({
        ggplot(filtered_data(), aes(x = Sepal.Length, y = Sepal.Width)) + 
          geom_point() +
          labs(title = paste(input$var1, sep = ""), x = "Sepal Length", y = "Sepal Width") 
      })
    })
  }
}

shinyApp(ui, server)

Upvotes: 0

Views: 185

Answers (1)

Mr.Rlover
Mr.Rlover

Reputation: 2613

From the comments in this app on line 34:

Need local so that each item gets its own number. Without it, the value of i in the renderPlot() will be the same across all instances, because of when the expression is evaluated.

So you need to assign i to a local variable otherwise it will be the same for all renderPlots, hence why your plots are identical. Also, your title should be changed to index the inputs, otherwise only the first element is used to the title argument because paste will return a list when it is passed a list, while title requires just one value.

Full app:


library(shiny)
library(shinyWidgets)
library(ggplot2)
library(tidyverse)
library(shinydashboard)

species = c("setosa", "versicolor", "virginica")

ui <- dashboardPage(
  dashboardHeader(title = "ddPCR"),
  
  sidebar <- dashboardSidebar(
    sidebarMenu(
      menuItem("General Overview", tabName = "tab1", icon = icon("dashboard"))
    )
  ),
  
  body <- dashboardBody(
    tabItems(
      tabItem(
        tabName = "tab1",
        uiOutput("species_dropdown"),
        DT::dataTableOutput("table1"),
        uiOutput("plots")
      )
    )
  )
)

server <- function(input, output) {
  
  output$species_dropdown <- renderUI({
    pickerInput(
      "var1",
      "Species:",
      choices = species,
      options = pickerOptions(
        actionsBox = T,
        header = "Close",
        liveSearch = T
      ),
      multiple = T
    )
  })
  
  filtered_data <- reactive({
    iris %>% 
      filter(Species %in% input$var1)
  })
  
  output$table1 <- DT::renderDataTable({
    req(input$var1)
    filtered_data()
  })
  
  # Insert the right number of plot output objects into the web page
  output$plots <- renderUI({
    req(input$var1)
    
    plot_output_list <- lapply(1:length(input$var1), function(i) {
      plotname <- paste("plot", i, sep="")
      plotOutput(plotname, height = 280, width = 250)
    })
    
    do.call(tagList, plot_output_list)
  })
  
  for (i in 1:length(species)) {
    
    local({
      my_i <- i #crucial
      
      plotname <- paste("plot", my_i, sep="") # use my_i instead of i
      
      output[[plotname]] <- renderPlot({
        
        ggplot(filtered_data(), aes(x = Sepal.Length, y = Sepal.Width)) + 
          geom_point() +
          labs(title = paste(input$var1[my_i], sep = ""), x = "Sepal Length", y = "Sepal Width") # title needs input$var1 indexed as paste will return a list otherwise, in which case only a first element gets used for the title hence all titles are identical
        

      })
    })
  }
  
}

shinyApp(ui, server)

enter image description here

Upvotes: 1

Related Questions