tidyverse / ggplot2

An implementation of the Grammar of Graphics in R
https://ggplot2.tidyverse.org
Other
6.5k stars 2.03k forks source link

`after_scale()` does not work in `Geom$default_aes` field #6135

Open teunbrand opened 2 weeks ago

teunbrand commented 2 weeks ago

This was brought to my attention by @larmarange.

Hypothetically, one could want to automate making the following plot, by setting the default colour aesthetic in the geom.

library(ggplot2)

ggplot(mpg, aes(displ, hwy, fill = drv)) +
  geom_point(aes(colour = after_scale(alpha(fill, 0.4))))

To do so, it does not seem unreasonable to make colour = after_scale(alpha(fill, 0.4)) part of the Geom$default_aes field.

GeomPointAlt <- ggproto(
  "GeomPointAlt", GeomPoint,
  default_aes = aes(
    colour = after_scale(alpha(fill, 0.4)),
    # Merge with default, removing pre-existing `colour`
    !!!modifyList(GeomPoint$default_aes, list(colour = NULL))
  )
)

However, that doesn't work because default aesthetics are evaluated in isolation, not in context of the data. For that reason, the fill aesthetic cannot be found.

ggplot(mpg, aes(displ, hwy, fill = drv)) +
  stat_identity(geom = GeomPointAlt)
#> Warning: Failed to apply `after_scale()` modifications to legend
#> Caused by error:
#> ! object 'fill' not found
#> Error: object 'fill' not found

Created on 2024-10-11 with reprex v2.1.1

The relevant line is indicated below, and I think we only need to add a data = data to the lapply().

https://github.com/tidyverse/ggplot2/blob/ddd207e926cc1c1847dc661d9a099b8ec19c4010/R/geom-.R#L139

yjunechoe commented 2 weeks ago

BTW I don't know if this will complicate things a lot, but I think get_geom_defaults() which also errors on that line will need a special treatment since data = NULL. (or maybe asking for a "default" value for delayed aesthetics should just error/skip early as that may be kinda odd)

get_geom_defaults(GeomPointAlt)
#> Error: object 'fill' not found