Sharing x-axis labels in a horizontal plotly subplot figure - r

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)
)

Related

Is there a way to add a shared axis title on a subplot?

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

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)

How to add fixed horizontal and vertical lines to a scatter plot in plotly

I have the following code producing a scatter plot and I would like to add both vertical and horizontal lines representing the mean values of the y axis and x axis, how could I do that?
f <- list(
family = "Courier New, monospace",
size = 18,
color = "#7f7f7f"
)
x <- list(
title = "Age of Buildings",
titlefont = f,
zeroline = FALSE,
showline = FALSE,
showticklabels = TRUE,
showgrid = TRUE
)
y <- list(
title = "Total Violations",
titlefont = f,
zeroline = FALSE,
showline = FALSE,
showticklabels = TRUE,
showgrid = TRUE
)
fig2 <- plot_ly(final, x=~agebuilding, y=~violationstotal, mode= "markers", color = ~INdexrehabless6, size = ~totalvalue)
fig2 <- fig2 %>% layout(xaxis = x, yaxis = y, legend=list(title=list(text='<b> Housing Conditions </b>'))) #chaging name legend
fig2
Here is the plot I get
Using some random data for your df final. I don't know whether plotly provides some kind of geom_h/vline ... Instead I constructed the lines using dataframes which contain the start and the end point of the lines. Have a look:
set.seed(50)
final <- data.frame(
agebuilding = 150 * runif(50),
violationstotal = 30 * runif(50),
INdexrehabless6 = factor(sample(0:1, 50, replace = TRUE)),
totalvalue = 100 * runif(50)
)
mean_x <- data.frame(x = rep(mean(final$agebuilding), 2), y = c(0, ceiling(10* max(final$violationstotal))/10))
mean_y <- data.frame(y = rep(mean(final$violationstotal), 2), x = c(0, ceiling(10* max(final$agebuilding))/10))
library(plotly)
fig2 <- plot_ly(final) %>%
add_markers(x=~agebuilding, y=~violationstotal, color = ~INdexrehabless6, size = ~totalvalue) %>%
add_lines(x = ~x, y = ~y, data = mean_x, name = "Mean x") %>%
add_lines(x = ~x, y = ~y, data = mean_y, name = "Mean y")
fig2 <- fig2 %>% layout(xaxis = x, yaxis = y, legend=list(title=list(text='<b> Housing Conditions </b>'))) #chaging name legend
fig2

How to change position of legend

I have the following code producing a scatter plot and I would like to change the position of the legend so that is still outside the plot but in the center or middle, how could I do that?
f <- list(
family = "Courier New, monospace",
size = 18,
color = "#7f7f7f"
)
x <- list(
title = "Age of Buildings",
titlefont = f,
zeroline = FALSE,
showline = FALSE,
showticklabels = TRUE,
showgrid = TRUE
)
y <- list(
title = "Total Violations",
titlefont = f,
zeroline = FALSE,
showline = FALSE,
showticklabels = TRUE,
showgrid = TRUE
)
fig2 <- plot_ly(final, x=~agebuilding, y=~violationstotal, mode= "markers", color = ~INdexrehabless6, size = ~totalvalue)
fig2 <- fig2 %>% layout(xaxis = x, yaxis = y, legend=list(title=list(text='<b> Housing Conditions </b>'))) #chaging name legend
fig2
Here is the plot I get
For the default legend with vertical orientation the positioning corresponds to
layout(legend = list(orientation = "v", y = 1, x = 1))
If you want to put it at the bottom in the y-direction use
layout(legend = list(orientation = "v", y = 0, x = 1))
and if you want to have it centered in the y-direction use
layout(legend = list(orientation = "v", y = .5, x = 1))
If case of a horizontal orientation the default positioning is
layout(legend = list(orientation = "h", y = -.1, x = 0))
and puts the legend in the lower left corner beneath the plot.
Theres a few ways to do this:
fig2 <- fig2 + layout(legend = list(x = 0.1, y = 0.9)) #puts it on the plot, mess with x and y numbers
fig2 <- fig2 + layout(legend = list(orientation = 'h')) #puts it on the below the plot
See this for more info: https://plotly.com/r/legend/
Basically you'd just do this to your code:
fig2 <- fig2 %>% layout(xaxis = x, yaxis = y, legend=list(title = list(text='<b> Housing Conditions </b>', orientation = 'h')))

R plotly overlay heatmaps

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)

Resources