6

Is there a way to add a layer to a ggplot without it affecting the training of the scales, or at least not affecting the limits of the scale?

The use case I am working on is where I want to include a reference line but only need it if the data would naturally include the line. I don't want the scales to be expanded to include the line if the rest of the data would not otherwise require it.

Set up a example

library("ggplot2")

g <- ggplot(data = mtcars, aes(x=mpg, y=hp)) + geom_point()

Add a reference line at 250 horsepower.

g + geom_hline(yintercept = 250)

ggplot(data = mtcars, aes(x=mpg, y=hp)) + geom_point() + geom_hline(yintercept = 250)

If I change the data to something which does not extend as far along the y axis, the y axis is expanded to include the horizontal line.

(g + geom_hline(yintercept = 250)) %+% mtcars[mtcars$cyl < 8,] 

ggplot(data = mtcars[mtcars$cyl < 8,], aes(x=mpg, y=hp)) + geom_point() + geom_hline(yintercept = 250)

What I want is some way to specify in the geom_hline layer that it should not be used when determining the y scale limits. Something that works for any geom/layer would be best. The result should look like

g %+% mtcars[mtcars$cyl < 8,]

Desired look of previous plot

I am aware that I can manually set the y axis limits. But that does not solve my general problem because I don't know what the axis limits would otherwise be without the additional horizontal line layer.

I think it should be possible. In the examples in the documentation for layer_spatial, it mentions that annotation_spatial "layers don't train the scales, so data stays central", but I don't see a way to generalize that.

I have seen the question ggplot2: Adding a geom without affecting limits that is the same general question, but the answer there is specific to the added layer because of scaling the values within the layer.

Brian Diggs
  • 57,757
  • 13
  • 166
  • 188

1 Answers1

3

You could substitute an annotation_custom (which doesn't train the scales) in place of a geom_hline. In your case it would be:

g <- g + annotation_custom(
      grid::linesGrob(y = unit(c(0, 0), "npc")), ymin = 250, ymax = 250)

So now:

g

enter image description here

but

g %+% mtcars[mtcars$cyl < 8,]

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • I was hoping for a more generic solution that would work with any `geom` or `stat`, but it seems that is not possible. This solution works for the horizontal lines I need, and can be generalized to anything where what needs to be drawn is simple enough to specify the `grob`s for directly. – Brian Diggs Sep 08 '20 at 21:37
  • @BrianDiggs I know it's not ideal. It would be straightforward to write a wrapper around it to cover the common use cases though. I guess it would be nice if that was written in to ggplot already. – Allan Cameron Sep 08 '20 at 21:53