Reputation: 4795
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.
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
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
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
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
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
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)
Upvotes: 1