Plotly R: Interaction between subplot legend and highlight select - r

I am trying to get another plot to react to both legend (select/deselect levels of a factor) and drag select. In the following toy example drag select works as intended: the violin and boxplot are re-rendered to depict the points that are selected using the mouse. The legend works for the scatterplot, but not for the boxplot and violin that don't seem to share the legend. I read many posts about shared legend, but nothing worked for subplots of different types.
Even more surprisingly, when deplying to plotly cloud, the plot also loses it's drag select feature (altough I don't need the plotly cloud, I thought I should mention this).
Just to be clear, I'd like the boxplot and violin to be re-rendered based both on selecting on legend and drag-select on scatterplot. As these features theoretically work, the legend should provide the full data or subsets based on levels of the factor used for legend from which the drag selection could be carried out afterwards.
library(plotly)
d <- mtcars
d$cyl <- as.factor(d$cyl)
d <- highlight_key(mtcars)
sp <- plot_ly(d, x = ~mpg, y = ~disp,
color = ~factor(cyl), colors = c("red", "green", "blue"),
legendgroup = ~factor(cyl), showlegend = T) %>%
add_markers()
box <-
plot_ly(d, y = ~disp,
color = I("black"),
legendgroup = ~factor(cyl), showlegend = F) %>%
add_boxplot(name = " ")
violin <-
plot_ly(d, y = ~disp,
color = I("black"),
legendgroup = ~factor(cyl), showlegend = F) %>%
add_trace(type = "violin", name = " ")
p <-
subplot(sp, box, violin, shareY = TRUE, titleX = TRUE, titleY = TRUE) %>%
layout(
dragmode = "select",
barmode = "overlay",
title = "Click and drag scatterplot",
showlegend = TRUE
) %>%
highlight(on = "plotly_selected", off = "plotly_deselect")
p
Any help is greatly appreciated.

Related

R Plotly how to remove the grouping on the added linear regression line

I'm trying to draw a plot where, for the scatter plot, the color represents the region. However I noticed when I added a linear regression line, it still follows the color coding (and grouping on) on regions therefore it makes the line so odd and the legend is too messy.
I'm wondering whether there is a way to add a fitted line but ignore the region, or is there a way to still keep the legend for circles but remove the ones for lines? Thanks
My code for plotly looks like the following:
fig1 <- plot_ly(data = df1,
x = ~GDP_per_Capita,
y = ~Top_1000_Uni_per_Pop,
type = "scatter",
mode = "markers",
color = ~Region,
size = ~Top_1000_Uni,
hoverinfo = "text",
text = paste("Country: ", df1$TableName,
"<br>",
"Region: ", df1$Region)
) %>%
add_lines(data=df1, x = ~GDP_per_Capita, y = fitted(lr1),
line = list(width = 0.5, dash = "dot", color="red")) %>%
layout(showlegend = TRUE,
legend = list(orientation = 'l'),
title="Top Universities vs GDP per Capita",
xaxis=list(title="GDP per Capita", showgrid = T),
yaxis=list(title="Top 1000 University per million Population", showgrid = T))
#AmadeusNing you are basically there. Plotly lets you "configure" every trace and the legend that comes with it (including having it combined with other traces).
You can easily fix this with the showlegend parameter in the trace call. To remove the legend: showlegend = FALSE.
I do not have your data, so I revert back to the classical mpg dataset and define a simple linear regression for the line. I also stress the line width for presentation purposes in my example graph.
# define a regression line
lr <- lm(hwy ~ displ, mpg)
# draw basis plotly plot, create grouping by setting cyl as factor
fig1 <- plot_ly(data = mpg, x = ~displ, y = ~hwy, color = ~as.factor(cyl)
, type = "scatter", mode = "markers")
# add regression line
fig1 <- fig1 %>%
add_lines(data=mpg %>% group_by(cyl), x = ~displ, y = fitted(lr)
, line = list(width = 2, dash = "dot", color="red")
#----------------- remove legend for line - comment out to see it displayed
, showlegend = FALSE
)

Is it possible to combine the ggplot legend with the plotly legend so they are equivalent?

