0
votes

I am trying to create a shiny app with a plotly output.
The plot should have multiple y axes, and update based on the variables selected.
The question is how to combine the shiny reactivity and plotly while using add_lines, as at the moment if I select less variables than add_lines the code does not function

Sample code:

library(shiny)
library(dplyr)
library(plotly)
library(tidyr)

data <- cbind(
  seq(from = 1, to = 30, by = 1),
  sample(seq(from = 100, to = 300, by = 10), size = 30, replace = TRUE),
  sample(seq(from = 1, to = 100, by = 9), size = 30, replace = TRUE),
  sample(seq(from = 50, to = 60, by = 2), size = 30, replace = TRUE),
  sample(seq(from = 100, to = 130, by = 1), size = 30, replace = TRUE)
) %>% 
  as.data.frame()

names(data) <- c("date", "a", "b", "x", "y")

data <- data %>% gather("key", "value", 2:5)

ui <- fluidPage(
  column(
    width = 3,
    selectInput("select", "Select var:", choices = c("a", "b", "x", "y"), selected = c("a", "b", "x"), multiple = TRUE)
  ),
  column(
    width = 9
  ),
  column(
    width = 12,
    plotlyOutput("plot")
  )
)

server <-  function(input, output){

  output$plot <- renderPlotly({
    data <- data %>% filter(key %in% c("date", input$select)) %>% spread(key, value)

    plot_ly(x = ~data$date) %>%
      add_lines(y = ~data[, 2], name = input$select[1], line = list(color = "red")) %>%
      add_lines(y = ~data[, 3], name = input$select[2], line = list(color = "blue"), yaxis = "y2") %>%
      add_lines(y = ~data[, 4], name = input$select[3], line = list(color = "green"), yaxis = "y3") %>%
      layout(
        yaxis = list(
          side = "left"
        ),
        yaxis2 = list(
          side = "left",
          overlaying = "y",
          anchor = "free",
          position = 0.02
        ),
        yaxis3 = list(
          side = "left",
          overlaying = "y",
          anchor = "free",
          position = 0.04
        )
      )
  })
}

shinyApp(ui, server)
2

2 Answers

1
votes

At least of ~6 months ago, my understanding (from plotly support) was that plotly did not concisely handle this sort of conditional plotting. Instead, if you can enumerate all plotting scenarios, you can use something like the following (which does not actually work yet, need to fix other parts of your code), with an else if for each plotting scenario:

output$plot <- renderPlotly({
data <- data %>% filter(key %in% c("date", input$select)) %>% spread(key, value)

if (input$select == c("a", "b", "x")) {
plot_ly(x = ~data$date) %>%
  add_lines(y = ~data[, 2], name = input$select[1], line = list(color = "red")) %>%
  add_lines(y = ~data[, 3], name = input$select[2], line = list(color = "blue"), yaxis = "y2") %>%
  add_lines(y = ~data[, 4], name = input$select[3], line = list(color = "green"), yaxis = "y3") %>%
  layout(
    yaxis = list(
      side = "left"
    ),
    yaxis2 = list(
      side = "left",
      overlaying = "y",
      anchor = "free",
      position = 0.02
    ),
    yaxis3 = list(
      side = "left",
      overlaying = "y",
      anchor = "free",
      position = 0.04
    )
  )
} else if (input$select == c("a", "b")) {
plot_ly(x = ~data$date) %>%
  add_lines(y = ~data[, 2], name = input$select[1], line = list(color = "red")) %>%
  add_lines(y = ~data[, 3], name = input$select[2], line = list(color = "blue"), yaxis = "y2") %>%
  layout(
    yaxis = list(
      side = "left"
    ),
    yaxis2 = list(
      side = "left",
      overlaying = "y",
      anchor = "free",
      position = 0.02
    ),
    yaxis3 = list(
      side = "left",
      overlaying = "y",
      anchor = "free",
      position = 0.04
    )
  )
    }

})

It is not concise, but may work if there are not a huge number of scenarios.

It also appears you're overwriting your data with the filter call; if your data set is not huge you might skip that step and organize your data outside of the plot call. Otherwise you probably need a reactive function outside of the plot call, that gets called from within the plotting functioning, leaving the original data unmodified.

1
votes

Here is the solution for you:

library(shiny)
library(dplyr)
library(plotly)
library(tidyr)

data <- cbind(
  seq(from = 1, to = 30, by = 1),
  sample(seq(from = 100, to = 300, by = 10), size = 30, replace = TRUE),
  sample(seq(from = 1, to = 100, by = 9), size = 30, replace = TRUE),
  sample(seq(from = 50, to = 60, by = 2), size = 30, replace = TRUE),
  sample(seq(from = 100, to = 130, by = 1), size = 30, replace = TRUE)
) %>% 
  as.data.frame()

names(data) <- c("date", "a", "b", "x", "y")

data <- data %>% gather("key", "value", 2:5)

ui <- fluidPage(
  column(
    width = 3,
    selectInput("select", "Select var:", choices = c("a", "b", "x", "y"), selected = c("a", "b", "x"), multiple = TRUE)
  ),
  column(
    width = 9
  ),
  column(
    width = 12,
    plotlyOutput("plot")
  )
)

server <-  function(input, output){

  output$plot <- renderPlotly({
    data <- data %>% filter(key %in% c("date", input$select)) 

    plot_ly(data, x = ~date, y=~value, color=~key) %>%
      layout(
        yaxis = list(
          side = "left"
        ),
        yaxis2 = list(
          side = "left",
          overlaying = "y",
          anchor = "free",
          position = 0.02
        ),
        yaxis3 = list(
          side = "left",
          overlaying = "y",
          anchor = "free",
          position = 0.04
        )
      )
  })
}

shinyApp(ui, server)

It is very easy, you should not spread your dataset, instead with long format you can setup an argument color= in plotly, which will directly group your data according to set variable:

plot_ly(data, x = ~date, y=~value, color=~key)