Show different geoms in a legend for a common aesthetic

This graph uses points, pointranges, and lines to plot different kinds of data. The data sources are distinguished by color.

Currently, the legend shows all the geoms at once.

I would like the legend to reflect only the geom used with that color. So:

  • "acs 1-year" will only show a pointrange
  • "admin v1" and "admin v2" will only show a line
  • "decennial census" will only show a point

I want to keep all four legend entries in a single legend for the "colour" aesthetic.

Here is the replication data.

structure(list(year = c(1990, 1991, 1992, 1993, 1994, 1995, 1996, 
1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 
2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 
2019, 2020, 2021, 2022), admin1 = c(105939L, 105568L, 105207L, 
105252L, 104878L, 104498L, 103960L, 104071L, 103689L, 103474L, 
104039L, 104874L, 104949L, 105767L, 106559L, 107339L, 106348L, 
105580L, 104455L, 102815L, 101121L, 99244L, 96823L, 95091L, 94082L, 
92769L, 91811L, 91680L, 91350L, 82767L, 89030L, 89764L, 89942L
), admin2 = c(105944L, 105573L, 105209L, 105254L, 104880L, 104500L, 
103961L, 104072L, 103691L, 103475L, 104039L, 104874L, 104949L, 
105767L, 106559L, 107339L, 106348L, 106021L, 104874L, 103208L, 
101580L, 99732L, 97343L, 95648L, 94685L, 93390L, 92467L, 92375L, 
92137L, 92703L, 93417L, 94222L, 95200L), decennial_count = c(107682, 
NA, NA, NA, NA, NA, NA, NA, NA, NA, 105235, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, 100296, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA), acs1 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, 113122, 109275, 105005, 105918, 104330, 102660, 
98506, 95432, 97196, 97841, 94046, 93184, 92218, 96609, 92982, 
NA, 96044, NA), acs1_moe = c(NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA, NA, 4433, 3735, 3484, 3227, 3379, 3527, 
3171, 3259, 3105, 3800, 3711, 3232, 3507, 3816, 3261, NA, 3817, 
NA)), row.names = c(NA, -33L), class = c("tbl_df", "tbl", "data.frame"

And here is the minimal ggplot2 code.

ggplot(df) +
  geom_line(aes(x = year, y = admin1, color = "admin v1")) +
  geom_line(aes(x = year, y = admin2, color = "admin v2")) +
  geom_pointrange(aes(x = year, y = acs1, ymin = (acs1 - acs1_moe),
                      ymax = (acs1 + acs1_moe), color = "acs 1-year")) +
  geom_point(aes(year, decennial_count, color = "decennial census"),
             size = 4, shape = "diamond")

Thanks in advance. I apologize if I've missed an obvious explanation for this situation, but I haven't been able to find one in the docs.

I do have a hacky way. First, the code:

[df <- ...]


df |>
  pivot_longer(c(admin1, admin2),
               names_to = "admin_v",
               values_to = "admin") |>
  ggplot() +
  geom_line(aes(x = year, y = admin, color = admin_v)) +
  geom_pointrange(aes(x = year, y = acs1, ymin = (acs1 - acs1_moe),
                      ymax = (acs1 + acs1_moe), shape = "acs-1"), color = "red") +
  geom_point(aes(year, decennial_count, fill = "decennial count"),
             color = "purple",
             size = 4, shape = "diamond")
#> Warning: Removed 34 rows containing missing values (geom_pointrange).
#> Warning: Removed 60 rows containing missing values (geom_point).

Created on 2022-12-08 by the reprex package (v2.0.1)

The problem you have is that you define "color = " within the aes() call. So from the ggplot point of view, all of these are different values of a same color scale, it makes sense to show the legend together.

This is only true in the case of admin1/admin2, so I've put them in a single call (using a pivot_longer() beforehand to have tidy data). Then, I've taken all the color = out of the aes(), so you can specify the color as a value, rather than as an aesthetic mapping.

Now the problem is to make the legend icons appear at all. I've experimented with show.legends = TRUE, but it has the tendency to show all symbols on top of each other, which is not what you want. It might be possible to do better using a named logical vector in show.legend, I haven't figured out how.

So that left me with one approach: using irrelevant aesthetics to put in the name of interest. Somehow, it works, but it doesn't feel really right.

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.