Related
I'm trying to create a 2x2 subplot, with both plots in each column having the same y-axis title, like this :
i.e. one 'title' (here called annotations, cf. later) for the left column (blue+green) and one for the right column (yellow+red).
I can easily have a yaxis title for each plot but I'm stumped as to making shared ones.
I tried using annotations, like this (this is the code used to render the plot shown above) :
if (!require("plotly")) install.packages("plotly")
library(plotly)
group <- c("a", "b", "c")
values <- c(0, 5, 10)
df <- data.frame(group, values)
plot <- df %>%
plot_ly() %>%
add_trace(x = ~group, y = ~values, type = "scatter", mode = "line") %>%
layout(yaxis = list(ticks = "outside"), xaxis = list(showline = TRUE))
plot
subdf1 <- subplot(plot, plot, nrows = 1, margin = 0.06)
subdf2 <- subplot(plot, plot, nrows = 1, margin = 0.06)
subdf <- subplot(subdf1, subdf2, nrows = 2, margin = 0.06) %>%
layout(annotations = list(list(x = -0.1, y = 0.5, text = "<b>First annotation</b>", xref = "paper", yref = "paper", xanchor = "center", yanchor = "center", showarrow = FALSE, textangle = -90, font = list(color = "black", size = 16)),
list(x = 0.48, y = 0.5, text = "<b>Second annotation</b>", xref = "paper", yref = "paper", xanchor = "center", yanchor = "center", showarrow = FALSE, textangle = -90, font = list(color = "black", size = 16))))
subdf
My main gripe with this method is that when the plot is resized, the annotations (mainly the first one, in the negative range for x-axis placement) move around the x-axis.
Same plot but wider :
I used xref = "paper" as I thought it meant the whole plot area i.e. the whole white background, but in such case, my annotation wouldn't disappear (and wouldn't be in negatives, but I'm possibly not thinking about this the right way). I did try using xref = x but it won't go into negatives and instead just push the data to the right.
So all in all, two questions :
Is there a native way to have a shared axis title for subplots?
If not, is there a way to make sure that my annotations stay in the same relative place as the plots and axes when resizing the subplot?
If you aren't tied to using plotly, this can be done in a straightforward way using faceting in ggplot. It may require some rearranging of your data into tidy format but gives some serious flexibility while plotting!
library(ggplot2)
group <- c("a", "b", "c")
values <- c(0, 5, 10)
df <- data.frame(group, values)
df <- data.frame(group = rep(c('a','b','c'), 4),
values = rep(c(0,5,10), 4),
facet = rep(c('W','X','Y','Z'), each = 3))
ggplot(df, aes(x = group, y = values, colour = facet, group = 1)) +
geom_line(size = 1.1) +
geom_point(size = 2) +
facet_wrap(~facet) +
theme_bw() +
labs(x = 'Shared X axis title', y = 'Shared Y axis title', colour = 'Traces') +
theme(
strip.background = element_blank(),
strip.text.x = element_blank()
)
You could create a separate title in each layout of both subplots and combine them using titleY like this:
library(plotly)
library(dplyr)
group <- c("a", "b", "c")
values <- c(0, 5, 10)
df <- data.frame(group, values)
plot <- df %>%
plot_ly() %>%
add_trace(x = ~group, y = ~values, type = "scatter", mode = "line") %>%
layout(yaxis = list(ticks = "outside"), xaxis = list(showline = TRUE))
subdf1 <- subplot(plot, plot, nrows = 1) %>%
layout(yaxis = list(title = "First annotation"))
subdf2 <- subplot(plot, plot, nrows = 1) %>%
layout(yaxis = list(title = "Second annotation"))
subdf <- subplot(subdf1, subdf2, nrows = 2, titleY = TRUE)
subdf
Created on 2023-01-23 with reprex v2.0.2
Edit
Change margin in layout:
library(plotly)
library(dplyr)
group <- c("a", "b", "c")
values <- c(0, 5, 10)
df <- data.frame(group, values)
plot <- df %>%
plot_ly() %>%
add_trace(x = ~group, y = ~values, type = "scatter", mode = "line") %>%
layout(yaxis = list(ticks = "outside"), xaxis = list(showline = TRUE))
subdf1 <- subplot(plot, plot, nrows = 1, margin = 0.06)
subdf2 <- subplot(plot, plot, nrows = 1, margin = 0.06)
subdf <- subplot(subdf1, subdf2, nrows = 2, margin = 0.06) %>%
layout(margin = 0.01,
annotations = list(list(x = -0.1, y = 0.5, text = "<b>First annotation</b>", xref = "paper", yref = "paper", xanchor = "center", yanchor = "center", showarrow = FALSE, textangle = -90, font = list(color = "black", size = 16)),
list(x = 0.48, y = 0.5, text = "<b>Second annotation</b>", xref = "paper", yref = "paper", xanchor = "center", yanchor = "center", showarrow = FALSE, textangle = -90, font = list(color = "black", size = 16))))
subdf
Created on 2023-01-23 with reprex v2.0.2
I'm trying to set the colour in R for "points" or markers in plotly with a custom palette. It doesn't work. I'm also trying to add a title to the graph, but it won't appear. In addition, I'd like to remove some elements of the legend (like the extra "z") and add a title to the legend elements. Nothing seems to work, even if it is present in the code.
library(plotly)
library(tidyverse)
set.seed(123456)
pal <- c("black", "orange", "darkgreen", "pink")
pal <- setNames(pal, levels(as.factor(c("gr1","gr2","gr3","gr4"))))
linedat = data.frame(x = rep(mean(1:50),2),
y = rep(mean(1:50),2),
z = c(0,1))
zoom = 3
pg = plotly::plot_ly(x = 1:50,
y = 1:50,
z = outer(1:50,1:50,"+")/100) %>%
add_surface(contours = list(
z = list(show = TRUE, start = 0, end = 1, size = 0.05)),
opacity = 1) %>%
add_markers(x = rnorm(50,25,5),
y = rnorm(50,25,5),
marker = list(opacity = 0.9),
type="scatter3d",
mode = "markers",inherit = FALSE,
colors = pal,
color = as.factor(sample(1:4,50,replace = T)),
z = rbinom(50,1,.5)) %>%
layout(scene=list(title = "Title won't show up????",
xaxis = list(title = 'trait1'),
yaxis = list(title = 'trait1',autorange = "reversed"),
camera = list(eye = list(x = cos(0.8*pi)*zoom, y = sin(pi*1.3)*zoom, z= 2.00)),
legend = list(title=list(text='<b> Groups </b>')))) %>%
add_trace(data=linedat, x=~x, y=~y, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[1],
width = 14),name = "Avg. data1")%>%
add_trace(data=linedat, x=~x+20, y=~y+20, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[4],
width = 14),name = "Avg. data2")
pg
See how here I'm able to set the colour of the points, but I'm not able to get the names of the colours to show in the legend (I modified the code to match what #Kat suggested):
library(plotly)
library(tidyverse)
set.seed(123456)
pal <- c("black", "orange", "darkgreen", "pink")
pal <- setNames(pal, levels(as.factor(c("gr1","gr2","gr3","gr4"))))
linedat = data.frame(x = rep(mean(1:50),2),
y = rep(mean(1:50),2),
z = c(0,1))
zoom = 3
pg = plotly::plot_ly(x = 1:50,
y = 1:50,
z = outer(1:50,1:50,"+")/100) %>%
add_surface(contours = list(
z = list(show = TRUE, start = 0, end = 1, size = 0.05)),
opacity = 1, colorbar = list(title = "Only one Z")) %>%
add_markers(x = rnorm(50,25,5),
y = rnorm(50,25,5),
marker = list(color = pal[as.factor(sample(1:4,50,replace = T))],opacity = 0.9),
type="scatter3d",
mode = "markers",inherit = FALSE,
colors = pal,
z = rbinom(50,1,.5)) %>%
layout(title = "Title that won't show",
margin = list(t = 40),
legend = list(title = list(
text = "<br>Legends deserve names, too")),
scene=list(xaxis = list(title = 'trait1'),
yaxis = list(title = 'trait1',autorange = "reversed"),
camera = list(eye = list(x = cos(0.8*pi)*zoom,
y = sin(pi*1.3)*zoom, z= 2.00)))) %>%
add_trace(data=linedat, x=~x, y=~y, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[1],
width = 14),name = "Avg. data1")%>%
add_trace(data=linedat, x=~x+20, y=~y+20, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[4],
width = 14),name = "Avg. data2");pg
Your original plot_ly call and add_trace calls can remain as is. I've included the changes needed for the layout call and added the call needed for colorbar.
The layout first.
layout(title = "Title that won't show", # <------ give my plot a name
margin = list(t = 40), # don't smash that title, either
legend = list(title = list(
text = "<br>Legends deserve names, too")), # <--- name my legend
scene=list(title = "Title won't show up????", # <- this can go away
xaxis = list(title = 'trait1'),
yaxis = list(title = 'trait1',autorange = "reversed"),
camera = list(eye = list(x = cos(0.8*pi)*zoom,
y = sin(pi*1.3)*zoom, z= 2.00)),
legend = list(title=list(text='<b> Groups </b>')))) %>%
colorbar(title = "Only one Z") # <--- give me one z colorbar title
(Some colors are different in the image; I didn't realize I had the pal object...sigh)
Update
This addresses your additional questions.
First, I'm not getting an error from the method in which I title the color bar. You'll have to share what error you're getting.
I didn't realize that it was ignoring the colors you set in markers, either. The easiest way to address this is to call that trace first. Since nothing else was declared within markers, I called opacity outside of the list, but it's fine the way you have it.
First, I commented out the setNames call, because that won't work for the marker's trace and it doesn't matter to the other traces.
library(plotly)
set.seed(123456)
pal <- c("black", "orange", "darkgreen", "pink")
# pal <- setNames(pal, levels(as.factor(c("gr1","gr2","gr3","gr4"))))
linedat = data.frame(x = rep(mean(1:50),2),
y = rep(mean(1:50),2),
z = c(0,1))
zoom = 3
I made plot_ly() empty and put all the data for the surface in that trace. I also added inherit = false so that the layout could go at the end without the data errors.
set.seed(123456)
pg = plotly::plot_ly() %>%
add_trace(inherit = F,
x = rnorm(50,25,5),
y = rnorm(50,25,5),
type="scatter3d",
mode = "markers",
opacity = .8,
colors = pal,
color = as.factor(sample(1:4, 50, replace = T)),
z = rbinom(50,1,.5)) %>%
add_surface(x = 1:50,
y = 1:50,
z = outer(1:50,1:50,"+")/100,
colorscale = "Viridis",
contours = list(
z = list(show = TRUE, start = 0, end = 1, size = 0.05)),
opacity = 1) %>%
add_trace(data=linedat, x=~x, y=~y, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[1],
width = 14),name = "Avg. data1", inherit = F) %>%
add_trace(data=linedat, x=~x+20, y=~y+20, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[4],
width = 14),name = "Avg. data2", inherit = F) %>%
The last part is the layout, but this is not different than my original answer.
layout(title = "Title that won't show", # <------ give my plot a name!
margin = list(t = 40), # don't smash that title, either
legend = list(title = list(
text = "<br>Legends deserve names, too"),
tracegroupgap = 350), # <--- name my legend!
scene=list(title = "Title won't show up????", # <--- this can go away!
xaxis = list(title = 'trait1'),
yaxis = list(title = 'trait1',autorange = "reversed"),
camera = list(eye = list(x = cos(0.8*pi)*zoom,
y = sin(pi*1.3)*zoom, z= 2.00)),
legend = list(title=list(text='<b> Groups </b>')))) %>%
colorbar(title = "Only one Z") # <--- give me one z for the title!
pg
I am trying to reproduce a graph generated via ggplotly with plot_ly. I am struggling however with the colorbar.
This is the ggploty plot that I would like to reproduce, and in particular the colorbar:
library(plotly)
X <- data.frame(w = rep(c("a", "b"), each = 8),
x = 1:16,
y = 1:16,
z = c(1, 1:13, 13, 13))
X$z_scaled <- (X$z-min(X$z))/(max(X$z)-min(X$z)) # scale to 0-1
# ggplot
gg <- ggplot(X) +
geom_point(aes(x, y, color = z_scaled, alpha = w, text = paste0(x, ", ", y))) +
scale_color_gradient2(low = '#0d71db', mid = "#dbc00d", high = '#db220d',
midpoint = .5, breaks = 0:1, limits = 0:1) +
scale_alpha_manual(name = " ", values = rep(1, nrow(X))) +
labs(color = "Z", x = "", y = "")
ggplotly(gg, type = "scattergl", tooltip = "text") %>% toWebGL()
This is what I have with plot_ly:
length_unique_vals <- length(unique(X$z))
.colors <- colorRampPalette(c('#0d71db', "#dbc00d", "#db220d"))(length_unique_vals)
.colors <- .colors[factor(X$z)]
plot_ly() %>%
add_markers(
data = X, x = ~x, y = ~y,
split = ~w,
text = ~paste0(x, ", ", y),
hoverinfo = "text",
type = "scattergl",
mode = "markers",
marker = list(
# color = ~z_scaled,
color = .colors,
# colorscale = list(c(0, .5, 1), c("#0d71db", "#dbc00d", "#db220d")),
colorscale = .colors,
hoverlabel = list(bgcolor = .colors),
colorbar = list(
title = list(text = "Z"),
len = .5,
x = 1,
y = .7
)
)
) %>%
layout(
legend = list(x = 1, y = .4, bgcolor = 'rgba(255,255,255,0.6)')
) %>% toWebGL() %>% partial_bundle(local = FALSE)
As you can see, the colorbar is not displaying correctly. I have tried multiple possibilities (commented above) without success. What am I missing?
Edit
#Kat's answer solves the colorbar issue. However, if you want to use scattergl or toWebGl you will need to fix the hoverlabel background so it remains dynamic. Here is a solution for that below building on her answer.
length_unique_vals <- length(unique(X$z))
.colors <- colorRampPalette(c('#0d71db', "#dbc00d", "#db220d"))(length_unique_vals)
.colors <- .colors[factor(X$z)]
plot_ly() %>%
add_trace(x = ~x,
y = ~y,
split = ~w, # instead of alpha or opacity
data = X,
type = "scattergl",
mode = "markers",
color = ~z_scaled, # color = var and colors = literal colors
colors = c('#0d71db', "#dbc00d", '#db220d'),
hoverlabel = list(bgcolor = .colors)) %>% # Fix hovercolor bg
layout(xaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
yaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
plot_bgcolor = "#eeeeee") %>% # gray background
colorbar(title = "Z", # colorbar title
dtick = c(0, 1), # colorbar ticks
thickness = 25) %>% # width
toWebGL() %>% partial_bundle(local = FALSE)
Edit 2
The hoverlabel bgcolor breaks down then the split factor is not ordered. This is why it needs to be ordered first.
library(plotly)
library(data.table)
X <- data.table(w = rep(c("a", "b"), 8), #not ordered
x = 1:16,
y = 1:16,
z = c(1, 1:13, 13, 13))
X[, z_scaled := (X$z-min(X$z))/(max(X$z)-min(X$z))] # scale to 0-1
# Get colors for hoverlabel bgcolor
X <- X[order(w)]
length_unique_vals <- length(unique(X$z))
.colors <- colorRampPalette(c('#0d71db', "#dbc00d", "#db220d"))(length_unique_vals)
.colors <- .colors[factor(X$z)]
plot_ly() %>%
add_trace(x = ~x,
y = ~y,
split = ~w, # instead of alpha or opacity
data = X,
type = "scattergl",
mode = "markers",
color = ~z_scaled, # color = var and colors = literal colors
colors = c('#0d71db', "#dbc00d", '#db220d'),
hoverlabel = list(bgcolor = .colors),
marker = list(size = 10)) %>% # Fix hovercolor bg
layout(xaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
yaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
plot_bgcolor = "#eeeeee") %>% # gray background
colorbar(title = "Z", # colorbar title
dtick = c(0, 1), # colorbar ticks
thickness = 25) %>% # width
toWebGL() %>% partial_bundle(local = FALSE)
Assuming you even wanted the gray background, this should work. If something isn't clear or not what you were looking for, let me know.
You don't even need the text, because you specified the default hover content.
plot_ly() %>%
add_trace(x = ~x,
y = ~y,
split = ~w, # instead of alpha or opacity
data = X,
type = "scatter",
mode = "markers",
color = ~z_scaled, # color = var and colors = literal colors
colors = c('#0d71db', "#dbc00d", '#db220d')) %>%
layout(xaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
yaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
plot_bgcolor = "#eeeeee") %>% # gray background
colorbar(title = "Z", # colorbar title
dtick = c(0, 1), # colorbar ticks
thickness = 25) # width
How can I reproduce the following graph in plotly
library(dplyr)
library(ggplot2)
tibble(x =1:10, y = 1:10) %>%
ggplot(aes(x,y)) +
geom_line() +
scale_y_continuous(sec.axis = ~.*2)
I tried the following code based on this answer here
library(dplyr)
library(plotly)
tibble(x =1:10, y = 1:10) %>%
mutate(y2 = y*2) %>%
plot_ly() %>%
add_lines(x =~x, y =~y) %>%
add_lines(x= ~x, y=~y2,
yaxis = "y2", color = I("transparent"),
hoverinfo='skip', showlegend=FALSE) %>%
layout(yaxis2 = list(side = "right", overlaying = "y", showgrid = FALSE),
margin = list(r = 50))
While at first glance it appears to work, it only provides a partial solution, since if I interactively try to change the scale of the main (left) y axis on the produced graph (by dragging it up or down), the right axis does not move with the graph (because it is linked only to the second invisible graph). This of course is not acceptable as it does not allow using any of interactive features of plotly reliably which is the reason I wanted to use it to begin with instead of ggplot.
Edit: Just realized that this plotly solution does not seem to work at all in the case of non linear transformation between the axes (while ggplot handles it beautifully).
You just need to set up dtick and tick0 for plotly to have the same graph as ggplot2 one. See below;
library(plotly)
library(dplyr)
tibble(x =1:10, y = 1:10) %>%
mutate(y2 = y*2) -> df1
n0 <- 4
y0 <- max(df1$y)/n0
x0 <- max(df1$x)/n0
df1 %>%
plot_ly(data = . , x = ~x, y = ~y,
type = "scatter", mode = "lines", width = 800,
color = I("red"), name = "") %>%
add_trace(x = ~x, y = ~y2, yaxis = "y2",
color = I("red"), name = "") %>%
layout(yaxis = list(showline = FALSE, side = "left",
title = "", color = "red",
dtick = y0, tick0 = y0, fixedrange=TRUE),
yaxis2 = list(showline = FALSE, side = "right", overlaying = "y", position = 1,
title = "", anchor = "free", color = "blue",
dtick = 2*y0, tick0 = 2*y0, fixedrange=TRUE),
xaxis = list(showline = FALSE, zeroline = FALSE, title = "",
dtick = x0, tick0 = x0),
showlegend = FALSE,
margin = list(l = 50, r = 50, b = 50, t = 50, pad = 4)
)
Created on 2020-06-19 by the reprex package (v0.3.0)
I'm creating a R plotly boxplot for these data:
set.seed(1)
df <- data.frame(value = rnorm(100),
value.error. = runif(100,0.01,0.1),
treatment = rep(LETTERS[1:10], 10),
replicate = rep(1:10, 10), stringsAsFactors = F)
df$treatment <- factor(df$treatment)
Where in each box I add the replicates as points:
library(dplyr)
plotly::plot_ly(x = df$treatment, split = df$treatment, y = df$value,
type = "box", showlegend = F, color = df$treatment,
boxpoints = F, fillcolor = 'white') %>%
plotly::add_trace(x = df$treatment, y = df$value, type = 'scatter', mode = "markers",
marker = list(size = 8), showlegend = F, color = df$treatment)
Which gives:
Now I'd like to add vertical error bars to each point (according to df$value.error).
This:
plotly::plot_ly(x = df$treatment, split = df$treatment, y = df$value,
type = "box", showlegend = F, color = df$treatment,
boxpoints = F, fillcolor = 'white') %>%
plotly::add_trace(x = df$treatment, y = df$value, type = 'scatter', mode = "markers",
marker = list(size = 8), showlegend = F, color = df$treatment) %>%
plotly::add_trace(error_y = list(array = df$sd), showlegend = F)
Gives me the same plot above.
However, if I only plot the points and add their errors using:
plotly::plot_ly(x = df$treatment, y = df$value,
type = 'scatter', mode = "markers",
marker = list(size = 8), showlegend = F, color = df$treatment) %>%
plotly::add_trace(error_y =list(array = df$sd), showlegend = F)
I do get the points with their vertical error bars:
So my question is how to have the box + points + error bars to work?
And, if the solution can also combine jittering the points with their error bars that will be even better.
You can add the box plot after plotting the points and error bars.
library(plotly)
plot_ly(data = df,
x = ~treatment, y = ~value,
type = 'scatter', mode = "markers",
marker = list(size = 8), showlegend = F, color = df$treatment) %>%
add_trace(error_y =list(array = ~value.error.), showlegend = F) %>%
add_boxplot(x = ~treatment, split = ~treatment, y = ~value,
showlegend = F, color = ~treatment,
boxpoints = F, fillcolor = 'white')
Data:
set.seed(1)
df <- data.frame(value = rnorm(100),
value.error. = runif(100,0.01,0.1),
treatment = rep(LETTERS[1:10], 10),
replicate = rep(1:10, 10),
stringsAsFactors = F)
df$treatment <- factor(df$treatment)