Separate symbol and color in plotly legend - r

I want to achieve the same result as this ggplot code with plotly:
mtcars %>% add_rownames('car') %>%
ggplot(aes(x = mpg,
y = disp,
color = as.factor(gear),
shape = as.factor(cyl))) +
geom_point()
which results in:
My plotly code is:
library(dplyr)
mtcars %>% add_rownames('car') %>%
plot_ly(x = ~mpg,
y = ~disp,
text = ~car,
color = ~as.factor(gear),
symbol = ~as.factor(cyl),
mode = 'markers')
which enumerates all possible combinations of colors and shapes in the legend.
Is there a way to have a similar legend to the ggplot?

UPDATE: To overcome some of the issues mentioned for my previous solution (see below) and to increase the usability of the legend, one can simply add the column name to the legend description and then assign the legendgroups to each category.
mtcars %>% rownames_to_column('car') %>%
plot_ly() %>%
#Plot symbols for cyl
add_trace(type = "scatter",
x = ~mpg,
y = ~disp,
text = ~car,
symbol = ~paste0(cyl," cyl."),
mode = 'markers',
marker = list(color = "grey", size = 15)) %>%
#Overlay color for gears
add_trace(type = "scatter",
x = ~mpg,
y = ~disp,
text = ~car,
color = ~paste0(gear, " gears"),
mode = 'markers')
This is the previous solution, which is visually closer to the ggplot2 equivalent:
Based on the answer of dww in this thread, we can manually create the groups for cylinders and gears. Subsequently, with the answer of Artem Sokolov this thread, we can add the legend titles as annotations.
mtcars %>% rownames_to_column('car') %>%
plot_ly() %>%
#Plot symbols for cyl
add_trace(type = "scatter",
x = ~mpg,
y = ~disp,
text = ~car,
symbol = ~as.factor(cyl),
mode = 'markers',
legendgroup="cyl",
marker = list(color = "grey", size = 15)) %>%
#Overlay color for gears
add_trace(type = "scatter",
x = ~mpg,
y = ~disp,
text = ~car,
color = ~as.factor(gear),
mode = 'markers',
legendgroup="gear") %>%
#Add Legend Titles (manual)
add_annotations( text="Cylinders:", xref="paper", yref="paper",
x=1.02, xanchor="left",
y=0.9, yanchor="bottom", # Same y as legend below
legendtitle=TRUE, showarrow=FALSE ) %>%
add_annotations( text="Gears:", xref="paper", yref="paper",
x=1.02, xanchor="left",
y=0.7, yanchor="bottom", # Y depends on the height of the plot
legendtitle=TRUE, showarrow=FALSE ) %>%
#Increase distance between groups in Legend
layout(legend=list(tracegroupgap =30, y=0.9, yanchor="top"))
Unsolved issues:
Groups have to be created manually
Groups are just overlayed (color over shape). This means that only the whole group can be dis-/activated in the legend (e.g., it is not possible to only show only the entries with 4 cylinders)
The position of the second legend title (annotation) depends on the height of the plot!

Related

How to adjust legend title position in ggplotly in r? [duplicate]

In the following example how can i add a title to the legend in plot_ly for R ?
mtcars %>% plot_ly(x = ~disp, y = ~mpg, color = ~factor(cyl), size = ~wt) %>% add_markers(
hoverinfo = "text",
text = ~paste("Displacement = ", disp, "\nMiles Per Gallon = ", mpg) ) %>% layout(title ="Custom Hover Text")
thanks
This functionality has since been included within the layout function in the legend option. There's a sub-option called title within which you can supply a list that includes the text.
mtcars %>%
plot_ly(x = ~disp, y = ~mpg, color = ~factor(cyl), size = ~wt) %>%
add_markers(hoverinfo = "text",
text = ~paste("Displacement = ", disp, "\nMiles Per Gallon = ", mpg) ) %>%
layout(title = "Custom Hover Text",
legend = list(title = list(text = "<b>Cylinders</b>"))) # TITLE HERE
The only way I know is to use an annotation and add it to the plot. Like this:
legendtitle <- list(yref='paper',xref="paper",y=1.05,x=1.1, text="Cylinders",showarrow=F)
mtcars %>% plot_ly(x = ~disp, y = ~mpg, color = ~factor(cyl), size = ~wt) %>%
add_markers( hoverinfo = "text",
text = ~paste("Displacement=",disp, "\nMiles Per Gallon = ", mpg)) %>%
layout(title ="Custom Hover Text", annotations=legendtitle )
Yielding:
It is a bit tricky to place the legend title though, not sure if this placement would always work.
Another way would be to use ggplot and ggplotly of course, and let ggplot figure it out.

