How to add a label to the x / y axis whenever a vertical / horizontal line is added to a ggplot?

1.2k views Asked by At

The x and y axis are labeled based on certain interval set by ggplot.

In the event that a horizontal or vertical line is added to the plot, the goal is to label x or y axis at the exact value of the line.

How can this be done?

1

There are 1 answers

1
Allan Cameron On BEST ANSWER

This isn't totally straightforward, but it is possible. I would probably use these two little functions to automate the tricky parts:

add_x_break <- function(plot, xval) {
  
  p2 <- ggplot_build(plot)
  breaks <- p2$layout$panel_params[[1]]$x$breaks
  breaks <- breaks[!is.na(breaks)]
  
  plot +
    geom_vline(xintercept = xval) +
    scale_x_continuous(breaks = sort(c(xval, breaks)))
}

add_y_break <- function(plot, yval) {
  
  p2 <- ggplot_build(plot)
  breaks <- p2$layout$panel_params[[1]]$y$breaks
  breaks <- breaks[!is.na(breaks)]
  
  plot +
    geom_hline(yintercept = yval) +
    scale_y_continuous(breaks = sort(c(yval, breaks)))
}

These would work like this. Start with a basic plot:

library(ggplot2)

set.seed(1)

df <- data.frame(x = 1:10, y = runif(10))

p <- ggplot(df, aes(x, y)) + 
  geom_point() +
  ylim(0, 1)

p

Add a vertical line with add_x_break

p <- add_x_break(p, 6.34)

p

Add a horizontal line with add_y_break:

p <- add_y_break(p, 0.333)
#> Scale for 'y' is already present. Adding another scale for 'y', which will
#> replace the existing scale.

p


ADDENDUM

If for some reason you do not have the code that generated the plot, or the vline is already present, you could use the following function to extract the xintercept and add it to the axis breaks:

add_x_intercepts <- function(p) {
  
  p2 <- ggplot_build(p)
  breaks <- p2$layout$panel_params[[1]]$x$breaks
  breaks <- breaks[!is.na(breaks)]
  
  vals <- unlist(lapply(seq_along(p$layers), function(x) {
    d <- layer_data(p, x)
    if('xintercept' %in% names(d)) d$xintercept else numeric()
  }))
  
  p + scale_x_continuous(breaks = sort(c(vals, breaks)))
}

So, for example:

set.seed(1)

df <- data.frame(x = 1:10, y = runif(10))

p <- ggplot(df, aes(x, y)) + 
  geom_point() +
  geom_vline(xintercept = 6.34) +
  ylim(0, 1)

p

enter image description here

Then we can do:

add_x_intercepts(p)

enter image description here

The y intercepts of geom_hline can be obtained in a similar way, which should hopefully be evident from the code of add_x_intercepts