Partially shaded background for ggplotly-object - r

I'm trying to create a lineplot of a timeseries with ggplot2 and convert it to plotly. A part of the background of this plot is supposed to be shaded in a different color (like this one: Using geom_rect for time series shading in R). Unfortunately annotate() as well as geom_rect aren't transferred to the ggplotly-object as it seems. Therefore I tried to retroactively add a shape using plotly-code (based on this example: https://plot.ly/r/shapes/), but it is not working either, as shown in this reproducible example:
library(plotly)
library(ggplot2)
plot <-ggplot(data = economics, aes(x = date, y = unemploy)) + geom_line()
plot <- ggplotly(plot)
layout(plot,
shapes = list(
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.9,
x0 = "1980-01-01", x1 = "1990-01-01",
y0 = 0, y1 = 4000
)
)
)
So how can I get this shading for my ggplotly-object? Recreating the whole plot in plotly is unfortunately not possible.
Thanks!

As quoted in previous answer, we can access the shapes through plot[['x']][['layout']][['shapes']], and then use the function layout(...) to create the new shapes.
But the plot <- layout(...) function didn't work for me. (throwing unused argument error) Thus, I tried to just use plot[['x']][['layout']][['shapes']] functionality only, and it works.
plot[['x']][['layout']][['shapes']] <- list(
list(type = "rect",
fillcolor = "lightgreen", line = list(color = "lightgreen"), opacity = 0.3,
x0 = 0, x1 = 30, xref = "x",
y0 = -100, y1 = 100, yref = "y"))
Hope that helps for others who can't use the layout(..) function.

