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

Off-label use of plot labels #5

Closed
teunbrand opened this issue Jan 31, 2025 · 11 comments
Closed

Off-label use of plot labels #5

teunbrand opened this issue Jan 31, 2025 · 11 comments

Comments

@teunbrand
Copy link

Hi there,

The ggplot2 package is planning an update for around May 2025 and a reverse dependency test indentified a problem with the yamlet package.
The details are explained in tidyverse/ggplot2#6290, but essentially ggplot2 doesn't populate the plot$labels field before plot building anymore.

The change in ggplot2 is at odds with how labels are used in the .decorated_ggplot function. It appears to me that the plot labels are being used to iterate over aesthetics, but labels are not suitable to derive aesthetics. I think this is off-label use, and would suggest that the yamlet package changes its approach. You can test the changes yourself with the development version of ggplot2 (pak::pak("tidyverse/ggplot2"))

Best,
Teun

@bergsmat
Copy link
Owner

@teunbrand Hi Teun.

Thanks for identifying this issue. I agree that my usage is off-label. It has worked great for years, though I have always suspected it would fail one day. Today is that day!

Briefly, the yamlet package allows users to define all their labels in advance, as column attributes on a data.frame having decorated prepended to the class vector. Method ggplot.decorated() calls NextMethod() and then prepends decorated_ggplot to the class vector of the result. Method print.decorated_ggplot() (like ggplot_build.decorated_ggplot()) iterates through plot$labels, replacing them (where appropriate) with "better" labels from the column attributes of plot$data.

Based on your notes, it looks like I can make good use of the accessor function get_labs(). It is not clear to me (yet) whether all label types can be set with labs(). I'm also interested in the display of facets, which I currently query by means of names(x$facet$params$facets), names(x$facet$params$rows), names(x$facet$params$cols) (possibly also off-label).

I will install the development version and revise. Please mention if you think I'm missing something important.

@teunbrand
Copy link
Author

replacing them (where appropriate) with "better" labels from the column attributes of plot$data

This should also natively happen in the upcoming ggplot2, so it might pay off to test around a bit to see if this can be skipped

I'm also interested in the display of facets

We also made a get_strip_labels() accessor function for this

@bergsmat
Copy link
Owner

@teunbrand Very helpful, thanks!

@bergsmat
Copy link
Owner

@teunbrand ?labs() for the ggplot version given by pak::pak() documents the argument dictionary which appears to be exactly what I need in the majority of cases. At the moment, though, it seems the argument is ignored, and therefore difficult to test. reprex::reprex() gives an unhelpful error at my end, so I illustrate with pdf, attached (example stolen from the help file). Less than 70 issues open for tidyvers/ggplot2 at the moment (impressive!) and none of them seem to be related. Do you think this is a bug?

dictionary.pdf

@teunbrand
Copy link
Author

teunbrand commented Feb 18, 2025

That appears to be a misspelling in the examples. The correct argument name is dictionary, not dict. I don't think this is a bug, but it is a documentation issue that requires correction on ggplot2's end.

@bergsmat
Copy link
Owner

@teunbrand Earlier you hinted that the development version of ggplot2 may automatically respect column labels. Indeed, I find it so:

library(ggplot2)
attr(mtcars$wt, 'label') <- 'Weight (kg)'
ggplot(mtcars, aes(wt, mpg)) + geom_point()

uses the label attribute of wt for the x axis label. Useful! But requires significant de-escalation at my end.

  1. Can you point me to the documentation of this behavior?
  2. Is there some way for my package code to distinguish versions before and after this change? (analogous to your get_labs() detector in #6078).

@teunbrand
Copy link
Author

teunbrand commented Feb 18, 2025

Useful! But requires significant de-escalation at my end.

Yeah I'm afraid efforts to improve ggplot2 sometimes hurt packages that rely on ggplot2's internal mechanisms. We try to avoid unnecessary breakage, but we cannot always find satisfactory solutions for every dependency (as is the case here).

  1. The only documentation that exists on this topic is in the related news bullet: https://github.com/tidyverse/ggplot2/blob/d835cfe2ffb0ff68c9a4b224ec1fcb4caba83892/NEWS.md?plain=1#L145-L146. The label priorities are (1) expression derived from the stat-part of a layer (2) user-provided expression (3) label attribute of the column when the expression is a symbol (i.e. aes(wt) works but not aes(wt + 1) (4) label in dictionary (5) direct label via labs() (6) scale name (7) guide title. The new ones are (3) and (4).
  2. I've used "get_labs" %in% getNamespaceExports("ggplot2") before to test for the absence/presence of the new label system.

@bergsmat
Copy link
Owner

@teunbrand Very helpful, thanks.

@bergsmat
Copy link
Owner

@teunbrand At your convenience, please consider:

library(ggplot2)
attr(mtcars$wt, 'label') <- 'Weight (kg)'
a <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
b <- ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs(x = 'Weight (kg)')
identical(get_labs(a), get_labs(b))
#> [1] TRUE
a$labels
#> list()
b$labels
#> $x
#> [1] "Weight (kg)"

Created on 2025-02-19 with reprex v2.1.1

Like ggplot2, yamlet wants to be helpful but not too helpful. It is happy to calculate fancy axis labels for the user, but only if the user has not supplied these him/herself. Although a and b above look identical, and have identical get_labs(), yamlet would want to mess with a (auto-append units, convert to expression if plotmath detected, etc.) but not with b. So I will still need to peek at plot$labels to establish permission-to-modify (currently I just check if the label for x is "x", etc.). If the interface provides an official way to detect user-specified labels, please let me know.

@teunbrand
Copy link
Author

Sounds sensible to me, it is not forbidden to look into internal structures but they are subject to change.

@bergsmat
Copy link
Owner

@teunbrand At 62acf (v. 1.2.1) I get clean package checks and only expected differences when using R-devel with either ggplot2 3.5.1 or the pre-release. I'll close this tentatively, but please don't hesitate to re-open if something warrants further attention. Thanks again for identifying the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants