How can I in the plotly plot: a) position the axis titles horizontally above their respective axes? b) reduce the space between y=0 and the X-axis? c) draw the green data points for Power before the increase and after the decrease (at y=0) as thick as the green data points where y>0?
I would greatly appreciate your support.
library(plotly)
# Sample data
d <- data.frame(
time = seq(1, 600, by = 1),
VO2 = c(rnorm(120, 250, 10), rnorm(120, 300, 15), seq(300, 2000, length.out = 240), rnorm(120, 300, 15)),
VCO2 = c(rnorm(120, 200, 10), rnorm(120, 250, 15), seq(250, 1800, length.out = 240), rnorm(120, 250, 15)),
power = c(rep(0, 120), rep(0, 120), seq(0, 160, length.out = 240), rep(0, 120))
)
# Create the plot
fig <- plot_ly(data = d) %>%
add_lines(x = ~time, y = ~VO2,
line = list(color = 'blue'), name = "VO2") %>%
add_lines(x = ~time, y = ~VCO2, yaxis = "y2",
line = list(color = 'red'), name = "VCO2") %>%
add_lines(x = ~time, y = ~power, yaxis = "y3",
line = list(color = 'green'), name = "Power") %>%
layout(
xaxis = list(
title = list(text = 'Time [sec]', standoff = 10),
color = 'black',
showline = TRUE,
tickcolor = 'black',
tickwidth = 2,
linewidth = 2,
ticks = "outside"
),
yaxis = list(
title = list(text = 'VO2', standoff = 20),
showline = TRUE,
side = "left",
color = 'blue',
tickcolor = 'blue',
tickwidth = 2,
linewidth = 2,
ticks = "outside",
range = c(0, max(c(d$VO2, d$VCO2)))
),
yaxis2 = list(
title = list(text = 'VCO2', standoff = 20),
showline = TRUE,
overlaying = "y",
anchor = "free",
side = "left",
color = 'red',
tickcolor = 'red',
tickwidth = 2,
linewidth = 2,
ticks = "outside",
position = -0.1,
range = c(0, max(c(d$VO2, d$VCO2)))
),
yaxis3 = list(
title = list(text = 'Power', standoff = 20),
showline = TRUE,
overlaying = "y",
side = "right",
color = 'green',
tickcolor = 'green',
tickwidth = 2,
linewidth = 2,
ticks = "outside",
range = c(0, max(d$power) * 1.25)
),
showlegend = FALSE, # Removes legend
margin = list(pad = 50, b = 0, l = 100, r = 100)
)
fig
You said you were looking for three things:
There are three main steps to add the axis titles on top:
shift
to "y2" axisNew layout example removing titles (this code will be provided in full in a bit, this is just a bit to show you what's happening.
yaxis = list(
# title = list(text = 'VO2', standoff = 20), # remove title
title = '',
showline = TRUE,
For the shift, I did a bit of guess and check, since the plot wanted to overlap the axes when I removed padding, I moved y2
left of y1
axis. This is with the shift
in yaxis2 = ...
set to 70.
The other aspects of the layout remain as you coded them in your question.
When I created the annotations, I used x
positions like I used shift, a bit of guess and check for visual aesthetics. I used the colors for titles as you've assigned them. Lastly, I set the font size to 14. That's the plotly default axis title size.
Most of the content needed to make an annotation is identical here, so I used lapply
to build the list.
This is the code used to create the annotations:
xs <- c(-.095, -.2, 1.095) # x-axis positions
clr <- c("blue", "red", "green") # title colors
anno <- lapply(1:3, \(k) { # build new axis titles as annotations
if(names(d)[k + 1] != toupper(names(d)[k + 1])) { # take titles from dataframe
tlt = paste0(toupper(substring(names(d)[k + 1], 1, 1)), # capitalize if necessary
substring(names(d)[k + 1], 2, 1000))
} else {
tlt = names(d)[k + 1]
} # using paper space, on top of y axes, at variable x positions
list(xref = "paper", yref = "paper", x = xs[k], y = 1.1, showarrow = F,
font = list(color = clr[k], size = 14), text = tlt)
})
The content in anno
can now be assigned to annotations
in the plot layout
.
Lastly, shift
will not work without an update to the backend Plotly.js. To update this dependency, I used the following function.
fixer <- function(plt) {
# changes to dependency so that all code works
plt$dependencies[[5]]$src$file = NULL
plt$dependencies[[5]]$src$href = "https://cdn.plot.ly"
plt$dependencies[[5]]$script = "plotly-2.33.0.min.js"
plt$dependencies[[5]]$local = FALSE
plt$dependencies[[5]]$package = NULL
plt
}
Now that shift works, all you need to do to reduce the padding is lower the number you called for in layout(margin = list
.
I'm not exactly sure what you want here, but I've interpreted it as markers for all points in which d$power == 0
. That being said, with six hundred observations, even when the points are huge, it's just a blob that looks like a thick line until you zoom in. I'm guessing that this is just data for this question, not the data you're actually using, so perhaps this information will be more useful in the actual context you're using it in.
To add the markers, you can add another trace. For this trace, you need to identify that it is not using the same data as the other traces.
Here is the markers trace code I used:
add_markers(inherit = F, data = d %>% filter(power == 0), # criteria for markers
x = ~time, y = ~power, yaxis = "y3",
marker = list(color = "green", size = 10), name = "")
This is what the plot looks like with these changes (note the blob for the zero line of power
).
library(plotly)
# Sample data
d <- data.frame(
time = seq(1, 600, by = 1),
VO2 = c(rnorm(120, 250, 10), rnorm(120, 300, 15), seq(300, 2000, length.out = 240), rnorm(120, 300, 15)),
VCO2 = c(rnorm(120, 200, 10), rnorm(120, 250, 15), seq(250, 1800, length.out = 240), rnorm(120, 250, 15)),
power = c(rep(0, 120), rep(0, 120), seq(0, 160, length.out = 240), rep(0, 120))
)
fixer <- function(plt) {
# changes to dependency so that all code works
plt$dependencies[[5]]$src$file = NULL
plt$dependencies[[5]]$src$href = "https://cdn.plot.ly"
plt$dependencies[[5]]$script = "plotly-2.33.0.min.js"
plt$dependencies[[5]]$local = FALSE
plt$dependencies[[5]]$package = NULL
plt
}
# Create the plot
fig <- plot_ly(data = d) %>%
add_lines(x = ~time, y = ~VO2,
line = list(color = 'blue'), name = "VO2") %>%
add_lines(x = ~time, y = ~VCO2, yaxis = "y2",
line = list(color = 'red'), name = "VCO2") %>%
add_lines(x = ~time, y = ~power, yaxis = "y3",
line = list(color = 'green'), name = "Power") %>%
add_markers(inherit = F, data = d %>% filter(power == 0), # criteria for markers
x = ~time, y = ~power, yaxis = "y3",
marker = list(color = "green", size = 10), name = "") %>%
layout(
xaxis = list(
title = list(text = 'Time [sec]', standoff = 10), domain = c(0, 1),
color = 'black',
showline = TRUE,
tickcolor = 'black',
tickwidth = 2,
linewidth = 2,
ticks = "outside", zeroline = F
),
yaxis = list(
# title = list(text = 'VO2', standoff = 20),
title = '',
showline = TRUE,
side = "left",
color = 'blue',
tickcolor = 'blue',
tickwidth = 2,
linewidth = 2,
ticks = "outside",
range = c(0, max(c(d$VO2, d$VCO2)))
),
yaxis2 = list(
# title = list(text = 'VCO2', standoff = 20),
showline = TRUE,
overlaying = "y",
anchor = "free",
side = "left",
color = 'red',
tickcolor = 'red',
tickwidth = 2,
linewidth = 2,
ticks = "outside",
shift = -70,
# position = -0.1,
range = c(0, max(c(d$VO2, d$VCO2)))
),
yaxis3 = list(
# title = list(text = 'Power', standoff = 20),
showline = TRUE,
overlaying = "y",
side = "right",
color = 'green',
tickcolor = 'green',
tickwidth = 2,
linewidth = 2,
ticks = "outside",
range = c(0, max(d$power) * 1.25)
),
showlegend = FALSE, # Removes legend
# margin = list(pad = 50, b = 0, l = 100, r = 100)
margin = list(pad = 20, b = 0, l = 150, r = 100, t = 50)
)
xs <- c(-.095, -.2, 1.095) # x-axis positions
clr <- c("blue", "red", "green") # title colors
anno <- lapply(1:3, \(k) { # build new axis titles as annotations
if(names(d)[k + 1] != toupper(names(d)[k + 1])) { # take titles from dataframe
tlt = paste0(toupper(substring(names(d)[k + 1], 1, 1)), # capitalize if necessary
substring(names(d)[k + 1], 2, 1000))
} else {
tlt = names(d)[k + 1]
} # using paper space, on top of y axes, at variable x positions
list(xref = "paper", yref = "paper", x = xs[k], y = 1.1, showarrow = F,
font = list(color = clr[k], size = 14), text = tlt)
})
fig %>% layout(annotations = anno) %>% fixer() # create the plot!