R plotly histogram hover text

This is my code. Just a simple historgram. But what I wanted to do is to customize the hover text so that when I hover, it will display all species included in that histogram bar. Can you help me?
iris %>%
plot_ly(x=~Sepal.Length, color=~Sepal.Width, text=~Species) %>%
add_histogram()
Here's the output. But when I hover it seems the text is only displaying the first species in the table.
plotly_hist
I'm not sure whether this is possible. Probably you are demanding too much from plotly. After trying some options I think there are two ways to go if you want the different Species to show up in the tooltip:
First option is to use a stacked histogram using hovermode = "unified" like so:
library(plotly)
fig <- plot_ly()
fig <- fig %>% add_trace(data = filter(iris, Species == "setosa"),
x = ~Sepal.Length,
color = ~Species,
text = ~Species,
type='histogram',
bingroup=1, showlegend = FALSE)
fig <- fig %>% add_trace(data = filter(iris, Species == "versicolor"),
x = ~Sepal.Length,
color = ~Species,
text = ~Species,
type='histogram',
bingroup=1, showlegend = FALSE)
fig <- fig %>% add_trace(data = filter(iris, Species == "virginica"),
x = ~Sepal.Length,
color = ~Species,
text = ~Species,
type='histogram',
bingroup=1, showlegend = FALSE)
fig <- fig %>% layout(
hovermode="unified",
barmode="stack",
bargap=0.1)
fig
The second option would be to make the computations yourself, i.e. binning and summarising and to make a bar chart of the counts.
iris %>%
mutate(Sepal.Length.Cut = cut(Sepal.Length, breaks = seq(4, 8, .5), right = FALSE)) %>%
group_by(Sepal.Length.Cut, Species) %>%
summarise(n = n(), Sepal.Width = sum(Sepal.Width)) %>%
tidyr::unite("text", Species, n, sep = ": ", remove = FALSE) %>%
summarise(n = sum(n), Sepal.Width = sum(Sepal.Width) / n, text = paste(unique(text), collapse = "\n")) %>%
plot_ly(x = ~Sepal.Length.Cut, y = ~n, text = ~text) %>%
add_bars(marker = list(colorscale = "Rainbow"), hovertemplate = "%{y}<br>%{text}")
Edit A third option would be to use ggplotly(). This way it is an easy task to add annotations displayling the total numbers per bin. This way we can make use of the stats layers in ggplot2 which will do all the computations. To the best of my knowledge that couldn't be done that easily using "pure" plotly.
library(plotly)
ggplot(iris, aes(Sepal.Length, fill = Species)) +
stat_bin(breaks = seq(4, 8, .5), closed = "left") +
stat_bin(breaks = seq(4, 8, .5), closed = "left", geom = "text", mapping = aes(Sepal.Length, label = ..count..), inherit.aes = FALSE, vjust = -.5) +
theme_light()
ggplotly()

How to remove duplicate legend entries w/ plotly subplots()

How can I remove the duplicates in my legend when using plotly's subplots()?
Here is my MWE:
library(plotly)
library(ggplot2)
library(tidyr)
mpg %>%
group_by(class) %>%
do(p = plot_ly(., x = ~cyl, y = ~displ, color = ~trans, type = 'bar')) %>%
subplot(nrows = 2, shareX = TRUE, titleX = TRUE) %>%
layout(barmode = 'stack')
plotly does not have facet like ggplot2 so it will add legend for each subplot or you can turn it off for some of them.
Here we do not have a layer with all the ~class entries nor two plots with no intersection in class which their combination also covers all of them. In that case, we could set showlegend to TRUE for those specific plot(s) and set it to FALSE for the rest and also set the legendgroup to trans so we get a unique but also complete legend.
As I said, here we do not have that special case. So What I can think of are two possibilities:
Adding the whole data (duplicating whole dataframe) and assigning class of All to them. Then plotting that along with original data but keep the legend only for class == All.
Using ggplot::facet_wrap and then ggplotly to make a plotly object. However, this would cause some issues with x-axis (compare ggplot object to plotly ones).
library(plotly)
library(ggplot2)
library(dplyr)
ly_plot <- . %>%
plot_ly(x = ~cyl, y = ~displ, color = ~trans,
type = 'bar', showlegend = ~all(legendC)) %>%
add_annotations(
text = ~unique(class),
x = 0.5,
y = 1,
yref = "paper",
xref = "paper",
xanchor = "middle",
yanchor = "top",
showarrow = FALSE,
font = list(size = 15))
mpg %>%
mutate(class= "_All_") %>%
rbind(.,mpg) %>%
mutate(legendC = (class == "_All_")) %>%
group_by(class) %>%
do(p = ly_plot(.)) %>%
subplot(nrows = 2, shareX = TRUE, titleX = TRUE) %>%
layout(barmode = 'stack')
#> Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large,
#> allowed maximum for palette Set2 is 8
#> Returning the palette you asked for with that many colors
p <- ggplot(data = mpg, aes(x=cyl, y=displ, fill=trans))+
geom_bar(stat="identity") +
facet_wrap(~class)
p
ggplotly(p) #seems for this we should also set "colour = trans"
Another workaround using the tidyverse. The following steps are added to the original MWE:
Convert the trans column to a factor.
Use tidyr's complete to fill (non-NA) dummy values for the missing factor levels in each class group.
Follow M-M's suggestion setting showlegend to TRUE for a single group and legendgroup to trans to link the legend entries between subplots.
library(plotly)
library(tidyverse)
mpg %>%
mutate_at("trans", as.factor) %>%
group_by(class) %>%
group_map(.f = ~{
## fill missing levels w/ displ = 0, cyl = first available value
complete(.x, trans, fill = list(displ = 0, cyl = head(.x$cyl, 1))) %>%
plot_ly(x = ~cyl, y = ~displ, color = ~trans, colors = "Paired", type = "bar",
showlegend = (.y == "2seater"), legendgroup = ~trans) %>%
layout(yaxis = list(title = as.character(.y)), barmode = "stack")
}) %>%
subplot(nrows = 2, shareX = TRUE, titleY = TRUE)