I'm trying to combine ggplot and plotly together to make a timeline.
It's working great, but have an issue using the legend. The code below replicates what I'm trying to do.
library(ggplot2)
library(plotly)
x=1:10
df2 = data.frame(x,y = 2*x+rnorm(length(x)),lab = as.factor(c("col1","col2")))
status_colors <- c("#0070C0", "#00B050", "#FFC000", "#C00000","darkgreen","purple","darkgrey","blue","salmon","darkorange","black","navy","darkblue")
status_levels <- c(sort(unique(df2$lab)))
p= ggplot(df2,aes(x=x, y=y, col = lab)) + geom_point() + labs(col="labtest") +
scale_color_manual(values=status_colors,
labels=status_levels, drop = FALSE)
fig = ggplotly(p, tooltip = NULL)
fig %>%
add_text(
x = df2$x,
y = ifelse(df2$y>0,df2$y+0.05,df2$y-0.05),
text = df2$lab,
hovertext = df2$lab,
hoverinfo = 'text',
mode ="text",
textfont = list(color=status_colors[df2$lab], size =10),
marker = list(color=status_colors[df2$lab], size = 0.00001),
showlegend = T,
textposition = ifelse(df2$y>0,"top center","bottom center")
)
Basically, as you can see in the image, the label of each point is the same colour as the point that it is attached to. But whenever I add the legend of the label text from plotly, there is a new legend that appears that controls all the points regardless of their colour.
Thus, is there a way to combine the ggplot legend with the plotly legend so that it's only written col1 and col2 with the right colour and that whenever I interact with the points of a certain colour, the label attached to it stays there?
In other words, is there a way to remove the "trace 2" legend and make the "add_text" know that there is a legend already created in ggplot?
If I got you right, besides getting rid of the second legend (which can be simply achievd by setting showlegend = FALSE) you want one legend to control both the points and the labels. This can be achieved via legendgroups. Instead of adding labels with one add_text you could (or have to? Sorry. Still a plotly newbie so perhaps there is a simpler approach) add the labels via two add_text calls one for each col. Instead of copy and paste (which is probably okay for just two cols, but with more cols ...) you can add these via the magic of purrr::reduce to the ggplotly object. Try this:
library(ggplot2)
library(plotly)
library(purrr)
x=1:10
df2 = data.frame(x,y = 2*x+rnorm(length(x)),lab = as.factor(c("col1","col2")))
status_colors <- c("#0070C0", "#00B050", "#FFC000", "#C00000","darkgreen","purple","darkgrey","blue","salmon","darkorange","black","navy","darkblue")
status_levels <- c(sort(unique(df2$lab)))
p= ggplot(df2,aes(x=x, y=y, col = lab)) + geom_point() +
labs(col="labtest") +
scale_color_manual(values=status_colors,
labels=status_levels, drop = FALSE)
fig = ggplotly(p, tooltip = NULL)
purrr::reduce(c("col1", "col2"), ~ .x %>% add_text(
data = filter(df2, lab == .y),
x = ~x,
y = ~ifelse(y > 0, y + 0.05, y-0.05),
text = ~lab,
hovertext = ~lab,
hoverinfo = 'text',
mode ="text",
textfont = list(color= ~status_colors[lab], size =10),
marker = list(color= ~status_colors[lab], size = 0.00001),
showlegend = FALSE,
textposition = ~ifelse(y>0, "top center","bottom center"),
legendgroup = .y
), .init = fig)
BTW: I also simplified the code a little bit. You don't need df2$... because (gg)plotly already knows the data.

Colors and text annotations in Plotly R - interactive mode

Problem: The following code produces a plotly plot which groups data based on color and annotates text on the respective y-data-points. When interacting with the plot (in the viewer pane), the selection of e.g. only model a4 (by clicking on the line) does not work correctly as the lines disappear for all other models but the according numbers won't. Any ideas how to fix this?
library(plotly)
library(data.table)
dt <- as.data.table(mpg)
plot_ly(dt[model %in% c("a4", "passat")],
x = ~year,
y = ~displ,
color = ~model,
colors = "Set1") %>%
add_lines() %>%
add_text(y = ~displ,
text = ~displ,
textposition = "top right",
showlegend = FALSE) %>%
layout(xaxis = list(title = ""),
yaxis = list(title = "Anzahl"))
Below you can find a figure describing my problem. Once I select only a4 in the plotly chart, the passat line disappears however the numbers associated to this line remain.
Aim: How to modify the code such that not only the line disappears for a4/passat but also the associated numbers?
Appreciate your suggestions / inputs.
The add_text statement has the option showlegend as FALSE, which effectively hides a potential second legend that would show/hide the text/numbers.
One strategy could be to use legendgroup to group the two legends together for lines and text, while still hiding the text legend. The group should be assigned to model in this case.
library(plotly)
library(data.table)
dt <- as.data.table(mpg)
plot_ly(dt[model %in% c("a4", "passat")],
x = ~year,
y = ~displ,
color = ~model,
colors = "Set1") %>%
add_lines(legendgroup = ~model) %>%
add_text(y = ~displ,
text = ~displ,
textposition = "top right",
showlegend = FALSE,
legendgroup = ~model) %>%
layout(xaxis = list(title = ""),
yaxis = list(title = "Anzahl"))
Plot

Remove continuous legend from plotly

