common axis subplots with plot.ly for R - r

I am attempting to use subplots with the plot.ly R library for interactive online charting. I can successfully create a subplot, however am struggling to only have a single y-axis that is common to both charts.
The plot.ly website does provide an example for a common x-axis, however this is done slightly differently using and additional trace rather than the group option that is provided within the plot_ly() function.
example code:
library(data.table)
library(plotly)
dt <- data.table(x = c("A","B","C","D","A","B","C","D"),
y = c(12,4,3,9,5,10,3,7),
group = factor(c(rep("G1",4),rep("G2",4))))
dt$id <- as.integer(dt$group)
xx <- xaxis_standard
yy <- yaxis_standard
p <- plot_ly(dt, x=x, y=y, group = group, xaxis = paste0("x",id))
p <- layout(p, yaxis = list(range = c(0, max(y))))
p <- subplot(p, margin = 0.05)
p <- layout(p,showlegend = F, yaxis = list(anchor = 'x1'))
p
This image shows what results when I execute the code.
What I would like to have is the same chart, however without the y-axis on the right hand subplot.

Subplots are on separate axes labeled xaxis2, yaxis2, etc. Those axes are also arguments to layout().
p <- layout(p, showlegend = F, yaxis = list(anchor = 'x1'),
yaxis2 = list(showticklabels = F))
p

Related

how to change order of R plotly hover labels with "x unified" mode

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"

R Plotly linked subplot with percentage histogram and categories coloured

The Background
I am using the plotly API in R to create two linked plots. The first is a scatter plot and the second is a bar chart that should show the percentage of data belonging to each category, in the current selection. I can't make the percentages behave as expected.
The problem
The plots render correctly and the interactive selection works fine. When I select a set of data points in the top scatter plot, I would like to see the percentage of that selection that belongs to each category. Instead what I see is the percentage of points in that selection in that category that belong to that category, in other words always 100%. I guess this is because I set color = ~c which applies a grouping to the category.
The Example
Here is a reproducible example to follow. First create some dummy data.
library(plotly)
n = 1000
make_axis = function(n) c(rnorm(n, -1, 1), rnorm(n, 2, 0.25))
data = data.frame(
x = make_axis(n),
y = make_axis(n),
c = rep(c("A", "B"), each = n)
)
Create a sharedData object and supply it to plot_ly() for the base plot.
shared_data = data %>%
highlight_key()
baseplot = plot_ly(shared_data)
Make the individual panels.
points = baseplot %>%
add_markers(x = ~x, y = ~y, color = ~c)
bars = baseplot %>%
add_histogram(x = ~c, color = ~c, histnorm = "percent", showlegend = FALSE) %>%
layout(barmode = "group")
And put them together in a linked subplot with selection and highlighting.
subplot(points, bars) %>%
layout(dragmode = "select") %>%
highlight("plotly_selected")
Here is a screenshot of this to illustrate the problem.
An Aside
Incidentally when I set histnorm = "" in add_histogram() then I get closer to the expected behaviour but I do want percentages and not counts. When I remove color = ~c then I get closer to the expected behaviour but I do want the consistent colour scheme.
What have I tried
I have tried manually supplying the colours but then some of the linked selection breaks. I have tried creating a separate summarised data set from the sharedData object first and then plotting that but again this breaks the linkage between the plots.
If anyone has any clues as to how to solve this I would be very grateful.
To me it seems the behaviour you are looking for isn't implemented in plotly.
Please see schema(): object ► traces ► histogram ► attributes ► histnorm ► description
However, here is the closest I was able to achive via add_bars and perprocessing the data (Sorry for adding data.table, you will be able to do the same in base R, just personal preference):
library(plotly)
library(data.table)
n = 1000
make_axis = function(n) c(rnorm(n, -1, 1), rnorm(n, 2, 0.25))
DT = data.table(
x = make_axis(n),
y = make_axis(n),
c = rep(c("A", "B"), each = n)
)
DT[, grp_percent := rep(100/.N, .N), by = "c"]
shared_data = DT %>%
highlight_key()
baseplot = plot_ly(shared_data)
# Make the individual panels.
points = baseplot %>%
add_markers(x = ~x, y = ~y, color = ~c)
bars = baseplot %>%
add_bars(x = ~c, y = ~grp_percent, color = ~c, showlegend = FALSE) %>%
layout(barmode = "group")
subplot(points, bars) %>%
layout(dragmode = "select") %>%
highlight("plotly_selected")
Unfortunately, the resulting hoverinfo isn't really desirable.

One slider to control graphs in single subplot in R using plotly

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

Adding color and bubble size legend in R plotly

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

How to plot horizontal lines between y-axis ticks in plotly?

Here is some example code to illustrate my issue.
library(plotly)
p <- plot_ly(x = mtcars$mpg, y = seq_along(rownames(mtcars)), text=rownames(mtcars),
type = 'scatter', mode = 'markers')
ax <- list(
title = "",
ticktext = rownames(mtcars),
tickvals = seq(1,32)
)
line <- list(
type = "line",
line = list(color = "pink"),
xref = "x",
yref = "y"
layer = 'below'
)
lines <- list()
for (i in seq_along(rownames(mtcars))) {
line[["x0"]] <- mtcars$mpg[i] - 1
line[["x1"]] <- mtcars$mpg[i] + 1
line[c("y0", "y1")] <- i
lines <- c(lines, list(line))
}
p <- layout(p, title = 'Highlighting with Lines', shapes = lines, yaxis=ax)
p
I would like to add horizontal lines through the plot to separate each y-axis label. I would prefer the line split the labels as well as the graph, but splitting just the graph would suffice. I have looked extensively through the plotly reference but have yet to find anything that appears to help. I was told that there might be some sort of solution through some custom JS in the y-axisof the layout section, but am unsure on how I would go about this / am not very JS savvy.
I was able to accomplish this by adding more lines to the lines list. Under the for loop shown above, if you add the code:
for (i in seq_along(rownames(mtcars))) {
line[["x0"]] <- 10
line[["x1"]] <- 35
line[c("y0", "y1")] <- i-.5
lines <- c(lines, list(line))
}
It will place a separating line in between each of the data lines.

Resources