setting specific color palettes for individual traces in plotly chart

i'm trying to define a specific color palette for each individual trace of a chart in plotly in r. my code is as follows:
library(ggplot2)
library(dplyr)
library(plotly)
colors_1 <- c("#2e71c9","#ffb728")
colors_2 <- c("#4f4f4f","#000000")
data_subset_1 <-
diamonds %>%
filter(clarity %in% c("VVS2", "VS1")) %>%
mutate(cut = as.character(cut)) %>%
count(cut, clarity)
data_subset_2 <-
diamonds %>%
filter(clarity %in% c("SI1", "IF")) %>%
mutate(cut = as.character(cut)) %>%
count(cut, clarity)
plot_ly() %>%
add_bars(data = data_subset_1, x = ~cut, y = ~n, color = ~clarity, colors = colors_1) %>%
add_bars(data = data_subset_2, x = ~cut, y = ~n, color = ~clarity, colors = colors_2)
the bars of the first trace should be in blue and orange, the 2nd trace in grey and black, but clearly they all are all in color. if i remove the colors = colors_2 from the second trace, it does not even change anything at all. it seems the colors defined in the first trace are being used to calculate a color palette that is used for all traces of the plot, but i'd like to specifically assign color palettes.
Try the following:
cols <- setNames(c("#2e71c9","#ffb728", "#CCDDDD","#000000"),
c("VVS2", "VS1", "SI1", "IF"))
plot_ly() %>%
add_bars(data = data_subset_1, x = ~cut, y = ~n, color = ~clarity, colors = cols) %>%
add_bars(data = data_subset_2, x = ~cut, y = ~n, color = ~clarity)

Add title to the plotly legend

In the following example how can i add a title to the legend in plot_ly for R ?
mtcars %>% plot_ly(x = ~disp, y = ~mpg, color = ~factor(cyl), size = ~wt) %>% add_markers(
hoverinfo = "text",
text = ~paste("Displacement = ", disp, "\nMiles Per Gallon = ", mpg) ) %>% layout(title ="Custom Hover Text")
thanks
This functionality has since been included within the layout function in the legend option. There's a sub-option called title within which you can supply a list that includes the text.
mtcars %>%
plot_ly(x = ~disp, y = ~mpg, color = ~factor(cyl), size = ~wt) %>%
add_markers(hoverinfo = "text",
text = ~paste("Displacement = ", disp, "\nMiles Per Gallon = ", mpg) ) %>%
layout(title = "Custom Hover Text",
legend = list(title = list(text = "<b>Cylinders</b>"))) # TITLE HERE
The only way I know is to use an annotation and add it to the plot. Like this:
legendtitle <- list(yref='paper',xref="paper",y=1.05,x=1.1, text="Cylinders",showarrow=F)
mtcars %>% plot_ly(x = ~disp, y = ~mpg, color = ~factor(cyl), size = ~wt) %>%
add_markers( hoverinfo = "text",
text = ~paste("Displacement=",disp, "\nMiles Per Gallon = ", mpg)) %>%
layout(title ="Custom Hover Text", annotations=legendtitle )
Yielding:
It is a bit tricky to place the legend title though, not sure if this placement would always work.
Another way would be to use ggplot and ggplotly of course, and let ggplot figure it out.

Resources