R plotly overlay heatmaps - r

I have two matrices, A and B, I want to make two heatmaps with plotly, and overlay them
library(plotly)
A = matrix(c(1:8, rep(0, 8)), ncol = 4)
B = matrix(c(rep(0, 8), 1:8), ncol = 4)
PA <- plot_ly(z = A, type = "heatmap", colors = colorRamp(c("white", "green")))
PB <- plot_ly(z = B, type = "heatmap", colors = colorRamp(c("white", "red")))
When I try to overlay them, they are indeed overplayed, but the second heatmap totally masked the first one.
PA %>% add_trace(z = B, type = "heatmap")
I could change the opacity in order to 'see' both heatmaps
PA %>% add_trace(z = B, opacity = 0.5, type = "heatmap")
But it is really not beautiful, and I cannot set different colours for each heatmap.
Is there any elegant way to overlay them like the following example? thanks a lot.
p = plot_ly(x = rnorm(500), opacity = 0.6, type = "histogram") %>%
add_trace(x = rnorm(500)+1) %>%
layout(barmode="overlay")

I am not sure if it is possible, but maybe you could trick it. You could try:
ay <- list(
title = "",
zeroline = FALSE,
showline = FALSE,
showticklabels = FALSE,
showgrid = FALSE
)
PB <- PB %>% layout(yaxis = ay, xaxis = list(range = c(1.5, 3.5), dtick = 1))
PA <- PA %>% layout(yaxis = list(dtick = 1), xaxis = list(range = c(-0.5, 1.5), dtick = 1))
subplot(PA, PB, nrows = 1, shareX = TRUE, shareY = FALSE)

Related

Setting colours in plotly R is not working (factors) in add markers

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

R plotly with custom colorbar

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

Secondary axis in plotly does not work for different scales

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)

Sharing x-axis labels in a horizontal plotly subplot figure

I have a list of figures created with R's plotly, just as an example:
library(plotly)
library(dplyr)
set.seed(1)
data.df <- data.frame(val = c(rnorm(100,0,1),rnorm(100,1,1)), group = c(rep("A",100),rep("B",100)))
density.df <- do.call(rbind,lapply(levels(data.df$group),function(g){
dens <- density(dplyr::filter(data.df,group == g)$val)
data.frame(x = dens$x, y = dens$y, group = g)
}))
plot.list <- lapply(1:5,function(x)
plot_ly(x = density.df$x, y = density.df$y, type = 'scatter', mode = 'lines',color = density.df$group, showlegend = (x == 5)) %>%
layout(xaxis = list(title= "Value", zeroline = F), yaxis = list(title = "Density", zeroline = F))
)
Which I'd like to put together horizontally, where there will be only a single shared x-axis label and a single shared y-axis label.
I'm using:
plotly::subplot(plot.list, nrows = 1, shareX = T, shareY = T, titleX = T, titleY = T)
And getting:
Is it not possible to get a single x-axis label in a horizontal plot?
The same occurs for the y-axis label if I change the nrows argument value from 1 to 5.
You can manually add an annotation
https://community.plot.ly/t/subplots-how-to-add-master-axis-titles/13927/5
myplotly(df) %>%
add_annotations(
text = "my x title",
x = 0.5,
y = 0,
yref = "paper",
xref = "paper",
xanchor = "center",
yanchor = "bottom",
yshift = -35,
showarrow = FALSE,
font = list(size = 15)
)

Add jitter to box plot using markers in plotly

I made a boxplot:
dat %>%
plot_ly(y = ~xval, color = ~get(col), type = "box",
boxpoints = "all", jitter = 0.7,
pointpos = 0, marker = list(size = 3),
source = shiny_source, key = shiny_key,
hoverinfo = 'text', text = txt)
but problem is that jittered points are not interactive and cannot be marked separately, so I came with an idea to add those points using add_markers:
dat %>%
plot_ly(y = ~xval, color = ~get(col), type = "box",
boxpoints = FALSE, jitter = 0.7,
pointpos = 0, marker = list(size = 3),
source = shiny_source, key = shiny_key,
hoverinfo = 'col', text = txt
) %>%
add_markers(x = ~get(col), y = ~varval, size = I(6))
but now points are in straight line and I'd like to add some jitter (for example by using beeswarm package). But I don't know how to get coordinates of qualitative variable IC0 on X axis. Any ideas?
I find myself in the same potential case often with plotly and ggplot2-- 3 lines of code to get 90% of what I want, and 30 lines of code to get the aesthetics just right.
One potential solution/workaround: Take advantage of R's "factors are coded with integers" paradigm, plot everything on a numeric scale, and then cover your tracks by hiding x labels and x hover values.
dat <- data.frame(xval = sample(100,1000,replace = TRUE),
group = as.factor(sample(c("a","b","c"),1000,replace = TRUE)))
dat %>%
plot_ly() %>%
add_trace(x = ~as.numeric(group),y = ~xval, color = ~group, type = "box",
hoverinfo = 'name+y') %>%
add_markers(x = ~jitter(as.numeric(group)), y = ~xval, color = ~group,
marker = list(size = 6),
hoverinfo = "text",
text = ~paste0("Group: ",group,
"<br>xval: ",xval),
showlegend = FALSE) %>%
layout(legend = list(orientation = "h",
x =0.5, xanchor = "center",
y = 1, yanchor = "bottom"
),
xaxis = list(title = "Group",
showticklabels = FALSE))
Yields the following

Resources