2

I would like to create a barplot with a horizontal line in plotly R. There is an option to use add_segments like here. Unfortunately it doesn't display the full width of the graph and cuts of at the centers of the bars. Here is a simple reproducible example:

library(plotly)
library(dplyr)

iris %>%
  group_by(Species) %>%
  summarise(sepal_length_tot = sum(Sepal.Length)) %>%
  plot_ly() %>%
  add_trace(mode = "bars", x = ~Species, y = ~sepal_length_tot, color = "Species") %>%
  add_segments(x = "setosa", xend = "virginica", y = 200, yend = 200, color = "line")

Created on 2025-08-22 with reprex v2.1.0

As you can see it is not using the full width of the graph. So I was wondering how we can add a horizontal line to a barplot in plotly R and display it in the legend?

1

2 Answers 2

4

The problem occurs, because plotly uses a categorical x-axis for the 3 bars, thus adding a line trace has to follow the 3 category values. Therefore, the line can not extend over the xaxis factor values. It becomes more clear, if you limit the bar widths and look into plotly_json

out1

You could just add symbols / annotations but they will not appear as trace and thus have no legend (e.g. add layout(annotations = list(x = -0.5, y = 200,showarrow = TRUE, arrowhead = 1,ax = 1100, ay = 0))).

What works however is to use a second continues xaxis xaxis2 and overlay it on top of the categorical one. Then we add a trace on the second axis and it will appear within the legend and cover the whole plot width. I used the trick explained here

library(plotly)
library(dplyr)

iris |>
  summarise(sepal_length_tot = sum(Sepal.Length), .by = Species) |>
  plot_ly() |>
  add_trace(type = "bar", x = ~Species, y = ~sepal_length_tot, color = "Species")|>
  layout(xaxis2 = list(overlaying='x', range=c(0,2), showticklabels=F))  |>
  add_trace(type = "scatter", mode = "lines", x = c(0, 2), y = c(200,200), color = "line", xaxis='x2', showlegend=T)

res

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

3 Comments

This is truly smart; however, I don't enjoy maintaining/debugging plots with dual-axis, specially when dealing with subplots and whatnot. On a separate note, while mode = "bars" works, you should use type = "bar" (I understand that comes from the original post).
I think it's a good idea here to use a 2ndary x-axis if OP wants to simply add a threshold across the whole x-axis with, it is simpler to do. But if they want to add multiple threshold lines which align with the boxplots, then your approach will be simpler to use. It's up to the usecase really
Agreed. As I said, this is really smart. I was just sharing my experience/frustration dealing with dual-axis plots. I have lots of them; and to plotly's credit, at least they're not limited to transformations like ggplot is (yeah, I understand Hadley's argument there, but ...).
2

We can use a numeric x-axis:

library(dplyr)
library(plotly)

iris %>%
  group_by(Species) %>%
  summarise(sepal_length_tot = sum(Sepal.Length)) %>%
  mutate(x_numeric = row_number() - 1) %>% 
  plot_ly() %>%
  add_trace(type = "bar", x = ~x_numeric, y = ~sepal_length_tot, 
            name = "Sepal Length", color = "Species", 
            colors = "#8da0cb") %>%
  add_segments(x = -0.5, xend = 2.5, y = 200, yend = 200, 
               line = list(color = "#66c2a5", width = 2), name = "Threshold", 
               hovertemplate = "Threshold: %{y}<extra></extra>") %>%
  layout(xaxis = list(tickmode = "array", title = "Species",
                      tickvals = ~x_numeric, ticktext = ~Species))

Created on 2025-08-22 with reprex v2.1.1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.