Search code examples
rplotlyr-plotlyggplotly

Updating multiple data attributes with plotly


I am trying to update multiple data attributes in a figure generated via plotly.

This is my script based on this post:

library(plotly)
library(magrittr)
library(data.table)

X <- data.table(x = c(1, 2, 1, 2), y = c(1, 1, 2, 2), z = rep(c("a", "b"), each = 2))

gg <- ggplot() + geom_point(data = X, aes(x = x, y = y, color = z))

ggplotly(gg) %>% 
  layout(
    updatemenus = list(
      list(
        buttons = list(
          list(method = "restyle",
               args = list(
                 list(x = list(X[z == "a", x])),
                 list(y = list(X[z == "a", y]))
               ),
               label = "a"),
          list(method = "restyle",
               args = list(
                 list(x = list(X[z == "b", x])),
                 list(y = list(X[z == "b", y]))
               ),
               label = "b")
        )
      )
    )
  )

enter image description here

However, as can be seen, the update menu does not work as intended. Not sure what the problem is.


Solution

  • There are too many lists in your above args parameter.

    Furthermore, when calling restyle without specifying the trace number you are restyling all traces.

    If you want to restyle a single trace you need to provide its trace index:

    library(plotly)
    library(magrittr)
    library(data.table)
    
    DT <- data.table(x = c(1, 2, 10, 20), y = c(1, 1, 20, 20), z = rep(c("a", "b"), each = 2))
    
    gg <- ggplot() + geom_point(data = DT, aes(x = x, y = y, color = z))
    
    ggplotly(gg) %>% 
      layout(
        updatemenus = list(
          list(
            buttons = list(
              list(method = "restyle",
                   args = list(list(x = DT[z == "a", x], y = DT[z == "a", y]), 0L),
                   label = "a"),
              list(method = "restyle",
                   args = list(list(x = DT[z == "b", x], y = DT[z == "b", y]), 1L),
                   label = "b")
            )
          )
        )
      )
    

    I'm not sure what you are trying to achive.

    If you want to show trace "a" when "a" is selected and trace "b" when "b" is selected this filter approach is problematic, as it completly removes traces (reduces the dataset) from the plot and once the trace is removed you can no longer restyle it.

    If you just want to toggle the trace visibility you should use the visible parameter instead of modifying the data:

    DT <- data.table(x = c(1, 2, 10, 20), y = c(1, 1, 20, 20), z = rep(c("a", "b"), each = 2))
    
    gg <- ggplot() + geom_point(data = DT, aes(x = x, y = y, color = z))
    
    ggplotly(gg) %>% style(visible = FALSE, traces = 2) %>% 
      layout(
        updatemenus = list(
          list(
            buttons = list(
              list(method = "restyle",
                   args = list(list(visible = c(TRUE, FALSE)), c(0, 1)),
                   label = "a"),
              list(method = "restyle",
                   args = list(list(visible = c(FALSE, TRUE)), c(0, 1)),
                   label = "b")
            )
          )
        )
      )
    

    result

    PS: Plotly.restyle expects the trace count starting from 0 (JS) and style() from 1 (R)

    Using addTraces/deleteTraces as done here would be another approach. However, if the data stays the same I guess toggling the visibility is less computationally intensive.