Skip to content

Feature requests: Composable mappings #4789

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

Closed
teunbrand opened this issue Mar 31, 2022 · 8 comments
Closed

Feature requests: Composable mappings #4789

teunbrand opened this issue Mar 31, 2022 · 8 comments

Comments

@teunbrand
Copy link
Collaborator

teunbrand commented Mar 31, 2022

Hi ggplot2 team,

In brief: I think it would be neat if it was possible to do aes(x = 1, y = 2) + aes(colour = class), and get the equivalent of aes(x = 1, y = 2, colour = class). In effect, I'm suggesting that the aes() mappings might be composed, much like the layers of a plot.

Why might this be neat? I'm probably not the only one who writes out small novels in most aes() calls. Most of them are pretty repetitive among layers or plots, but not exactly repetitive enough to warrant assigning the mapping to a variable for reuse.

Consider the example below, wherein we might want to recycle the colour mapping to other plots/layers as well. For instance, another dataset with different column names, which renders the fill part invalid, or another layer with similar but not duplicated mappings.

library(ggplot2)

ggplot(mtcars, aes(mpg, disp)) +
  geom_point(
    aes(fill   = factor(cyl),
        colour = after_scale(colorspace::darken(fill, 0.3))),
    shape = 21, size = 3
  )

What I'm suggesting is to write some mechanism wherein parts of mappings can be easily combined. For example:

`+.uneval` <- function(e1, e2) {
  if (!inherits(e2, "uneval")) {
    rlang::abort("Appropriate message here")
  }
  ans <- ggplot2:::defaults(e2, e1)
  class(ans) <- class(e1)
  ans
}

This then allows you to set the colour outside of the plot, combine it with the mapping of the layer, and reuse it elsewhere at a later point.

dark_outline <- aes(colour = after_scale(colorspace::darken(fill, 0.3)))

ggplot(mtcars, aes(mpg, disp)) +
  geom_point(
    aes(fill = factor(cyl)) + dark_outline,
    shape = 21, size = 3
  )

Created on 2022-03-31 by the reprex package (v2.0.1)

Thank you for your work and consideration of this FR!

@thomasp85
Copy link
Member

Alternative api that behaves more like classic vectors:

col_aes <- aes(col = gear)

ggplot(mtcars) + geom_point(aes(x = mpg, y = disp, col_aes))

@teunbrand
Copy link
Collaborator Author

teunbrand commented Apr 1, 2022

That also seems like a nice way to combine them. Might also be able to do c(aes(x = mpg), aes(y = disp)). I'm not married to the +-operator.

@yutannihilation
Copy link
Member

We can already do aes(x = mpg, y = disp, !!!col_aes). Do we feel this isn't enough?

@teunbrand
Copy link
Collaborator Author

teunbrand commented Apr 1, 2022

I was unaware of this possibility, and it does seem convenient, so thank you for pointing this out. Having a quick play with this, it seems it works when the named x and y arguments are provided in the same aes(). This makes it a bit impractical.

library(ggplot2)

col_aes <- aes(colour = gear)

aes(!!!col_aes)
#> Error in `FUN()`:
#> ! Can't use `!!!` at top level.

aes(x = mpg, !!!col_aes)
#> Error in `FUN()`:
#> ! Can't use `!!!` at top level.

aes(x = mpg, y = disp, !!!col_aes)
#> Aesthetic mapping: 
#> * `x`      -> `mpg`
#> * `y`      -> `disp`
#> * `colour` -> `gear`

If you provide x and y globally, and want to inject an aesthetic at a specific layer that inherits these from the global mapping, you'll run into the error above. You also couldn't use it provide the x or y aesthetics themselves.

x_aes <- aes(x = mpg)

aes(y = disp, !!!x_aes)
#> Error in `FUN()`:
#> ! Can't use `!!!` at top level.

Created on 2022-04-01 by the reprex package (v2.0.1)

@yutannihilation
Copy link
Member

Ah, true. Sorry, I forgot the problem. I think there is some open issue related to this behaviour, but I couldn't find it.

@teunbrand
Copy link
Collaborator Author

I think I may have found it in #2675. I suppose if that issue can be solved, this feature request is redundant, so feel free to close this in favour of the other issue.

@yutannihilation
Copy link
Member

Thanks, yes, that one. I hope so, but, considering I've failed to solve #2675, maybe we need some fresh approach. Let's keep this issue open.

@hadley
Copy link
Member

hadley commented Apr 14, 2022

Yeah, you only need to get past the first two arguments for it to work:

library(ggplot2)

col_aes <- aes(colour = gear)
aes(,,!!!col_aes)
#> Aesthetic mapping: 
#> * `colour` -> `gear`

Created on 2022-04-14 by the reprex package (v2.0.1)

I'd rather support !!! fully than add some new feature, and I'm worried that adding more + methods will have unintended consequences due to the challenge of correctly implementing double-dispatch generics in S3.

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

4 participants