I have a scatter plot with numeric values on both the axis. I want to add two draggable lines, one horizontal and one vertical. Also, I would like to change the color of points in the top-right quadrant formed by the two lines. I could not find any way to do this in R.
My question is similar to Horizontal/Vertical Line in plotly
Two things I want to add is
Ability to drag vertical and horizontal lines
Return values on which the two lines intersect the x and y axis, so that I can use those values as an input for another UI.
My code sample
data <- data.frame(y = sample(0:50, 100, replace = TRUE),
x = round(runif(100),2)
)
plot_ly(data, x = ~x, y = ~y)
(1) You can add draggable lines as follows:
plot_ly(data, x = ~x, y = ~y, type = 'scatter') %>%
layout(shapes = list(
#vertical line
list(type = "line", x0 = 0.4, x1 = 0.4,
y0 = 0, y1 = 1, yref = "paper"),
#horizontal line
list(type = "line", x0 = 0, x1 = 1,
y0 = 30, y1 = 30, xref = "paper"))) %>%
# allow to edit plot by dragging lines
config(edits = list(shapePosition = TRUE))
Similar to https://stackoverflow.com/a/54777145/5840900
(2) In an interactive environment, you can extract the values of the last dragged line with:
# Only within reactive shiny context
newData <- plotly::event_data("plotly_relayout")
Hopefully this is still helpful!
Related
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
I have some data like this:
data <- data.frame(x=runif(500), y=runif(500), z=runif(500))
I want a scatterplot with points colored independently/discretely in each dimension (X, Y, and Z) using RGB values.
This is what I have tried:
Code:
library(dplyr)
library(plotly)
xyz_colors <- rgb(data$x, data$y, data$z)
plot_ly(data = data,
x = ~x, y = ~y, z = ~z,
color= xyz_colors,
type = 'scatter3d',
mode='markers') %>%
layout(scene = list(xaxis = list(title = 'X'),
yaxis = list(title = 'Y'),
zaxis = list(title = 'Z')))
Plot:
RColorBrewer thinks I'm trying to create a continuous scale from 500 intermediate colors:
Warning messages:
1: In RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
2: In RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
What are some correct ways to color the points like this in R with Plotly?
Also, how can one generally assign colors to data points in R with Plotly, individually?
To clarify, I am trying to color each point where the color is of the format "#XXYYZZ" where 'XX' a value between 00 and FF linearly mapped to the value of data$x from 0 to 1. That is, the X dimension determines the amount of red, the Y dimension determines the amount of green, and the Z dimension determines the amount of blue. At 0,0,0 the point should be black and at 1,1,1 the point should be white. The reason for this is to make as easy to visualize the 3D position of the points as possible.
Updated answer after comments:
So, is there no way to color every point separately?
Yes, there is through the power and flexibility of add_traces(). And it's a lot less cumbersome than I first thought.
Just set up an empty plotly figure with some required 3D features:
p <-plot_ly(data = data, type = 'scatter3d', mode='markers')
And apply add_traces() in a loop over each defined color:
for (i in seq_along(xyz_colors)){
p <- p %>% add_trace(x=data$x[i], y=data$y[i], z=data$z[i],
marker = list(color = xyz_colors[i], opacity=0.6, size = 5),
name = xyz_colors[i])
}
And you can easily define single points with a color of your choice like this:
p <- p %>% add_trace(x=0, y=0, z=0,
marker = list(color = rgb(0, 0, 0), opacity=0.8, size = 20),
name = xyz_colors[i])
Plot:
Complete code:
library(dplyr)
library(plotly)
# data and colors
data <- data.frame(x=runif(500), y=runif(500), z=runif(500))
xyz_colors <- rgb(data$x, data$y, data$z)
# empty 3D plot
p <-plot_ly(data = data, type = 'scatter3d', mode='markers') %>%
layout(scene = list(xaxis = list(title = 'X'),
yaxis = list(title = 'Y'),
zaxis = list(title = 'Z')))
# one trace per color
for (i in seq_along(xyz_colors)){
p <- p %>% add_trace(x=data$x[i], y=data$y[i], z=data$z[i],
marker = list(color = xyz_colors[i], opacity=0.6, size = 5),
name = xyz_colors[i])
}
# Your favorite data point with your favorite color
p <- p %>% add_trace(x=0, y=0, z=0,
marker = list(color = rgb(0, 0, 0), opacity=0.8, size = 20),
name = xyz_colors[i])
p
Original answer:
In 3D plots you can use the same color for all of the points, discern different clusters or categories from each other using different colors, or you use individual colors for each point to illustrate a fourth value (or fourth dimension if you like, as described here) in your dataset. All these approaches are, as you put it, examples of '[...] correct ways to color the points [...]'. Have a look below and see if this suits your needs. I've included fourthVal <- data$x+data$y+data$z as an example for an extra dimension. What you end up using will depend entirely on your dataset and what you'd like to illustrate.
Code:
library(dplyr)
library(plotly)
data <- data.frame(x=runif(500), y=runif(500), z=runif(500))
xyz_colors <- rgb(data$x, data$y, data$z)
fourthVal <- data$x+data$y+data$z
plot_ly(data = data,
x = ~x, y = ~y, z = ~z,
color= fourthVal,
type = 'scatter3d',
mode='markers') %>%
layout(scene = list(xaxis = list(title = 'X'),
yaxis = list(title = 'Y'),
zaxis = list(title = 'Z')))
Plot:
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
}
I'm trying to use R plotly's bar type plot to generate a plot with horizontally laid out boxes and add to that a horizontal line which is in their background (rather than goes on top of them). In addition, I would like the line to extend symmetrically one box unit in each direction.
Here's what I'm doing:
plot.df <- data.frame(x = paste0("LONG NAME ",1:6),y = 0.2,width=0.75,group = c("A","B","B","B","C","A"),stringsAsFactors = F)
plot.df$group <- factor(plot.df$group)
plotly::plot_ly(plot.df) %>%
plotly::add_trace(x=~x,y=~y/2,type='scatter',mode='lines',line=list(color='black'),showlegend=F) %>%
plotly::add_bars(x=~x,y=~y,width=~width,color=~group) %>%
plotly::layout(xaxis=list(title=NA,zeroline=F,tickangle=45),yaxis=list(title=NA,zeroline=F,showgrid=F,range=c(0,1),showticklabels=F))
Which gives:
My questions are:
How to extend the the line in both directions
How to put the line in the background so it does not run over the boxes
I specified plot.df$y as 0.2 but the yaxis range to be c(0,1) so that the boxes don't look like long bars. But then the legend appears too high. Any better way to get square boxes with the legend appearing lower than it currently is?
For the horizontal line you can see Horizontal/Vertical Line in plotly
with
layout(legend =list(x = 1 ,y =0 ))
you can solve the legend problem
I could not solve your second point (put the bar in the background). I hope it helps:
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(plot.df) %>%
add_bars(x=~x,y=~y,width=~width,color=~group, hoverinfo = "text") %>%
layout(shapes = list(hline(0.1)))%>%
layout(legend =list(x = 1 ,y =0 ))%>%
layout(xaxis=list(title=NA,zeroline=F,tickangle=45),yaxis=list(title=NA,zeroline=F,showgrid=F,range=c(0,1),showticklabels=F))
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'))