0
votes

I am challenged trying to implement a feature into my Shiny app. The problem is two-fold:

  1. Is it possible to have 2 inputs from the same variable? I have one variable that is a list of indicators. I want the user to be able to select 2 indicators with selectInput, and then draw a scatter plot. There has to be 2 selectInputs because other parts of the app will rely on only the first selectInput. My data is long. I don't think it will work if I make it wide because my data includes latitude and longitude information so it wouldn't make sense to create a selectInput with names(data), for example.

  2. If I can have 2 selectInputs from the same variable, how would I call the values in my plot, since the value is called 'value' for both the inputs?

EDIT: Following Gregor's suggestion to reference the inputs with aes_string, I would expect the following example of mtcars gathered into long format to work, but I instead get an aesthetics or object not found error. I think I probably need to filter the data, but I don't understand how I can do that since my variable indicators now refers to both 'indicators' and 'indicators2'. I.e., I can't have

filtered <- 
      cars %>%
      filter(indicators == input$indicators,
             indicators == input$indicators2) 

Maybe I need to create a reactive expression that creates a new data frame instead? This is my non-working reproducible code with long-form mtcars:

library(ggplot2)
library(shiny)

cars <- mtcars %>%
  gather(indicators, value, mpg:carb)


ui <- fluidPage(

  # Application title
  titlePanel("mtcars"),

  sidebarLayout(
    sidebarPanel(
      selectInput("indicators",
                  label = "select indicator:",
                  choices = c("mpg", "cyl",  "disp", "hp",  "drat", "wt",   "qsec", "vs",   "am",   "gear", "carb")
                    ),
      selectInput("indicators2",
                  label = "select indicator:",
                  choices = c("mpg", "cyl",  "disp", "hp",  "drat", "wt",   "qsec", "vs",   "am",   "gear", "carb")
                  )
    ),

    mainPanel(
      plotOutput("carsPlot")
    )
  )
)

server <- function(input, output) {

  output$carsPlot <- renderPlot({
    ggplot(cars, aes_string(x = input$indicators, y = input$indicators2)) +
      geom_point(shape = 1)
  })
}

# Run the application 
shinyApp(ui = ui, server = server)
3

3 Answers

1
votes

You can use aes_string to pass input$indicators and input$indicators2 to ggplot like this. There is no need to cast your data into wide format since ggplot can actually handle long data better.

library(ggplot2)
library(shiny)

ui <- fluidPage(
  # Application title
  titlePanel("mtcars"),
  sidebarLayout(
    sidebarPanel(
      selectInput("indicators",
                  label = "select indicator:",
                  choices = names(mtcars)),
      selectInput("indicators2",
                  label = "select indicator:",
                  choices = names(mtcars))
    ),
    mainPanel(
      plotOutput("carsPlot")
    )
  )
)

server <- function(input, output) {
  output$carsPlot <- renderPlot({
    ggplot(mtcars, aes_string(x = input$indicators, y = input$indicators2)) +
      geom_point(shape = 1)
  })
}

# Run the application 
shinyApp(ui = ui, server = server)
0
votes

Here another solution with a selectInput in multiple mode.

library(dplyr)
library(tidyr)
library(ggplot2)
library(shiny)

cars <- mtcars %>%
  gather(indicators, value, mpg:carb)


ui <- fluidPage(

  titlePanel("mtcars"),

  sidebarLayout(
    sidebarPanel(
      uiOutput("ui_indicators")
    ),

    mainPanel(
      plotOutput("carsPlot")
    )
  )
)

server <- function(input, output) {

  output$ui_indicators <- renderUI({
    choices <- unique(cars$indicators)
    selectInput("indicators",
                label = "select indicators :",
                choices = choices,
                multiple = TRUE)
  })


  output$carsPlot <- renderPlot({

    filtered <- cars %>% filter(indicators %in% input$indicators)

    ggplot(filtered, aes(x = indicators, y = value)) +
      geom_point(shape=1)

  })
}

# Run the application 
shinyApp(ui = ui, server = server)
0
votes

Building on the tip from Gregor on aes_string, I managed to fix this. I ended up using the wide data and adding a proper reactive statement that creates a new data frame out of the selected indicators.

My server function now looks like this:

server <- function(input, output) {

  selectedVars <- reactive({
    cars[, c(input$indicators, input$indicators2)]
  })

  output$carsPlot <- renderPlot({
    ggplot(selectedVars(), aes_string(x = input$indicators, y = input$indicators2)) +
      geom_point(shape = 1)
  })
}

All works beautifully, and I am beginning to learn more about the utility of reactive functions in Shiny :)


Gregor de Cillia provided the answer I was looking for. The two inputs (which are not a problem) can be referenced using aes_string.