Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

guide_legend ignores legend.spacing.(x/y) depending on byrow #4352

Closed
teunbrand opened this issue Feb 22, 2021 · 5 comments
Closed

guide_legend ignores legend.spacing.(x/y) depending on byrow #4352

teunbrand opened this issue Feb 22, 2021 · 5 comments
Labels
feature a feature request or enhancement guides 📏

Comments

@teunbrand
Copy link
Collaborator

Hello there,

I apologise in advance if I submit issues too frequently; I do really appreciate all the good work you put into ggplot2.
By default, a legend appears to ignore the legend.spacing.y theme setting.

library(ggplot2)

p <- ggplot(mtcars, aes(mpg, fill = as.factor(cyl))) +
  geom_density() +
  theme(legend.spacing.y = unit(0.5, "cm"))
p

However, when byrow = TRUE in the guide, the legend.spacing.y is applied as expected.

p + guides(fill = guide_legend(byrow = TRUE))

Created on 2021-02-22 by the reprex package (v1.0.0)

For horizontal legend layouts, legend.spacing.x is ignored when byrow = TRUE, which is opposite to the vertical case.

ggplot(mtcars, aes(mpg, fill = as.factor(cyl))) +
  geom_density() +
  theme(legend.spacing.x = unit(0.5, "cm"),
        legend.position = "bottom") +
  guides(fill = guide_legend(label.position = "top",
                             byrow = TRUE))

I was surprised by this as I would have expected the legend.spacing to be applied regardless of the layout order of the keys, but I'm likely not aware of the considerations going in to this. I believe the code controlling the legend spacing and gaps sometimes interleave()s the label/key sizes with spacings, and sometimes doesn't, conditional on the byrow argument.

In an earlier comment (#3587 (comment)), it was proposed to get rid of the legend spacing, but I think there is a use-case for the spacing for the keys (the text margins maybe less so). Would it be a good idea to always interleave the spacing regardless of the byrow argument?

@thomasp85 thomasp85 added feature a feature request or enhancement guides 📏 labels Mar 24, 2021
@twest820
Copy link

I think there is a use-case for the spacing for the keys (the text margins maybe less so).

100% agree. So far as I know, byrow = TRUE is the only way to space out the keys of a vertical fill legend without having the key expand from a square into a rectangle. Since I depend on this feature it would really suck to have it cut (unless there is some other mechanism to avoid things like too closely packed lines text in vertical fill legends with multiline labels).

There's loads of questions about this on Stackoverflow and all the answers there about introducing spacing between legend keys using legend.spacing{.x, .y}, keywidth, and keyheight don't work for fill legends because they're all for geom_point() markers.

By default, a legend appears to ignore the legend.spacing.y theme setting.

As I understand it, by default legend.spacing.y applies only between the legend title and the first key of a vertical legend. E.g. between as.factor(cyl) and the four cylinder key in the example above. I think what byrow = TRUE then does is apply the spacing to every key in a vertical legend.

It's uncommon, but occasionally I find need to use legend.spacing.y with the default byrow = FALSE to get the spacing between a legend title and first key to look better.

For horizontal legend layouts, legend.spacing.x is ignored when byrow = TRUE, which is opposite to the vertical case.

It's probably not getting ignored. What's probably happening (and what I've seen in earlier versions of ggplot but haven't checked recently) is the spacing is applied between a horizontal legend and the plot. It's just harder to see because a horizontal legend has the title to the side and only one row of keys.

In general, my experience is it's cryptic which legend settings adjust sizing where under which circumstances. If there were figures in the documentation diagramming out how settings apply across different legend configurations what the default spacings and units are that would probably help considerably. But, if these exist, I don't really know where they're included. You can figure some of it out from the examples in various parts of the documentation (and answers to Stackoverflow questions) but these are at the level of individual settings rather than a master diagram.

@teunbrand
Copy link
Collaborator Author

It's uncommon, but occasionally I find need to use legend.spacing.y with the default byrow = FALSE to get the spacing between a legend title and first key to look better.

Intuitively, I'd look for the legend.title = element_text(margin = margin(b = {your_margin_here}) to control the spacing between the legend title and the keys (vertically at least). I don't find it intuitive to control this through the legend.spacing argument, as I would expect this to control the spacing between keys.

the spacing is applied between a horizontal legend and the plot.

Similarly, I would expect this to be controlled by the legend.box.margin or legend.margin arguments to the theme.

If there were figures in the documentation diagramming out how settings apply across different legend configurations what the default spacings and units are that would probably help considerably.

I agree with that this would be nice to have somewhere.

@teunbrand
Copy link
Collaborator Author

teunbrand commented May 19, 2022

I've thought about this some more, and have some more concrete suggestions (that can lead to a visual change). Consider the following plots:

library(ggplot2)

plt <- ggplot(mpg, aes(displ, hwy, colour = as.factor(cyl))) +
  geom_point() +
  theme(
    legend.spacing.x = unit(0.5, "cm"),
    legend.spacing.y = unit(0.5, "cm")
  )

p1 <- plt + guides(colour = guide_legend(byrow = TRUE, ncol = 2)) +
  ggtitle("byrow = TRUE")
p2 <- plt + guides(colour = guide_legend(byrow = FALSE, ncol = 2)) +
  ggtitle("byrow = FALSE")
patchwork::wrap_plots(list(p1, p2))

Created on 2022-05-19 by the reprex package (v2.0.1)

Foremost, my issue is that we cannot control the (vertical) spacing between keys when byrow = FALSE. Additionally, we cannot really control spacing in the x-direction before and after the text. See left side in diagram below (forgive me my illustration skills). Similar issues occur with alternative legend/label position placements.

image

In the diagram I've placed some suggestions on how the situation could be improved:

  • Regardless of byrow setting, legend spacing arguments are always applied. Having had a look at the legend drawing code, it seems that this would simplify the code somewhat.
  • To prevent users from having to hustle with element_text(margin = ...), we can introduce a new theme element that controls the spacing of text to keys, e.g. legend.spacing.text. Alternatively, we can just set a default, like how title_fontsize is used below:

ggplot2/R/guide-legend.r

Lines 331 to 338 in d6f5bf4

title_fontsize <- title.theme$size %||% calc_element("legend.title", theme)$size %||%
calc_element("text", theme)$size %||% 11
# gap between keys etc
# the default horizontal and vertical gap need to be the same to avoid strange
# effects for certain guide layouts
hgap <- width_cm(theme$legend.spacing.x %||% (0.5 * unit(title_fontsize, "pt")))
vgap <- height_cm(theme$legend.spacing.y %||% (0.5 * unit(title_fontsize, "pt")))

If this seems like a good idea to the ggplot2 maintainers, I can try to draft a PR to review.

@georgeblck
Copy link

I was faced with this problem of needing to adjust my vertical space when using guides(colour=guide_legend(ncol=2,byrow=FALSE)). As you have described it is not possible to adjust the vertical spacing between the keys this way.


This SO answer actually worked very well for me, so it is maybe a solution?

guides(colour=guide_legend(ncol=2,byrow=FALSE,
                           keywidth=0.5,
                           keyheight=0.1,
                           default.unit="cm"))

@teunbrand
Copy link
Collaborator Author

This was fixed by #5456.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement guides 📏
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants