3

Objective: to have a color legend for geom_point showing a colored dot for each group together with a legend for geom_abline showing one colored line for each line.

Am I doing something wrong? Is there a solution?

# data: mtcars + some made-up straight lines
library(ggplot2)
df = data.frame(Intercept = c(2,3,4), Slope = c(0,0,0), Group = factor(c(1, 2, 3)))

Comment about #1: There's nothing special about the basic plot, but I have grouped the data inside the aes() and made color an aes(). I think it is standard to have both "group" and "color" inside the aes to achieve grouping and coloring.

# 1. my basic plot
ggplot(data = mtcars, aes(x = mpg, y = wt, group = vs, color = factor(vs))) + 
    geom_point() -> p

Comment about #2: Clearly I did not set up ggplot right to handle the legend properly. I also tried to add group = Group inside the aes. But there is a somewhat more serious problem: geom_point forms 2 groups, geom_abline forms 3 groups, but the legend is showing only 4 color/line combinations. One of them has been merged (the green one). What have I done wrong here?

# 2. my naive attempt to add ablines of 3 different colours
p + geom_abline(data = df, aes(intercept = Intercept, slope = Slope,
                           colour = Group))

enter image description here

Comment about #3: The ablines have been removed in the legend, but the points are still not right. From here on it gets more and more desperate.

# 3. Suppress the ab_line legend?
p + geom_abline(data = df, aes(intercept = Intercept, slope = Slope,
                           colour = Group), show.legend = FALSE)

enter image description here

Comment about #4: This is what I'm going for at the moment. Better no legend than a wrong legend. Shame about losing the colors though.

# 4. Remove the geom_abline legend AND colors
p + geom_abline(data = df, aes(intercept = Intercept, slope = Slope))

enter image description here

Comment #5: I don't know what I was hoping here... that if I defined the data and aes inside the call to geom_point() rather than the ggplot(), somehow geom_abline()) would not hijack the colors and legend, but no, it does not appear to make a difference.

# 5. An attempt to set the aes inside geom_point() instead of ggplot()
ggplot() + 
    geom_point(data = mtcars, aes(x = mpg, y = wt, group = vs, color = factor(vs))) + 
    geom_abline(data = df, aes(intercept = Intercept, slope = Slope, color = "groups")) +
    scale_color_manual(values = c("red", "blue", "black"))

enter image description here

PatrickT
  • 10,037
  • 9
  • 76
  • 111
  • Not sure if this helps. You have 4 different grouping variables in your two datasets. In `mtcars` you have 0-1 variable `vs` and in your data `df` there is `Group` with levels 1, 2, 3. When you change levels of `Group` in `df` to e.g. A, B, C, you will get 5 different colours. This is kind of hack solution, because I would expect that `geom_abline` and `geom_point` would work with different datasets. – Adela Nov 07 '17 at 10:51
  • ggplot performs as expected, it is displaying factor _levels_. Try dropping the missing levels (`droplevels`). – Roman Luštrik Nov 07 '17 at 11:08
  • 1
    @Adela, thanks! I understand: ``ggplot`` doesn't see that the ``1`` from ``mtcars`` and the ``1`` from ``df`` are different. I wonder if that is expected or a feature limitation. – PatrickT Nov 07 '17 at 11:34
  • @RomanLuštrik, thanks. Which levels should I drop? From ``df``, I have ``Group : Factor w/ 3 levels "1","2","3": 1 2 3`` And from ``mtcars`` I have ``Factor w/ 2 levels "0","1": 1 1 2 2 1 2 1 2 2 2``. Clearly ``df`` and ``mtcars`` have ``"1"`` as a common level. What do I drop? – PatrickT Nov 07 '17 at 11:36

2 Answers2

5

One option would be to use a filled shape for the mtcars data, then you can have a fill scale and a colour scale, rather than two colour scales. You could add an option such as colour="white" to the geom_point statement in order to change the colour of the edges of the points, if you don't want the black outlines.

library(ggplot2)
df = data.frame(Intercept = c(2,3,4), Slope = c(0,0,0), Group = factor(c(1, 2, 3)))
ggplot(data = mtcars, aes(x = mpg, y = wt, group = vs, fill = factor(vs))) + 
           geom_point(shape=21, size=2) +
           geom_abline(data = df, aes(intercept = Intercept, slope = Slope,
                              colour = Group))

enter image description here

Andrew Gustar
  • 17,295
  • 1
  • 22
  • 32
  • 1
    Fine piece of ``ggplot`` hacking, thanks! I guess we have raised a couple of issues here. ONE: ``ggplot`` gets confused when grouping from different dataframes that carry different variables with the same level (here it was ``1``). TWO: ``ggplot`` doesn't separate ``geom_point`` pieces of legend that originate from different geoms, e.g. it treats points made by ``geom_point()`` the same as it does points made by ``geom_abline()``. – PatrickT Nov 07 '17 at 11:44
  • 1
    Is there anyway to have the "Group" lines in the legend horizontal ? – Hugues Feb 02 '23 at 16:25
  • @Hugues Yes there is - if you replace the `geom_abline` statement with `geom_hline(data = df, aes(yintercept = Intercept, colour = Group))` (which also means you don't need `Slope` defined in `df`). – Andrew Gustar Feb 03 '23 at 10:36
  • I need Slope, and geom_abline, I just want the legend to render with horizontal lines, as usual in a chart. – Hugues Feb 04 '23 at 16:37
  • 1
    @Hugues This might help... https://stackoverflow.com/questions/35703983/how-to-change-angle-of-line-in-customized-legend-in-ggplot2 – Andrew Gustar Feb 04 '23 at 17:03
  • 1
    It worked, with some tweaks. – Hugues Feb 04 '23 at 19:12
0

if you need or want a horizontal line in the legend you might consider using this code:

library(ggplot2)
df = data.frame(Intercept = c(2,3,4), Slope = c(0,0,0),
Group = factor(c(1, 2, 3)))
ggplot(data = mtcars,
   aes(x = mpg, y = wt, group = vs, fill = factor(vs))) + 
geom_point(shape=21, size=2) +
geom_hline(data = df,
          aes(yintercept = Intercept,colour = Group))

plot with geom_hline

POPPKPD
  • 41
  • 3
  • Thanks, but I really need ``geom_abline`` as the lines I'm drawing are the linear quantiles. Andrew has offered a good answer. There doesn't appear to be a simpler way to achieve the result at this time. – PatrickT Nov 08 '17 at 12:20