8

ggplot2 combines glyphs for the same scale which leads to confusing behaviour when using geom_hline and geom_vline to show two perpendicular lines as the key glyph for each color contains a cross (+) rather than | or - in correspondence to that color. How to prevent ggplot from combining - and | key glyphs?

library(ggplot2)
means = as.data.frame(t(colMeans(mtcars)))

(
    ggplot(mtcars, aes(x = disp, y = drat))
    + geom_point()
    + geom_hline(data = means, aes(color = 'average disp', yintercept = drat))
    + geom_vline(data = means, aes(color = 'average drat', xintercept = disp))
)

enter image description here

I tried setting key_glyph explicitly for both the geom_vline and geom_hline but it does not change anything.

Note: aside of the ease of interpretation, this is crucial for creating colorblind-friendly plots.

krassowski
  • 13,598
  • 4
  • 60
  • 92
  • 2
    Great question. I notice adding `, show.legend = F` to one of the lines makes ggplot2 show only a single glyph twice. – M.Viking Feb 02 '21 at 01:05

2 Answers2

5

Possible hacks off the top of my mind:

  1. Disguise a shape legend (with horizontal & vertical lines) as a colour legend (pro: less manual work than the following approaches; con: difficult to map the shape aesthetic to anything else)

  2. Convert the ggplot2 object to a grob object via ggplotGrob, & remove the offending grobs for horizontal / vertical line segments there (pro: doesn't affect any other aesthetic or require other packages; con: require familiarity with grob modifications & manual work for each plot)

  3. Create separate plots with only the desired legend items, then combine the legends together using one of the many available packages such as patchwork or cowplot (pro: can deal with any combination of desired legend; con: require familiarity with other packages + manual labour to tweak the plots' appearances)

I haven't looked into modifying the underlying ggplot code itself just yet, because I feel that under most circumstances, combining glyphs is a desirable end. Legend for horizontal and vertical lines is a valid exception, but not a particularly common one. So I'd go with hacks for now.

Demonstration for the first hack below:

ggplot(mtcars, aes(x = disp, y = drat)) + 
  geom_point() + 
  geom_hline(data = means, aes(color = 'average disp', yintercept = drat),
             show.legend = F) +
  geom_vline(data = means, aes(color = 'average drat', xintercept = disp),
             show.legend = F) +
  geom_point(data = data.frame(val = c(means$drat, means$disp), 
                               name = c("average disp", "average drat")),
             aes(x = 150, y = 3, # arbitrary values, anything within the plot's range
                 color = name, shape = name),
             alpha = 0,          # don't actually show the point in the plot
             size = 5,           # adjust size / stroke to suit your plot's size
             stroke = 2) +
  scale_shape_manual(name = "colour",
                     values = c("average disp" = "\U2014", # horizontal line
                                "average drat" = "\U007C"),# vertical line
                     guide = guide_legend(override.aes = list(alpha = 1)))

result

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
  • Z.Lin, would you mind adding this as an answer to the (duplicately related) question as per above? – tjebo Feb 02 '21 at 22:46
4

As per this answer to the same question (this was asked recently) - create a second scale. In the linked question you will also find a related GitHub issue, here for your convenience.

library(ggplot2)
library(ggnewscale)
means = as.data.frame(t(colMeans(mtcars)))

  ggplot(mtcars, aes(x = disp, y = drat))+
    geom_point()+
    geom_hline(data = means, aes(color = 'average disp', yintercept = drat))+
    scale_color_manual(values = "red", name = NULL) +
    new_scale_color() +
    geom_vline(data = means, aes(color = 'average drat', xintercept = disp)) +
    scale_color_manual(values = "blue", name = NULL)

Created on 2021-02-02 by the reprex package (v0.3.0)

tjebo
  • 21,977
  • 7
  • 58
  • 94
  • Thank you, this is a great solution indeed. I confirmed this one as a duplicate of the other. It is surprising to me that the SO question wizard did not pick it up and humbling to see that I could not find it. – krassowski Feb 02 '21 at 23:03
  • 1
    @krassowski PS you’ve asked a good question +1. I find duplicates in those cases a really good and important thing, as it helps broadening search terms. I don’t think I would have found the other question myself. it might not even have been indexed because so fresh. I find it also kind of cool how this particular question was asked just so close to one another twice. And maybe even more often, difficult to know. – tjebo Feb 03 '21 at 03:27