drawing rectangle on plotly R - 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.

Related

R Plotly how to link text to bubbles in order to remove it when click on legend

With plotly on R, I have a bubble scatter plot and I want to add black text on each bubble. I also have my bubbles colored following a column of my data frame (Type: Yes or No). On my legend, when I click on "Yes" it will remove the bubbles linked to "Yes" but the text is staying, even if I added a 'legendgroup=~Type' in both traces.
How can I remove the text linked to the bubble when the user click on the legend ?
It is possible but only if I set 'color = ~Type' in my text trace, but I want to keep the text in black.
Example :
df <- data.frame(Projet=c("A", "B", "C"), x=c(0.2, 0.4, 0.6), y=c(0.6, 0.5, 0.1), Size=c(2,5,8), Type=c("Yes", "Yes", "No"))
fig <- plot_ly(df, x = ~ x, y = ~y) %>%
add_trace(color = ~Type, size = ~Size,
type = 'scatter', mode = 'markers',
sizes = c(20, 80),
marker = list(symbol = 'circle', sizemode = 'diameter',line = list(width = 2, color = 'gray60')),
hovertext = ~paste('Projet:', Projet, '<br>Size (kE):', Size),
hoverinfo="text", legendgroup=~Type) %>%
add_trace(type="scatter", mode="text", text=~Projet, showlegend=F, legendgroup=~Type)
fig
Which gives :
And if I click on "Yes" in the legend :
=> I want to remove the "A" and "B" text in this case
Thanks !
When I looked at your Plotly object, you assigned three group names to the parameter legendgroup. The reason it worked in your initial call add_trace is that Plotly will split a trace by color. In your call for text, everything is the same color, so it didn't automatically split the trace.
In your call for the text, you need to add split to split the trace.
Check it out
library(plotly)
df <- data.frame(Project = c("A", "B", "C"), x = c(0.2, 0.4, 0.6),
y = c(0.6, 0.5, 0.1), Size = c(2,5,8),
Type = c("Yes", "Yes", "No"))
fig <- plot_ly(df, x = ~ x, y = ~y) %>%
add_trace(color = ~Type, size = ~Size,
type = 'scatter', mode = 'markers',
sizes = c(20, 80),
marker = list(symbol = 'circle', sizemode = 'diameter',
line = list(width = 2, color = 'gray60')),
hovertext = ~paste('Project:', Project, '<br>Size (kE):', Size),
hoverinfo = "text", legendgroup = ~Type) %>%
add_trace(type = "scatter", mode = "text", text = ~Project,
showlegend = F, legendgroup = ~Type, split = ~Type) # <- I'm new!
fig

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

Plot multiple legends in R-plotly

I have 5 continuous variables that I'd like to graph together in R plotly.
I wrote the following code and got the plot to run as expected, but I cannot figure out how to deal with the legends. As is, the color legend appears, but the size legend does not.
I would like to plot both legends and control their locations within the plot. Suggestions from a similar post Adding color and bubble size legend in R plotly do not solve the problem.
Here's the code and sample data:
x<-sample(30)
y<-sample(30)
z<-sample(30)
c<-sample(30)
s<-sample(30)
fig <- plot_ly (x = x, y = y, z = z, color = c,
colors = c("#440154FF", "#1F968BFF", "#FDE725FF"), size = s,
marker = list(symbol = 'circle', sizemode = 'diameter'), sizes = c(1, 30))
fig <- fig %>% add_markers()
fig <- fig %>% layout(scene = list(xaxis = list (title = 'X'),
yaxis = list(title = 'Y'),
zaxis = list(title = 'Z'),
annotations = list(x = 1.05, y =1.02,
text = 'Gradient title',
xref = 'paper', yref = 'paper',
showarrow=FALSE, showlegend=TRUE)))
fig
It's been a while since this question was asked, but I have an answer. Initially, I tried to make the legend a subplot, but the legend from the 3D markers is offset from the plot-as-a-legend of bubble sizes. To fix that issue, I created an image of the bubbles and added it to the original plot as an image.
Using the information from fig in your original code, I created another figure (the bubbles and sizes).
figB <- plot_ly(x = 1, y = seq(30, 5, by = -5),
size = seq(30, 5, by = -5),
sizes = c(1, 30),
type = "scatter",
mode = "markers",
color = seq(30, 5, by = -5),
colors = c("#440154FF", "#1F968BFF", "#FDE725FF"),
marker = list(sizeref = 0.1,
sizemode = "area"),
height = 275, width = 100) %>%
layout(
xaxis = list(zeroline = F, showline = F, showticklabels = F, showgrid = F),
yaxis = list(showgrid = F, side = "right")) %>% # numbers on right (as fig legend)
hide_colorbar()
figB
I used three different libraries for this next part: htmlwidgets, webshot, and magick.
# create temp files
tmp <- tempfile(fileext = ".html") # plotly to html
tmp2 <- tempfile(fileext = ".png") # html to png
# create html
htmlwidgets::saveWidget(figB, tmp, background = "transparent")
# create png
webshot::webshot(tmp, tmp2, zoom = 2, vwidth = 150, vheight = 275) # to get great res
# make the png an object
itsBack <- magick::image_read(tmp2)
# check the amount of white space
magick::image_border(itsBack, "gray") # not too much white space; good res
unlink(tmp) # remove tempfile connection
unlink(tmp2)
For this last step, I copied the code from your original figure. The image needs to be added to layout. I removed code that didn't impact the figure, as well.
# set up placement of image below the initial legend
imgr = list(
source = raster2uri(as.raster(itsBack)),
xref = "paper",
yref = "paper",
y = .5, # paper domain is 0 to 1, this puts the top in the middle
x = .95, # almost all the way right
sizex = .45, # scale image down (0-1)
sizey = .45, # scale image down (0-1)
opacity = 1,
layer = "above")
# Rebuild fig without the initial legend - then add imgr to the legend
fig <- plot_ly (x = x, y = y, z = z, color = c,
colors = c("#440154FF", "#1F968BFF", "#FDE725FF"),
size = s,
marker = list(symbol = 'circle',
sizemode = 'diameter'),
sizes = c(1, 30))
fig <- fig %>% layout(
scene = list(xaxis = list(title = 'X'),
yaxis = list(title = 'Y'),
zaxis = list(title = 'Z')),
images = imgr) # adding bubbles here
fig
Depending on what you're doing with the graph, the placement and scaling may need to be adjusted. While plotly objects scale dynamically, the png won't be nearly as dynamic-friendly. The image is scaled down to 45% of its original size, so you have a lot of room to grow, but you may have to adjust those parameters (sizex and sizey). If you rescale your viewer window, you may also need to refresh the view. (Use the refresh icon in the Viewer pane.)

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
}

Partially shaded background for ggplotly-object

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.

Resources