-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Aesthetics for position adjustments #6100
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
Conversation
@teunbrand As you're working on this, I'd suggest you also look at the long history of issues with misplaced boxplots or violins when categories are missing. I realized long ago that the only way to truly fix this is to somehow have position adjustments be aware of aesthetics. Not sure whether your current work can address this, but conceptually it seems related. See for example the long discussion here: #3022, and various other issues quoted therein. Also, here is a reprex that highlights the issue. There is no way I am aware of that can properly format and place boxplots when categories are missing. library(ggplot2)
# boxes for water polo, netball, and gymnastics have the wrong size
ggplot(ggridges::Aus_athletes, aes(height, sport, fill = sex)) +
geom_boxplot() # box for water polo is in the wrong location
ggplot(ggridges::Aus_athletes, aes(height, sport, fill = sex)) +
geom_boxplot(position = position_dodge(preserve = "single")) # boxes for water polo, netball, and gymnastics are in the wrong location
ggplot(ggridges::Aus_athletes, aes(height, sport, fill = sex)) +
geom_boxplot(position = position_dodge2(preserve = "single")) Created on 2024-09-12 with reprex v2.0.2 Let me know if you think I should open a new issue for this. I don't think we have one currently, but am not entirely certain. |
Hi @clauswilke, I think that the issue you're highlight is fundamentally the same as #3345. I have pondered them, but haven't been able to come up with a good solution for them (yet). In any case, that issue will not be fixed by this PR. |
Yes, agreed, it's the same as #3345. |
On second thought, this PR might be able to fix it if we have something like an |
I've added an As an example without devtools::load_all("~/packages/ggplot2")
#> ℹ Loading ggplot2
set.seed(0)
df <- data.frame(
x = rep(rep(LETTERS[1:7], c(3, 2, 2, 2, 1, 1, 1)), each = 10),
y = rnorm(120),
group = rep(c("X", "Y", "Z")[c(1:3, 1:2, 1, 3, 2:3, 1:3)], each = 10)
)
ggplot(df, aes(x, y, fill = group)) +
geom_boxplot(position = position_dodge(preserve = "single")) Now with the last_plot() + aes(order = group) Created on 2024-09-18 with reprex v2.1.1 |
There isn't anything I'd like to add at this moment, so I'm marking this as ready for review |
use_defaults = function(self, data, params = list()) { | ||
|
||
aes <- self$aesthetics() | ||
defaults <- self$default_aes | ||
|
||
params <- params[intersect(names(params), aes)] | ||
params <- params[setdiff(names(params), names(data))] | ||
defaults <- defaults[setdiff(names(defaults), c(names(params), names(data)))] | ||
|
||
if ((length(params) + length(defaults)) < 1) { | ||
return(data) | ||
} | ||
|
||
new <- compact(lapply(defaults, eval_tidy, data = data)) | ||
new[names(params)] <- params | ||
check_aesthetics(new, nrow(data)) | ||
|
||
data[names(new)] <- new | ||
data | ||
|
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.
There are some restrictions here that don't apply to the classic aesthetics, right? You can't use after_scale()
for instance. Is that somehow taken care of?
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.
Any surviving modifiers would be ignored. after_stat()
is already evaluated, so that isn't really a concern. after_scale()
doesn't really apply as position adjustments are computed before scales are applied. It will be interpreted as there is no such aesthetic, therefore the default value is substituted.
I'm not sure whether this was clear, but this does not replace Geom$use_defaults()
, it just evaluates the Position$default_aes
really.
@@ -104,7 +106,10 @@ PositionDodge <- ggproto("PositionDodge", Position, | |||
preserve = "total", | |||
orientation = "x", | |||
reverse = NULL, | |||
default_aes = aes(order = NULL), |
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.
I can be a bit worried that order
is so general that it conflicts with aesthetics from extension packages. Have you looked into that?
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.
It is hard to search for, but I can confidently tell you that no package on CRAN has a scale_order_*()
function. I've personally never seen an extension package use an order
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.
LGTM
Merge branch 'main' into position_aesthetics # Conflicts: # R/utilities-help.R
This PR aims to fix #3026, fix #5445 and fix #3022.
Briefly, it adds the ability for position adjustments to declare their own aesthetics. It is currently only implemented for
position_nudge()
.On any layer where
position = "nudge"
, one can usenudge_x
andnudge_y
either as a mapped aesthetic insideaes()
, or static aesthetic passed throughlayer(params)
.A few examples:
Created on 2024-09-12 with reprex v2.1.1