Repository navigation
guide vs. show.legend with new_scale() #59
Description
Maybe I am misunderstanding the expected behavior of the argument show.legend in "ggplot2"
Should this layer be included in the legends? NA, the default, includes if any aesthetics are mapped. FALSE never includes, and TRUE always includes. It can also be a named logical vector to finely select the aesthetics to display.
It appears that show.legend behaves surprisingly with multiple geoms/layers in "ggplot2" (see first example below);
similarly, it does not work with ggnewscale::new_scale() (see further examples below).
Questions/problems related to show.legend have been documented by others: #31 and #32.
Instead of using show.legend, #31 (comment) provides a solution (or hack) via guide = "none" (see last examples below).
Could be helpful for users of the package to document the behavior of show.legend in combination with ggnewscale::new_scale() and provide examples using guide = "none"?
Thanks!
# Make up some values
nn <- 6
n <- 3 * 5 * nn
m <- data.frame(
id1 = factor(rep(1:3, each = n / 3)),
id2 = factor(rep(1:5, each = n / 5)),
x = runif(n, 1, 80)
)
m[["y"]] <- 10 + m[["x"]] * runif(n, 1, 2)
# The behavior of `show.legend` with multiple geoms is surprising in ggplot2:
# Plot `id2` by colored lines and omit legend (`show.legend = FALSE`);
# add plot `id1` by colored points and show legend (`show.legend = TRUE`)
# --> expected outcome: no legend for `id2`; legend for `id1` with colored points
# --> actual outcome: legend for `id2` with colored points (instead of lines); no legend for `id1`
p1 <- ggplot2::ggplot(
data = m,
mapping = ggplot2::aes(x = x, y = y)
) +
ggplot2::geom_line(
ggplot2::aes(color = id2),
show.legend = FALSE
) +
ggplot2::geom_point(
ggplot2::aes(color = id1),
show.legend = TRUE
) +
ggplot2::scale_color_viridis_d()
plot(p1)# Plot `id2` by colored lines and omit legend (`show.legend = FALSE`);
# --> expected outcome: no legend
# --> actual outcome: as expected
p1a <- ggplot2::ggplot(
data = m,
mapping = ggplot2::aes(x = x, y = y)
) +
ggplot2::geom_line(
ggplot2::aes(color = id2),
show.legend = FALSE
) +
ggplot2::scale_color_viridis_d()
plot(p1a)# Additionally, plot `id1` by new colors; show legend (`show.legend = TRUE`)
# --> expected outcome: no legend for `id2`, legend for `id1`
# --> actual outcome: legend for both `id1` and `id2` are shown
# (with incorrect (black) color for `id2`)
p2 <- p1a +
ggnewscale::new_scale_color() +
ggplot2::geom_line(
ggplot2::aes(color = id1),
show.legend = TRUE
) +
ggplot2::scale_color_viridis_d()
plot(p2)# Similarly, plot `id1` by new colors and with different line width;
# show legend (`show.legend = TRUE`)
# --> expected outcome: no legend for `id2`, legend for `id1`
# --> actual outcome: legend for both `id1` and `id2` are shown
# (with incorrect (blue) color and incorrect line width for `id2`;
# note: color in legend of `id2` differs from previous example)
p3 <- p1a +
ggnewscale::new_scale_color() +
ggplot2::geom_smooth(
linewidth = 1,
ggplot2::aes(color = id1),
show.legend = TRUE
) +
ggplot2::scale_color_viridis_d()
plot(p3)
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'# An analogous unexpected behavior occurs for the opposite, e.g.,
# - Plot `id2` by color and show legend (`show.legend = TRUE`)
# - Add plot `id1` by new colors and with different line width;
# omit legend (`show.legend = FALSE`)
# --> expected outcome: legend for `id2`, no legend for `id1`
# --> actual outcome: legend for both `id1` and `id2` are shown
# (with incorrect color and incorrect line width for `id1`)
p4 <- ggplot2::ggplot(
data = m,
mapping = ggplot2::aes(x = x, y = y)
) +
ggplot2::geom_line(
ggplot2::aes(color = id2),
show.legend = TRUE
) +
ggplot2::scale_color_viridis_d() +
ggnewscale::new_scale_color() +
ggplot2::geom_smooth(
linewidth = 1,
ggplot2::aes(color = id1),
show.legend = FALSE
) +
ggplot2::scale_color_viridis_d()
plot(p4)
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'# Solution/Hack
# from https://github.com/eliocamp/ggnewscale/issues/31#issuecomment-834398072
# --- to omit a legend, don't use `show.legend = FALSE`,
# instead use `guide = "none"`
p5a <- ggplot2::ggplot(
data = m,
mapping = ggplot2::aes(x = x, y = y)
) +
ggplot2::geom_line(
ggplot2::aes(color = id2)
) +
ggplot2::scale_color_viridis_d(guide = "none") +
ggnewscale::new_scale_color() +
ggplot2::geom_smooth(
linewidth = 1,
ggplot2::aes(color = id1),
show.legend = TRUE
) +
ggplot2::scale_color_viridis_d()
plot(p5a)
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'# respectively
p5b <- ggplot2::ggplot(
data = m,
mapping = ggplot2::aes(x = x, y = y)
) +
ggplot2::geom_line(
ggplot2::aes(color = id2),
show.legend = TRUE
) +
ggplot2::scale_color_viridis_d() +
ggnewscale::new_scale_color() +
ggplot2::geom_smooth(
linewidth = 1,
ggplot2::aes(color = id1)
) +
ggplot2::scale_color_viridis_d(guide = "none")
plot(p5b)
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'# Session info
print(packageVersion("ggnewscale"))
#> [1] '0.4.9'
print(packageVersion("ggplot2"))
#> [1] '3.4.4'Created on 2023-11-28 with reprex v2.0.2







