adilapapaya
adilapapaya

Reputation: 4795

Plotly legendgroup for subplots so a single legend controls all charts

I'm using plotly in r to generate a number of subplots. A toy example is shown below.

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

## Toy Example
ui <- fluidPage(
  h3("Diamonds"),
  plotlyOutput("plot", height = 600)
)

server <- function(input, output, session) {

  # reduce down the dataset to make the example simpler
  dat <- diamonds %>% 
    filter(clarity %in% c("I1", "IF")) %>%
    mutate(clarity = factor(clarity, levels = c("I1", "IF")))

  output$plot <- renderPlotly({

    # Generates the chart for a single clarity
    byClarity <- function(df){

      Clarity <- df$clarity[1];

      plot_ly(df, x = ~carat, y = ~price, color = ~cut, name = ~clarity) %>%
        add_trace(
          type="bar"
          ## Also tried adding this with no success
          # legendgroup = ~cut
        ) %>%
        layout(
          barmode = "stack"
        )
    }

    dat %>% 
      split(.$clarity) %>% 
      lapply(byClarity) %>%
      subplot(nrows = NROW(.), shareX = TRUE, which_layout = "merge")
  })
} 

shinyApp(ui, server)

I would like to make the legends such that clicking on a 'Cut' on the legend will show/hide that 'Cut' from both charts instead of just the chart associated with that legend.

DefaultVsIdeal

I looked at legendgroup but can't figure out how to associate it with cut instead of clarity (clarity is the grouping I'm using to make the subplots).

I also need the solution to work with raw plot_ly and not ggplotly as there are other plot_ly functionalities I need that aren't available in ggplotly.

Any help would be appreciated. I am using plotly_4.5.2, dplyr_0.5.0, and shiny_0.14.

Upvotes: 11

Views: 7551

Answers (3)

TrainedMusician
TrainedMusician

Reputation: 97

I have a working example with pure plot_ly code. However, it is not my proudest work:

library(ggplot2)
library(dplyr)
library(plotly)
dat <- diamonds %>% 
    filter(clarity %in% c("I1", "IF")) %>%
    mutate(clarity = factor(clarity, levels = c("I1", "IF")))

firstPlot <- plot_ly(data = dat, x = ~carat, y = ~depth, legendgroup = ~cut, color = ~cut)
secondPlot <- plot_ly(data = dat, x = ~carat, y = ~price, legendgroup = ~cut, color = ~cut)

together <- subplot(firstPlot, secondPlot)
together

subplot works! But not ideal Which yields this, but the legend still has duplicates.. This can be resolved with the showLegend = FALSE parameter, but not in the traditional manner..

for (layer in 1L:(length(together$x$data) / 2L)) {
    together$x$data[[layer]]$showlegend <- FALSE
}
together

Correctly grouped legend that is interactive! And this gives the final plots that are still linked together, while only displaying one of the values. If you plan on using more than two subplots, the for loop (which can be an lapply as well) can be altered to select all layers except the first by doing length(unique(dat$cut)):length(together$x$data) which is more robust in the long run.

Note: this works on most plot_ly graphs but not in all (e.g. 'box'), so if you want to apply this but still need help, let me know!

Upvotes: 2

shams
shams

Reputation: 21

Try adding legendgroup = ~cut to both traces and setting showlegend = F for one of them. Then in layout set showlegend = T

Like this:

plot_ly(df, x = ~carat, y = ~price, color = ~cut, name = ~clarity, legendgroup = ~cut, showlegend = T) %>%
    add_trace( type="bar", legendgroup = ~cut, showlegend = F) %>%
    layout(
      barmode = "stack",showlegend = T
    )

Upvotes: 2

Roman
Roman

Reputation: 17648

Ok, here is a solution using ggplot2:

library(ggplot2)
library(dplyr)
library(plotly)
dat <- diamonds %>% 
  filter(clarity %in% c("I1", "IF")) %>%
  mutate(clarity = factor(clarity, levels = c("I1", "IF")))
# Function for nice labels
k_label <- function(x) {
 c(0, paste0((x)/1000,"K")[-1])
}
# ggplot
p <- ggplot(dat,aes(x=carat, y=price, fill=cut)) + 
           geom_bar(stat="identity") + 
           facet_wrap(~clarity,nrow=2, scales = "free_y") +
           scale_y_continuous(labels = k_label) + 
           theme_minimal() + ylab("") + xlab("") +
           theme(legend.title=element_blank(),
                 panel.grid.major.x=element_blank())
# a plotly
ggplotly(p)

enter image description here

Upvotes: 1

Related Questions