Related
I have this simple graphic I am making with plotly.
library(plotly)
fig <- plot_ly(x = ~rnorm(50), type = "histogram")
fig
This is the output.
But my goal is this - no 0 on the y-axis.
This could be achieved by setting the tickvals and ticktext manually via layout options. Note that you have to se tickmode="array". One drawback is that this adds some additional space on the left and the top so that you have to additionally set the margins manually.
library(plotly)
set.seed(42)
x <- rnorm(50)
fig <- plot_ly(x = ~x, type = "histogram")
fig %>%
layout(yaxis = list(
tickvals = as.list(seq(0, 20, 2)),
ticktext = as.list(c("", seq(2, 20, 2))),
tickmode = "array"
), margin = list(l = 2, t = 2))
I'm looking to see if there's a way to change the order of the hoverlabels when using hovermode = "x unified" in the newest version of the R package of plotly (4.9.3). Alternatively, is it possible to revert back to the way the old version of plotly displayed the hoverlabels while still using the current version of the plotly package? From a data visualization perspective, the old way is much clearer in my opinion.
I've included a minimum reproducible example below. When I run this using plotly v4.9.2.1, I get the result shown in Figure A and when I run it in plotly v4.9.3, I get the result shown in Figure B. The benefits to Figure A over Figure B are:
Figure A labels are in descending order relative to the data on each line at the time specified. Also this is reactive to the time period, so if one line moves above another at a different time period, the relative positioning of the label also moves to reflect the ordering of the data. You can see in Figure B that the dark green (y1) line has the lowest value (66) yet it is shown at the top of the hoverlabel box. In figure B, the y1 label is at the bottom.
Figure A labels are attached to the individual lines, so its easier to see the hovertext as it applies to the line in question
Figure A:
Figure B:
Code:
library(plotly)
library(tidyr)
df <- data.frame(Date = seq(as.Date("2018-01-01"),
as.Date("2021-01-01"),
by = "months"),
stringsAsFactors = F)
df$y1 <- seq(0, 100, length.out = nrow(df))
df$y2 <- seq(0, 600, length.out = nrow(df))
df$y3 <- seq(0, 300, length.out = nrow(df))
df$y4 <- seq(0, 200, length.out = nrow(df))
df <- df %>%
pivot_longer(cols = -Date,
names_to = "yname",
values_to = ) %>%
arrange(yname, Date)
mycols <- c("#006633", "#70AD47", "#1F4E78", "#2F75B5", "#C65911", "#EF8C4F",
"#C00000", "#FF8B8B", "#7030A0", "#9966FF")
mycols <- mycols[1:length(unique(df$yname))]
p <- plot_ly()
p <- p %>%
add_trace(data = df,
x = ~Date,
y = ~value,
text = ~yname,
hovertemplate = paste('<b>%{text}</b>',
'<br>%{x}',
'<br>%{y}',
'<extra></extra>'),
color = ~yname, colors = mycols,
name = ~yname, yaxis = "y",
type = "scatter", mode = "lines",
showlegend = T)
p <- p %>%
layout(hovermode = "x unified",
legend = list(x = 1.12, y = .5, xanchor = "left"),
yaxis = list(fixedrange = T),
xaxis = list(title = "",
fixedrange = T,
hoverformat = "%b %d, %Y"),
showlegend = T)
p
Two answers:
the ordering of traces in the unified hoverlabel is always the same, regardless of the relative Y values of the traces. The order is the same as in the legend, so it will follow the ordering of the colors.
You can revert to the previous behavior with hovermode = "x" rather than hovermode = "x unified"
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.)
I'm looking for certain fix with range selector in plotly using R.
I have two plots visualized via a single subplot using Plotly in R. Now, I need to add a Range Slider/Selector to the complete plot, so that changing it modifies both my plots.
Is it possible via Plotly? (using R only)
This functionality is similar to Dygraphs synchronize feature(https://rstudio.github.io/dygraphs/gallery-synchronization.html).
I'd recommend using subplots option shareX = TRUE:
Please check the following example:
library(plotly)
DF1 <- data.frame(x=1:100, y=runif(100)+ seq(0, 1, length.out = 100))
DF2 <- data.frame(x=1:100, y=runif(100)+ seq(0, 2, length.out = 100))
p1 <- plot_ly(DF1, x = ~x, y = ~y, type = "scatter", mode = "lines+markers")
p2 <- plot_ly(DF2, x = ~x, y = ~y, type = "scatter", mode = "lines+markers")
p <- subplot(p1, p2, nrows = 2, shareX = TRUE)
p
Probably an easy one.
I have an xy dataset I'd like to plot using R's plotly. Here are the data:
set.seed(1)
df <- data.frame(x=1:10,y=runif(10,1,10),group=c(rep("A",9),"B"),group.size=as.integer(runif(10,1,10)))
I'd like to color the data by df$group and have the size of the points follow df$group.size (i.e., a bubble plot). In addition, I'd like to have both legends added.
This is my naive attempt:
require(plotly)
require(dplyr)
main.plot <-
plot_ly(type='scatter',mode="markers",color=~df$group,x=~df$x,y=~df$y,size=~df$group.size,marker=list(sizeref=0.1,sizemode="area",opacity=0.5),data=df,showlegend=T) %>%
layout(title="Title",xaxis=list(title="X",zeroline=F),yaxis=list(title="Y",zeroline=F))
which comes out as:
and unfortunately messes up the legend, at least how I want it to be: a point for each group having the same size but different colors.
Then to add a legend for the group.size I followed this, also helped by aocall's answer:
legend.plot <- plot_ly() %>% add_markers(x = 1, y = unique(df$group.size),
size = unique(df$group.size),
showlegend = T,
marker = list(sizeref=0.1,sizemode="area")) %>%
layout(title="TITLE",xaxis = list(zeroline=F,showline=F,showticklabels=F,showgrid=F),
yaxis=list(showgrid=F))
which comes out as:
Here my problem is that the legend is including values that do not exist in my data.
then I combine them using subplot:
subplot(legend.plot, main.plot, widths = c(0.1, 0.9))
I get this:
where the legend title is eliminated
So I'd be helpful for some help.
Based on the updated request:
Note the changes in legend.plot (mapping values to a sequence of integers, then manually changing the axis tick text), and the use of annotations to get a legend title. As explained in this answer, only one title may be used, regardless of how many subplots are used.
The circle on the plot legend seems to correspond to the minimum point size of each trace. Thus, I've added a point at (12, 12), and restricted the range of the axes to ensure it isn't shown.
titleX and titleY control the display of axis labels, as explained here.
set.seed(1)
df <- data.frame(x=1:10,y=runif(10,1,10),group=c(rep("A",9),"B"),group.size=as.integer(runif(10,1,10)))
require(plotly)
require(dplyr)
## Take unique values before adding dummy value
unique_vals <- unique(df$group.size)
df <- rbind(c(12, 12, "B", 1), df)
df[c(1, 2, 4)] <- lapply(df[c(1, 2, 4)], as.numeric)
main.plot <-
plot_ly(type='scatter',
mode="markers",
color=~df$group,
x=~df$x,
y=~df$y,
size=~df$group.size,
marker=list(
sizeref=0.1,
sizemode="area",
opacity=0.5),
data=df,
showlegend=T) %>%
layout(title="Title",
xaxis=list(title="X",zeroline=F, range=c(0, 11)),
yaxis=list(title="Y",zeroline=F, range=c(0, 11)))
legend.plot <- plot_ly() %>%
add_markers(x = 1,
y = seq_len(length(unique_vals)),
size = sort(unique_vals),
showlegend = F,
marker = list(sizeref=0.1,sizemode="area")) %>%
layout(
annotations = list(
list(x = 0.2,
y = 1,
text = "LEGEND TITLE",
showarrow = F,
xref='paper',
yref='paper')),
xaxis = list(
zeroline=F,
showline=F,
showticklabels=F,
showgrid=F),
yaxis=list(
showgrid=F,
tickmode = "array",
tickvals = seq_len(length(unique_vals)),
ticktext = sort(unique_vals)))
subplot(legend.plot, main.plot, widths = c(0.1, 0.9),
titleX=TRUE, titleY=TRUE)
Firstly, you are only passing in the unique values to the legend. If you pass in all possible values (ie, seq(min(x), max(x), by=1), or in this case seq_len(max(x))) the legend will show the full range.
Secondly, sizeref and sizemode in the marker argument alter the way that point size is calculated. The following example should produce a more consistent plot:
set.seed(1)
df <- data.frame(x=1:10,y=runif(10,1,10),group=c(rep("A",9),"B"),group.size=as.integer(runif(10,1,10)))
require(plotly)
require(dplyr)
a <- plot_ly(type='scatter',mode="markers",
color=~df$group,
x=~df$x,
y=~df$y,
size=df$group.size,
marker = list(sizeref=0.1, sizemode="area"),
data=df,
showlegend=F) %>%
layout(title="Title",
xaxis=list(title="X",zeroline=F),
yaxis=list(title="Y",zeroline=F))
b <- plot_ly() %>% add_markers(x = 1, y = seq_len(max(df$group.size)),
size = seq_len(max(df$group.size)),
showlegend = F,
marker = list(sizeref=0.1, sizemode="area")) %>%
layout(
xaxis = list(zeroline=F,showline=F,showticklabels=F,showgrid=F),
yaxis=list(showgrid=F))
subplot(b, a, widths = c(0.1, 0.9))