Search code examples
rggplot2plotlyggplotly

How to add dropdown filter list to ggplotly without using Shiny?


The code is as below:

{r, knitr::opts_chunk$set(echo = TRUE)}
library(plotly)
library(tidyverse)
library(magrittr)


#sample data
result_table = structure(list(wave = c(5L, 5L, 5L, 6L, 6L, 6L), term = c(1L, 
       2L, 3L, 1L, 2L, 3L), estimate = c(3.317, 0.887, 1, 0.828, 0.995, 
       1), Std.Error = c(1.249, 1.044, 1, 1.04, 1.003, 1), Statistic = c(10.305, 
       -1.571, 1, -3.297, -0.96, 1), P.Value = c(1, 1.01, 1, 1, 1.05, 
       1), conf.low = c(2.829, 0.802, 1, 0.749, 0.989, 1), conf.high = c(3.806, 
       0.973, 1, 0.906, 1, 1)), class = "data.frame", row.names = c(NA, 
       -6L))
result_table$wave %<>% as.factor
result_table$term %<>% as.factor
gg <- ggplot(result_table, aes(x = wave,  y = estimate,group = term)) +
      geom_point(aes(color = wave), size = 2) +
      geom_errorbar(aes(ymin=conf.low, ymax=conf.high,color = wave), 
                        linewidth=.1) + 
      geom_hline(yintercept = 0) + 
      labs(color = "Wave") +
      ylab('Estimate') +
      xlab('Term') +
      theme_classic() 

# Create a plotly object from ggplot
p <- ggplotly(gg)

p <- p %>% layout(showlegend = F,
  updatemenus = list(
    list(
      x = 1.5,
      y = 0.8,
#      yanchor = "bottom",
#      xanchor = 'center',
      buttons = list(
        list(method = "restyle",
             args = list("visible", list(TRUE, FALSE, FALSE)),
             label = "1"),
        
        list(method = "restyle",
             args = list("visible", list(FALSE, TRUE, FALSE)),
             label = "2"),
        
        list(method = "restyle",
             args = list("visible", list(FALSE, FALSE, TRUE)),
             label = "3")
      )
    )
  )
)

p

The result is : enter image description here

In the graph, I would like to make the variable term as a dropdown list, so that I can select the item in term and update the plot instantly. However, plotly plotted all the item in term in the same time, what can I do to fix this problem?

I used the code from Add dropdown filter list to ggplot in RMarkdown HTML file WITHOUT using shiny?, why his code could work well but the code of mine could not work well?

Is it because of geom_errorbar?


Solution

  • As already pointed out by @M-- in the comments, plotly thinks in traces. And to make your filter work requires to have separate for each term. I tried to get this right using the group aes but failed. Instead one option to get separate traces would be to add the geom layers separately for each value of term by filtering the data. To avoid duplicating code I use lapply. This way the layers or traces are added as groups of 4 traces per term, i.e. the first four traces are for term=1 and so on. Hence, you also have to account for that when you set the visibility of the traces in updatemenus:

    library(plotly)
    
    gg <- ggplot(result_table, aes(
      x = wave,
      y = estimate,
      color = wave,
      group = term
    )) +
      lapply(levels(result_table$term), \(x) {
        list(
          geom_point(data = ~ subset(.x, term == x), size = 2),
          geom_errorbar(
            data = ~ subset(.x, term == x),
            aes(ymin = conf.low, ymax = conf.high),
            linewidth = .1
          )
        )
      }) +
      geom_hline(yintercept = 0) +
      labs(color = "Wave") +
      ylab("Estimate") +
      xlab("Term") +
      theme_classic()
    
    # Create a plotly object from ggplot
    p <- ggplotly(gg)
    
    library(plotly)
    
    p <- p %>% layout(
      showlegend = F,
      updatemenus = list(
        list(
          x = 1.5,
          y = 0.8,
          buttons = list(
            list(
              method = "restyle",
              args = list(
                "visible",
                c(rep(TRUE, 4), rep(FALSE, 4), rep(FALSE, 4), TRUE)
              ),
              label = "1"
            ),
            list(
              method = "restyle",
              args = list(
                "visible",
                c(rep(FALSE, 4, 4), rep(TRUE, 4), rep(FALSE, 4), TRUE)
              ),
              label = "2"
            ),
            list(
              method = "restyle",
              args = list(
                "visible",
                c(rep(FALSE, 4, 4), rep(FALSE, 4), rep(TRUE, 4), TRUE)
              ),
              label = "3"
            )
          )
        )
      )
    )
    
    p
    

    enter image description here