Activity
eliocamp commentedon Nov 28, 2023
The issues are fixed in the development version of ggnewscale (
remotes::install_github(“eliocamp/ggnewscale”)).I probably won't be sending it to CRAN soon because I think I rather publish a rewrite of the internals that fixes other bugs and makes the code much more easy to follow. But it requires a change in ggplot2 which is not available yet.
Here, the line doesn’t contribute to the legend so the legend is only drawn with points instead of points and lines. The title of the legend inherits from the first layer. The legend is not drawn “for id2” nor id1, it’s drawn for the colour aesthetic, which combines all the unique values of id1 and id2.
The following examples work in the development version of ggnewscale
Default: shows both legends with lines
Remove legend for first layer: shows only legend for second layer
Remove legend for second layer: shows only legend for first layer
Remove legends for both layers: shows no legend.
Adds a smooth layer: shows legend only for that layer, with correct width.
Created on 2023-11-28 with reprex v2.0.2
dschlaep commentedon Nov 28, 2023
This is great, thanks so much for the fix and response! I installed the dev-version and the examples work nicely.
In your example "Remove legend for first layer: shows only legend for second layer", if I drop in an (unnecessary but possible)
show.legend = TRUE, then it reverts back to showing unexpected legends.Created on 2023-11-28 with reprex v2.0.2
eliocamp commentedon Nov 28, 2023
Thank you for the last example. I'll take it into account for the future refactoring. I suspect is something that could be fixed by the new logic.
eliocamp commentedon Jul 18, 2024
Looking into this,
show.legend = TRUEinstructs ggplot2 to include the geom in the legend even if it doesn't have an aesthetic mapping. So, take this example:The
id2line is being drawn in the legend corresponding to theid1line. But since theid2line is mapped to the second colour slot, it's drawn as a black line (the default colour, I guess), and since it's the second layer, it's drawn on top of theid1line.So in fact it seems that this is all working as written.
The issue is that the default guide takes "any" aes, so this part doesn't do anything.
ggnewscale/R/bump-aes-scales.R
Lines 30 to 32 in 51ad375
I could replace and make it take only the
new_aes, but that might cause other problems (the guide will not take fill aesthetics, for example).bennotkin commentedon Nov 4, 2024
I'm running into the issue of the last two comments, where
show.legend = Tin the second layer removes the color from the first scale.whereas if I remove
show.legend = T, the legend shows both scales appropriately.Including
show.legend = Tis helpful/needed when I want the legend to include all factor levels, even if not included in the chart:For additional info, I've not yet been able to minimally reproduce this, but in my actual use case, the fill color is still visible around a grey box in the legend:
I'm using v0.5.0, and also tried the latest Github release, which I believe is the same.
michaelhallquist commentedon Feb 3, 2025
I have a very similar problem for an R package that re-uses the fill aesthetic to layer brain images for visualization: https://github.com/michaelhallquist/ggbrain. Starting with ggplot2 3.5.0, when I want to show unused factor levels, I must now set
show.legend=TRUE, per tidyverse/ggplot2#5728. This undermines earlier layer settings. Any suggestions for how to go about debugging this would be most welcome! I feel like I'm close, but I don't quite know the innards of the object well enough.Thanks,
Michael
eliocamp commentedon Jan 7, 2026
Unfortunately I don't think there's anything I can do on ggnewscale side to fix this issue. Notice that this happens even when just using ggplot2 without ggnewscale at all
Created on 2026-01-07 with reprex v2.1.1