I'm using R/plotly to create a stacked filled area plot summarizing several accounts opened at different times, whose balances sometimes go to zero. Here's a simplified example using three accounts ("X", "Y", and "Z"):
df <- data.frame(cbind(
year <- c("2022-01-01","2022-02-01","2022-03-01","2022-04-01","2022-05-01","2022-06-01","2022-07-01","2022-08-01","2022-09-01","2022-10-01","2022-11-01","2022-12-01"),
x <- c(NA,NA,NA,1,2,3,2,3,4,5,3,4),
y <- c(1,2,2,1,2,4,3,2,NA,NA,NA,NA),
z <- c(NA,NA,NA,1,2,5,6,5,5,NA,NA,NA)
)) %>%
setNames(c("DATE","X","Y","Z")) %>%
mutate(DATE = as.Date(DATE, format = "%Y-%m-%d")) %>%
mutate(X = as.numeric(X), Y = as.numeric(Y), Z = as.numeric(Z)) %>%
pivot_longer(., -DATE, names_to = "LEGEND", values_to = "QUANTITY") %>%
mutate(LEGEND = factor(LEGEND, levels = c("X","Y","Z")))
When I create a graph using plotly, I prefer to use the hovermode = "x"
parameter to compare all quantities at a given time at once. However, when hovering over periods when an account is empty (i.e., QUANTITY = NA), I still get a tooltip box with hoverinfo showing y = 0
. With lots of accounts, this can clutter things up pretty quickly.
dfplot <- plot_ly(data = df, x = ~DATE, y = ~QUANTITY, color = ~LEGEND, type = "scatter", mode = "none", stackgroup = "one", fill = "tonexty") %>%
layout(
autosize = TRUE,
yaxis = list(range = c(0,15)),
hovermode = "x",
showlegend = TRUE
)
dfplot
X and Z are given values of "0", despite being NA
Is there a way to get rid of these extra tooltips? Or to conditionally skip hoverinfo
if y = 0 or NA?
I've been able to skip hoverinfo
for entire traces, and I have been able to get the behavior I want with simple scatter
traces (y = 0 is ignored in these situations). I'm not sure if this is something that is specific to stacked filled area plots or not. Any help would be greatly appreciated!
As I stated in my comment. I thought you meant the Y
trace, not y
for each trace. Here is my update.
plot_ly(data = df, x = ~DATE, y = ~QUANTITY, color = ~LEGEND, type = "scatter",
mode = "none", stackgroup = "one", fill = "tonexty") %>%
layout(autosize = TRUE, yaxis = list(range = c(0, 15)), hovermode = "x",
showlegend = TRUE) %>%
htmlwidgets::onRender("function(el){
el.on('plotly_hover', function(d) {
out = []; /* traces to drop from hover */
keep = []; /* traces to keep in the hover */
fixer = [2, 1, 0]; /* data order Z, X, Y; trace order X, Y, Z */
for(i = 0; i < 3; i++) { /* which traces stay? which go? */
j = fixer[i];
if(d.points[i].y == 0) {out.push(j);}
else {keep.push(j);}
}
if(out.length != 0) { /* drop from hover */
Plotly.restyle(el.id, {hoverinfo: 'none', hovertemplate: null}, out);
}
if(keep.length > 0 && keep.length < 3) { /* keep */
Plotly.restyle(el.id, {hoverinfo: 'all', hovertemplate: '%{y}'}, keep);
} else if(keep.length == 3) { /* keep */
Plotly.restyle(el.id, {hoverinfo: 'all'}, keep);
}
})
}")
I can drop the 'Y' trace from the x-unified hover, but it also drops the x-axis. I tried many different ways to get the x-axis back, but I haven't found a way that works.
This uses the library htmlwidgets
. However, since it's only for one function, I just appended it to the function. (I didn't call the library.)
I've piped the function from the graph but did not change any of your code.
Here's what you can do to drop the Y trace where there is no or a 0 value.
plot_ly(data = df, x = ~DATE, y = ~QUANTITY, color = ~LEGEND, type = "scatter",
mode = "none", stackgroup = "one", fill = "tonexty") %>%
layout(autosize = TRUE, yaxis = list(range = c(0, 15)), hovermode = "x",
showlegend = TRUE) %>%
htmlwidgets::onRender("function(el){
el.on('plotly_hover', function(d) {
if(d.points[1].y == 0) {
hinfo = {hoverinfo: 'none'};
} else {
hinfo = {hoverinfo: 'all'};
}
Plotly.restyle(el.id, hinfo, 1);
Plotly.restyle(el.id, {hoverinfo: 'all', hovertemplate: '%{y}'}, [0, 2]);
})
}")
You had assigned the graph to dfplot
. You can always just pipe the function I provided to the plot after you named it like this (it does the same thing).
dfplot %>%
htmlwidgets::onRender("function(el){
el.on('plotly_hover', function(d) {
if(d.points[1].y == 0) {
hinfo = {hoverinfo: 'none'};
} else {
hinfo = {hoverinfo: 'all'};
}
Plotly.restyle(el.id, hinfo, 1);
Plotly.restyle(el.id, {hoverinfo: 'all', hovertemplate: '%{y}'}, [0, 2]);
})
}")
By the way, I noticed a few things in your data creation that might make things a bit easier for you. If you already know this, just ignore me!
Odd things happen when you use <-
inside of a function. When creating an object, use either; assignment inside a function use =
.
In my example, note that cbind
and setNames
weren't used. Additionally, X, Y, and Z were transformed to numeric in one call. .,
isn't in pivot_longer
. Lastly, the last mutate
was replaced with the argument names_transform
in pivot_longer
.
df1 <- data.frame(
DATE = c("2022-01-01","2022-02-01","2022-03-01","2022-04-01","2022-05-01",
"2022-06-01","2022-07-01","2022-08-01","2022-09-01","2022-10-01",
"2022-11-01","2022-12-01"),
X = c(NA,NA,NA,1,2,3,2,3,4,5,3,4),
Y = c(1,2,2,1,2,4,3,2,NA,NA,NA,NA),
Z = c(NA,NA,NA,1,2,5,6,5,5,NA,NA,NA)) %>%
mutate(DATE = as.Date(DATE, format = "%Y-%m-%d"),
across(X:Z, as.numeric)) %>%
pivot_longer(-DATE, names_to = "LEGEND", values_to = "QUANTITY",
names_transform = list(LEGEND = as.factor))
all.equal(df, df1) # [1] TRUE