In order to make ggplotly work with shapes you would need to first clear the shapes coming from the conversion (beats me why they are there in the first place).
plot[['x']][['layout']][['shapes']] <- c()
and you would need to assign the layout function to your plot object.
plot <- layout(plot,
shapes = list(
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.9,
x0 = "1980-01-01", x1 = "1990-01-01",
y0 = 0, y1 = 4000
)
)
)
library(plotly)
library(ggplot2)
plot <-ggplot(data = economics, aes(x = date, y = unemploy)) + geom_line()
plot <- ggplotly(plot)
plot[['x']][['layout']][['shapes']] <- c()
plot <- layout(plot,
shapes = list(
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.5,
x0 = "1980-01-01", x1 = "1990-01-01",
y0 = 6000, y1 = 8000
)
)
)
plot
Update: It seems that either plotly` orgplot`` changed it's way of handling dates. Instead of passing a string dates, one would need to pass in integers now, i.e. it should be:
x0 = 315532800000, x1 = 631152000000
to get the correct result.

Related

drawing rectangle on plotly R

I would like to draw a rectangle polygon over a plotly plot that uses dates. As a toy example my code is
library(tidyverse)
library(plotly)
df <- tibble(daydate = dmy(c("01-01-2022", "01-02-2022", "15-02-2022", "27-02-2022")),
value = runif(4, 0, 100))
plot_ly(df, x = ~daydate, y = ~value, color = I("#9B4393"), type = 'scatter', mode = 'lines')
I would like to draw a rectangle that spans the dates "01-02-2022" to "15-02-2022", and from 0 to 100. Also, I would like to have the rectangular area grey colour that is quite transparent.
Many thanks
Define a rectangle:
my_rectangle <- list(
type = "rect",
fillcolor = "grey",
line = list(color = "black"),
opacity = 0.3,
x0 = "2022-02-01",
x1 = "2022-02-15",
xref = "x",
y0 = 0,
y1 = 100,
yref = "y"
)
Add it to the plot:
plot_ly(
df,
x = ~daydate,
y = ~value,
color = I("#9B4393"),
type = "scatter",
mode = "lines"
) |>
layout(
shapes = list(
my_rectangle
)
)
The R plotly documentation sets out how you can draw and style rectangles and various other shapes.

Changing the color of a specific ring in a radar chart created in plotly

I have a radar chart created w/ plotly as shown below. Is there a way to have only the ring at 100 colored red, while all other rings remain in their off-gray color?
library(plotly)
fig <- plot_ly(
type = 'scatterpolar',
r = c(135, 75, 110),
theta = c('A','B','C'),
fill = 'toself'
)
fig %>%
layout(
polar = list(
radialaxis = list(
visible = T,
range = c(0, 150)
)
)
)
To my knowledge, there is not an option to use custom formatting on any axis line other than zero, so there may not actually be a "canonical" answer here that you're hoping for.
That being said, adding another trace to your plot might be the best work-around, even if it's a little clunky. Because the added line trace overrides the default categorical axis, some more extensive customization of the angular axis and some math to manually calculate categorical values relative to 360 degree coordinate system (this is normally obscured from the user) is required.
Names <- c('A','B','C')
Values <- c(135, 75, 110)
ThetaValues <- seq(from = 0, to = 360, length.out = length(Values)+1L)[seq_len(length(Values - 1L))]
plot_ly() %>%
add_trace(type = 'scatterpolar',mode = "lines",
r = rep(100,100),
theta = seq(from = 0, to = 360, length.out = 100),
line = list(smoothing = 1,
color = "red",
shape = "spline"),
hoverinfo = "skip",
showlegend = FALSE) %>%
add_trace(type = 'scatterpolar',mode = "markers",
r = Values,
name = "",
theta = ThetaValues,
color = I("blue"),
fill = 'toself') %>%
layout(polar = list(radialaxis = list(visible = T,
range = c(0, 150)),
angularaxis = list(visible = T,
type = "category",
ticktext = Names,
tickvals = ThetaValues)))

R: How to ignore shapes when autoscaling chart's axis in plotly?

I have a plotly chart where the x-axis is date. I have added a rectangle shape to highlight a specified date period. Sometimes this date period will lie within the date range of the data displayed in the chart, in which case there is no problem. The chart appears as normal. However, it is possible that the date period lies outside of the date range in the chart (or it overlaps, with part of it outside the date range of the chart). In this case, when the chart is produced, the x-axis is autoscaled to show the full rectangle as well as the data (or the traces).
This is not what I want. I want it to be autoscaled on the date range of the data (traces) only (as if the rectangle didn't exist), but with the rectangle added in the background of the chart. If the user manually zooms out they should be able to see the full rectangle, including the part that lies outside of the data, but this should not be the default.
See the below code as an example. It is an edited version of the code taken from here: https://plotly.com/r/shapes/#lines
The date range of the data is 1967-2015. The autoscale of the x-axis should be based on this only. However, as one of the rectangles has x coordinates of 1940 and 1945, the x-axis is autoscaled to 1940-2015 in order to display the full rectangle, leaving a large gap between 1940 and 1967 with no data. I want it to be so that if the user manually zooms out to a scale of 1940-2015, this is what they would see, but the autoscale should ignore the rectangle and default to a range of 1967-2015.
library(plotly)
fig <- plot_ly(economics, x = ~date, y = ~uempmed, name = "unemployment")
# add shapes to the layout
fig <- layout(fig, title = 'Highlighting with Rectangles',
shapes = list(
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.3,
x0 = "1940-01-01", x1 = "1945-01-01", xref = "x",
y0 = 0, y1 = 1, yref = "paper"),
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.2,
x0 = "2000-01-01", x1 = "2005-01-01", xref = "x",
y0 = 0, y1 = 1, yref = "paper")))
fig
There does not appear to be anything in the documentation that addresses this issue, and I can't find anything on Google either. But I'd be surprised if I'm the only person who's ever wanted to do this, so I think there must surely be an option.
Does anyone know?
EDIT: I have discovered that I can define the range of the x-axis manually, and can take this to be the minimum and maximum of the x column. However, two drawbacks of this approach. One is that the outer points lie exactly on the boundary, so half of the point or bar (if you have a bar chart) gets cropped out which doesn't look good.
The other potential drawback is when you click the autoscale button it still zooms right out to see the rectangles outside of the range. This may or may not be desirable, but it will be good to know if an alternative method exists.
library(plotly)
fig <- plot_ly(economics, x = ~date, y = ~uempmed, name = "unemployment")
minx <- min(economics$date)
maxx <- max(economics$date)
x_axis <- list(
range = c(minx,maxx)
)
# add shapes to the layout
fig <- layout(fig, xaxis= x_axis, title = 'Highlighting with Rectangles',
shapes = list(
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.3,
x0 = "1940-01-01", x1 = "1945-01-01", xref = "x",
y0 = 0, y1 = 1, yref = "paper"),
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.2,
x0 = "2000-01-01", x1 = "2005-01-01", xref = "x",
y0 = 0, y1 = 1, yref = "paper")),
xaxis = list(range=c(as.Date("1980-01-01"),as.Date("2000-01-01")))
)
fig

Add a line that indicates the average value of an axis in a plotly scatterplot [duplicate]

I'm using the plotly package and I'm trying to add a horizontal line to a graph. Is there any way of doing it using plotly?
It can be done using ggplot2 and the ggplotly function as shown below:
library(plotly)
p <- ggplot() +
geom_hline(yintercept = 4) +
xlim(c(0,10)) +
ylim(c(0,10))
ggplotly(p)
But I can't add this to an existing plotly plot.
Also, the axis of my charts are not fixed, so it would be difficult (but not impossible) to just work out an x and y coordinate system for a horizontal line, but I'd rather just add one automatically.
I've looked into the y0 and dy arguments, but I can't seem to get the code for those to work, either. I'm not quite sure what they do exactly, but I think they're maybe what I'm looking for? I can't find good examples of their usage.
There are two main ways to do this (using either data or 'paper' coordinates). Assuming data coordinates, the easiest current way is via add_segments():
plot_ly() %>%
add_segments(x = 4, xend = 4, y = 0, yend = 10) %>%
add_segments(x = 3, xend = 5, y = 5, yend = 5)
Notice how we've hard coded the extent of these lines in data coordinates; so when zooming and panning the plot, the line will be "clipped" at those values. If you don't want these lines to be clipped, use a line shape with xref/yref set to paper (this puts the graph region on a 0-1 scale, rather than on the x/y data scale):
vline <- function(x = 0, color = "red") {
list(
type = "line",
y0 = 0,
y1 = 1,
yref = "paper",
x0 = x,
x1 = x,
line = list(color = color)
)
}
hline <- function(y = 0, color = "blue") {
list(
type = "line",
x0 = 0,
x1 = 1,
xref = "paper",
y0 = y,
y1 = y,
line = list(color = color)
)
}
plot_ly() %>%
layout(shapes = list(vline(4), hline(5)))
Alternatively, you could add a shape (i.e. line) under layout(). The following example adds a vertical line:
p <- plot_ly(data, x = ~x.data, y = ~y.data, text = ~text.data, type = 'scatter',
mode = 'markers', marker = list(size = ~size.data, opacity= 0.5)) %>%
layout(shapes=list(type='line', x0= 0.2, x1= 0.2, y0=min(allyvalues), y1=max(allyvalues), line=list(dash='dot', width=1)),
title = 'This is the Title',
xaxis = list(title = "X-Axis", showgrid = TRUE),
yaxis = list(title = "Y-Axis", showgrid = TRUE))
p
Building on Carson's nice answer above, here is a convenience function closer to ggplot's geom_vline()
# Add vertical line(s) at position x to plotly plot p
# Additional arguments: color, width (px), dash ('solid','dot', 'dash', etc)
# See https://plotly.com/r/reference/#layout-shapes-items-shape-line
add_vline = function(p, x, ...) {
l_shape = list(
type = "line",
y0 = 0, y1 = 1, yref = "paper", # i.e. y as a proportion of visible region
x0 = x, x1 = x,
line = list(...)
)
p %>% layout(shapes=list(l_shape))
}
To make the function additive the following modifications to the function can be used
add_vline = function(p, x, ...) {
if(!is.null(p$x$layoutAttrs)){
index <- unname(which(sapply(p$x$layoutAttrs, function(x)
!is.null(x$shapes))))
} else {
index <- integer()
}
l_shape = list(
type = "line",
y0 = 0, y1 = 1, yref = "paper", # i.e. y as a proportion of visible region
x0 = x, x1 = x,
line = list(
...
),
layer = "below"
)
if(length(index) > 0){
shapes <- p$x$layoutAttrs[[index]]$shapes
shapes[[length(shapes) + 1]] <- l_shape
p$x$layoutAttrs[[index]]$shapes <- shapes
} else {
p <- plotly::layout(
p = p,
shapes = list(l_shape)
)
}
p
}

Adding a Vertical / Horizontal Reference Line using Plotly

I'm working with a proportional bar chart and I'd like to draw a vertical line at a particular X value. I'd prefer to accomplish this using the plotly package, but it doesn't seem to be easily done.
The solution found at Horizontal/Vertical Line in plotly doesn't seem to get the job done.
I've provided some sample code below that could be used to draw the vertical line at X = 3.
library(plotly)
library(ggplot2)
plot_ly(diamonds[1:1000, ], x = ~x, y = ~cut, color = ~color) %>% add_bars()
I'd appreciate any help in this matter.
I found some information about lines in plotly from Zappos Engineering here. The range -0.5 to 4.5 is because there are five categories in the data provided, each centered on a whole number. The y range creates the line, while the x constant (at 3) keeps the line vertical.
p <- plot_ly(diamonds[1:1000, ], x = ~x, y = ~cut, color = ~color) %>% add_bars()
p <- layout(p, shapes = list(type = "line", fillcolor = "red",
line = list(color = "red"),
opacity = 1,
x0 = 3, x1 = 3, xref = 'x',
y0 = -0.5, y1 = 4.5, yref = 'y'))

Resources