5

I have a ggplot barplot with an errorbar, which I'm putting through ggplotly. My preference is to only show the top of the errorbar. In ggplot2, I simply specify the y_min of the errorbar to be the value of the bar, and plot the errorbar first, so that the bottom whisker is hidden behind the geom_bar. However, once I run the plot through ggplotly, the errorbar is shown on the top. Is there a way to not show the bottom whisker? I searched for ways to change the order of the 2 traces, or options for "top-only" errorbars, but couldn't find anything.

library(dplyr) library(ggplot2) library(plotly) df <- data.frame(y = c(5, 10), group = c("a", "b")) p <- ggplot(df) + geom_errorbar(aes(x = group, ymin = y, ymax = y + 10), linewidth = 0.1, width = 0.7) + geom_bar(aes(x = group, y = y), stat = "identity", fill = "lightgrey") # only top whisker is shown p # both top and bottom are showing ggplotly(p) 

2 Answers 2

5

Presumably there is a way to hack the plotly object to move the error bar so that it is underneath the bars, though a simpler solution might be to draw only the objects you want visible. You can do this by drawing a zero-height errorbar at ymax and a segment between y and ymax. That way the layer order doesn't matter.

p <- ggplot(df, aes(x = group, ymax = y + 10)) + geom_errorbar(aes(ymin = y + 10, ymax = y + 10), linewidth = 0.1, width = 0.7) + geom_linerange(aes(ymin = y), linewidth = 0.5) + geom_col(aes(x = group, y = y), fill = "lightgrey") ggplotly(p) 

enter image description here

It's important to point out that the vertical line segments in either case add no information to your plot, and one could argue that having just the zero-height errorbar as a horizontal line floating above the bar might be preferable to data visualization purists.

As a footnote, geom_col() superseded geom_bar(stat = "identity") about 8 years ago.

Sign up to request clarification or add additional context in comments.

Comments

2

Allan's workaround is the best you get, but let me try to explain. Even using plotly natively, we cannot get the effect you are after, since error bars are always drawn last. This is because error_y traces are created with WebGL engine and those traces are always drawn after the regular scatter traces, regardless of the order they were added into the plot.

You can see it here; even though we are adding the bars after adding the error-y, they are plotted first and hence the error-y's cap is visible.

library(plotly) df <- data.frame(y = c(5, 10), group = c("a", "b")) error_length <- 10 plot_ly() %>% add_trace( x = df$group, y = df$y + error_length/2, type = "scatter", mode = "markers", marker = list(color = "transparent"), error_y = list( type = "constant", value = error_length/2, color = "black", thickness = 0.4, width = 20), name = "Positive Errors", showlegend = FALSE) %>% add_trace(inherit = FALSE, type = "bar", x = df$group, y = df$y, color = I("lightgrey")) 

The workaround in plotly is to draw shapes instead of a bar plot (shapes are also drawn with WebGL):

fig <- plot_ly() %>% add_trace( x = as.integer(as.factor(df$group)), y = df$y + error_length/2, type = "scatter", mode = "markers", marker = list(color = "transparent"), error_y = list(type = "constant", value = error_length/2, color = "black", thickness = 0.4, width = 20), name = "Positive Errors", showlegend = FALSE) %>% layout(xaxis = list(range = as.integer(as.factor(df$group)) + c(-0.5, 0.5), tickvals = seq_along(df), ticktext = df$group), yaxis = list(zeroline=FALSE)) bar <- list(type = "rect", fillcolor = "lightgray", line = list(color = "lightgray"), xref = "x", yref = "y") bars <- list() bar_width <- 0.3 for (i in seq_along(df)) { bar[["x0"]] <- i - bar_width bar[["x1"]] <- i + bar_width bar[["y0"]] <- 0 bar[["y1"]] <- df$y[[i]] bars <- c(bars, list(bar)) } layout(fig, shapes = bars) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.