1

I'm trying to have a ggplot with two vertical lines on it, with a separate custom legend to explain what the lines represent. This is my code (using iris):

irate <- as.data.frame(iris)
irate$Species <- as.character(irate$Species)

irritating <- ggplot(irate) +
  geom_line(aes(y = Sepal.Length, x = Sepal.Width), color = "blue") +
  geom_point(aes(y = Sepal.Length, x = Sepal.Width, color = Species), size = 5) +
  theme(legend.position = "right", axis.text.y = element_blank(), axis.title.y = element_blank(), axis.ticks.y = element_blank(), panel.grid.major.y = element_blank())+
  labs(title = "The chart", x = "Sepal Width") +
  geom_vline(color = "black", linetype = "dashed", aes(xintercept = 3))+
  geom_vline(color = "purple", linetype = "dashed", aes(xintercept = 4))

irritating 

Result

I've tried using things like scale_color_manual (etc), but for some reason when doing so it will interfere with the main legend and not produce a separate one.

Using answers to questions like: Add legend to geom_vline

I add: +scale_color_manual(name = "still problematic", values = c("black", "purple", "red"))

the addition of "red" in the vector the only way to get it to produce a chart (otherwise there's a: "Insufficient values in manual scale. 3 needed but only 2 provided." error). enter image description here

alec22
  • 735
  • 2
  • 12

3 Answers3

4

One option to achieve your desired result would be to use a different aesthetic to create the colro legend for your vlines. In my code below I map on the linetype aes and use the override.aes argument of guide_legend to assign the right colors:

irate <- as.data.frame(iris)
irate$Species <- as.character(irate$Species)

library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.2.2

base <- ggplot(irate) +
  geom_line(aes(y = Sepal.Length, x = Sepal.Width), color = "white") +
  geom_point(aes(y = Sepal.Length, x = Sepal.Width, color = Species), size = 5) +
  theme(legend.position = "right", axis.text.y = element_blank(), axis.title.y = element_blank(), axis.ticks.y = element_blank(), panel.grid.major.y = element_blank())+
  labs(title = "The chart", x = "Sepal Width") 

base +
  geom_vline(color = "black", aes(xintercept = 3, linetype = "Black Line"))+
  geom_vline(color = "purple", aes(xintercept = 4, linetype  = "Purple line")) +
  scale_linetype_manual(name = "still problematic", values = c("dashed", "dashed")) +
  guides(linetype = guide_legend(override.aes = list(color = c("black", "purple"))))

And the second and perhaps cleaner solution would be to use the ggnewscale package which allows to have multiple legends for the same aesthetic:


library(ggnewscale)

base +
  new_scale_color() +
  geom_vline(linetype = "dashed", aes(xintercept = 3, color = "Black Line"))+
  geom_vline(linetype = "dashed", aes(xintercept = 4, color  = "Purple line")) +
  scale_color_manual(name = "still problematic", values = c("black", "purple"))

stefan
  • 90,330
  • 6
  • 25
  • 51
3

Here is a way with package ggnewscale that makes plotting two legends for two color mappings very easy.
The main trick is to create a data.frame with the x intercept values and colors, then assign this data set to the data argument of geom_vline. If this is run after new_scale_color() the colors will be the right ones.

library(ggplot2)
library(ggnewscale)

irate <- iris
irate$Species <- as.character(irate$Species)
happy <- data.frame(xintercept = c(3, 4), color = c("black", "purple"))

delightful <- ggplot(irate) +
  geom_line(aes(y = Sepal.Length, x = Sepal.Width), color = "blue") +
  geom_point(aes(y = Sepal.Length, x = Sepal.Width, color = Species), size = 5) +
  theme(legend.position = "right", axis.text.y = element_blank(), axis.title.y = element_blank(), axis.ticks.y = element_blank(), panel.grid.major.y = element_blank())+
  labs(title = "The chart", x = "Sepal Width") +
  new_scale_color() +
  geom_vline(
    data = happy,
    mapping = aes(xintercept = xintercept, color = color),
    linetype = "dashed"
  ) +
  scale_color_manual(values = c(black = "black", purple = "purple"))

delightful 

Created on 2022-11-30 with reprex v2.0.2

Rui Barradas
  • 70,273
  • 8
  • 34
  • 66
2

Using linetype in aes to put those parts in the legend you can then override the guide display colours:

library(ggplot2)

irate <- as.data.frame(iris)
irate$Species <- as.character(irate$Species)

irritating <- ggplot(irate) +
  geom_line(aes(y = Sepal.Length, x = Sepal.Width), color = "white") +
  geom_point(aes(y = Sepal.Length, x = Sepal.Width, color = Species), size = 5) +
  theme(
    legend.position = "right",
    axis.text.y = element_blank(),
    axis.title.y = element_blank(),
    axis.ticks.y = element_blank(),
    panel.grid.major.y = element_blank()
  ) +
  labs(title = "The chart", x = "Sepal Width") +
  geom_vline(linewidth = 1.5,
             color = "black",
             aes(xintercept = 3, linetype = "Something")) +
  geom_vline(linewidth = 1.5,
             color = "purple",
             aes(xintercept = 4, linetype = "Another thing")) +
  scale_linetype_manual(
    "Things",
    values = c("dashed", "dashed"),
    guide = guide_legend(override.aes = list(colour = c("purple", "black")))
  )

irritating

Andy Baxter
  • 5,833
  • 1
  • 8
  • 22