-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Proposal: factory pattern for color scales #5471
base: main
Are you sure you want to change the base?
Conversation
I'm fine with this idea in general, but we are not going to add additional speciality scales for various colours so this PR will only be about removing code duplication in the current code base |
@@ -14,7 +14,7 @@ scale_colour_hue( | |||
h.start = 0, | |||
direction = 1, | |||
na.value = "grey50", | |||
aesthetics = "colour" | |||
aesthetics = aesthetic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can avoid this usage parameter to be misreported by explicitly setting the formal.
For example, the hue factory could be:
scale_colour_hue_factory <- function(aesthetic) {
fun <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0,
direction = 1, na.value = "grey50") {
discrete_scale(aesthetics, palette = hue_pal(h, c, l, h.start, direction),
na.value = na.value, ...)
}
formals(fun)$aesthetics <- aesthetic
fun
}
@thomasp85 That's fine. Would you be ok with exporting the factory functions, so people making extensions can add specialty color scales? As it stands, the logic for default color scales (what to do when a scale isn't specified) is locked away in ggplot. Also, a question: Can one of you explain or point me to info about how the |
I think the whole
That is how I understand that argument. It has been proposed to move away from this in favour of setting default scales in the |
@teunbrand Thanks. Yeah, I partly agree about using parameters, but I also see the value in hard-coded functions for autocomplete. |
I was thinking about this PR, and could we not generalise the idea away from multiple factories to a single function to copy a scale for another aesthetic? Quick sketch blind to any specifics: devtools::load_all("~/packages/ggplot2")
#> ℹ Loading ggplot2
reuse_scale <- function(scale, aesthetic, scale_arg = caller_arg(scale)) {
check_function(scale)
if (!"aesthetics" %in% fn_fmls_names(scale)) {
cli::cli_abort(c(
"The {.arg scale} function must have an {.arg aesthetics} argument.",
i = "{.fn {scale_arg}} does not have that argument."
))
}
formals(scale)$aesthetics <- aesthetic
scale
}
# Valid input
scale_fill_viridis_c <- reuse_scale(scale_colour_viridis_c, "fill")
# Bad input
reuse_scale(print, "fill")
#> Error in `reuse_scale()`:
#> ! The `scale` function must have an `aesthetics` argument.
#> ℹ `print()` does not have that argument.
reuse_scale("scale_fill_viridis", "colour")
#> Error in `reuse_scale()`:
#> ! `scale` must be a function, not the string "scale_fill_viridis". Created on 2023-10-12 with reprex v2.0.2 |
Yes! I've thinking about something similar but didn't know about Does that approach duplicate the documentation association? For example, if you copy |
No you'd still have to document them as before. Quick boilerplate: #' The foo scale
#'
#' @param aesthetics descriptions
#'
#' @return something
#' @export
#'
#' @examples
#' NULL
scale_foo_continuous <- function(aesthetics = "foo") {
print(aesthetics)
}
#' @rdname scale_foo_continuous
#' @export
scale_bar_continuous <- reuse_scale(scale_foo_continuous, "bar") |
Currently, each color scale (hue, grey, viridis, etc.) needs a duplicate function for each aesthetic it maps to. Here are the functions in
scale-grey.r
:Problems with that approach:
Why the
aesthetics
argument isn't a sufficient solution:A new color aesthetic still needs defaults that behave like the others. If you map a new aesthetic in the same way that you map
fill
, but fill uses default the color scale, they won't match. So you'll need to duplicate the scale functions for hue, gradient, and viridis just for defaults to match the behavior of color and fill.Proposed approach: Create factory methods
Instead of
scale_colour_grey()
andscale_fill_grey
defined above, create one function takes an aesthetic as a parameter and returns a scale function:This approach avoids duplicating the argument defaults. Also, adding another color aesthetic only requires one line of code per color scale:
TODOs