I have a basic scatterplot that I've made in plotly (in R). I'm using a continuous input to color the data points which plotly converts into a gradient. However, the removelegend option doesn't remove a continuous legend the way it removes a discrete legend. Consider the example below.
data = mtcars
data$vs = as.character(data$vs)
plotly::plot_ly(
data = data,
x = ~disp,
y = ~mpg,
color = ~vs,
mode = "markers",
type = "scatter"
) %>%
layout(showlegend = FALSE)
plotly::plot_ly(
data = data,
x = ~disp,
y = ~mpg,
color = ~hp,
mode = "markers",
type = "scatter"
) %>%
layout(showlegend = FALSE)
Is there a way to remove the continuous legend?
The issue arises because in the continuous case plotly doesn't call it a legend, it's a color bar. The easiest way to remove it is to pipe in hide_colorbar()
plotly::plot_ly(
data = data,
x = ~disp,
y = ~mpg,
color = ~hp,
mode = "markers",
type = "scatter"
) %>%
hide_colorbar()

R: Colors in dumbell plot seem to mix in inappropriate ways

I am trying to produce a dumbell plot in R. In this case, there are four rows, and they need to have different and specific colors each. I define the colors as part of the dataset using colorRampPalette(). Then when I produce the plot, the colors get mixed in inappropriate ways. See the image below, and in particular the legend.
As you can see, the orange is supposed to be #7570B3 according to the legend. But this is not correct. The color 7570B3 is purple ! For this reason, the colors that I had defined in the dataset are mixed in the plot. "Alt 2" sound be in orange and "Alt 3" should be in purple.
Does anyone know how to fix this ? Any help would be very appreciated.
Here is a simple version of the code:
table_stats_scores <- data.frame(alt=c("alt1","alt2","alt3","alt4"),
average=c(15,20,10,5),
dumb_colors= colorRampPalette(brewer.pal(4,"Dark2"))(4),
min=c(10,15,5,0),max=c(20,25,15,10)
)
table_stats_scores # This is the dataset
table_stats_scores <- table_stats_scores[order(-
table_stats_scores$average),] # ordering
table_stats_scores$alt <- factor(table_stats_scores$alt,
levels = table_stats_scores$alt[order(table_stats_scores$average)])
# giving factor status to alternatives so that plot_ly() picks up on this
p <- plot_ly(table_stats_scores, x=table_stats_scores$average, color = ~
dumb_colors,
y=table_stats_scores$alt,text=table_stats_scores$alt) %>%
add_segments(x = ~min, xend = ~max, y = ~alt, yend = ~alt,name = "Min-Max
range", showlegend = FALSE, line = list(width = 4)) %>%
add_markers(x = ~average, y = ~alt, name = "Mean",
marker=list(size=8.5),showlegend = FALSE) %>%
add_text(textposition = "top right") %>%
layout(title = "Scores of alternatives",
xaxis = list(title = "scores"),
yaxis = list(title = "Alternatives")
)
p
Yes color can be an issue in plotly, because there are several ways to specify it, and the assignment order of the various elements from the dataframe can be hard to keep in sync.
The following changes were made:
added a list of brighter colors to your dataframe because I couldn't easily visualize the brewer.pal colors. Better to debug with something obvious.
changed the color parameter to the alt column, because it is really just used only indirectly to set the color, and mostly it determines the text in the legend.
added the colors to the text parameter (instead of alt) so I could see if it was assigning the colors correctly.
changed the sort order to the default "ascending" on the table_stat_scores sort because otherwise it assigned the colors in the incorrect order (don't completely understand this - seems like there is some mysterious sorting/re-ordering going on internally)
added a colors parameter to the add_segments and add_markers so that they set the color in the same way using the same column.
I think this gets you want you want:
library(plotly)
library(RColorBrewer)
table_stats_scores <- data.frame(alt=c("alt1","alt2","alt3","alt4"),
average=c(15,20,10,5),
dumb_colors= colorRampPalette(brewer.pal(4,"Dark2"))(4),
min=c(10,15,5,0),max=c(20,25,15,10)
)
table_stats_scores # This is the dataset
table_stats_scores$bright_colors <- c("#FF0000","#00FF00","#0000FF","#FF00FF")
table_stats_scores <- table_stats_scores[order(table_stats_scores$average),] # ordering
table_stats_scores$alt <- factor(table_stats_scores$alt,
levels = table_stats_scores$alt[order(table_stats_scores$average)])
# giving factor status to alternatives so that plot_ly() picks up on this
p <- plot_ly(table_stats_scores, x=~average, color = ~alt, y=~alt,text=~bright_colors) %>%
add_segments(x = ~min, xend = ~max, y = ~alt, yend = ~alt,name = "Min-Max range",
colors=~bright_colors, showlegend = FALSE, line = list(width = 4)) %>%
add_markers(x = ~average, y = ~alt, name = "Mean",
marker=list(size=8.5,colors=~bright_colors),showlegend = FALSE) %>%
add_text(textposition = "top right") %>%
layout(title = "Scores of alternatives",
xaxis = list(title = "scores"),
yaxis = list(title = "Alternatives")
)
p
yielding this:

Resources