tidyverse/ggplot2

Could position_dodge(preserve = 'single') centre bars/boxs where not all categories are present like in dodge2?

Opened this issue ยท 11 comments

Might not be possible, but thought I'd chuck it up just in case

See how waterpolo and gymnastics categories are treated in dodge. I'd prefer they were centred like in dodge2

library(tidyverse)
library(patchwork)

sports <- c("water polo", "swimming", "gymnastics", "field", "netball")

p1 <- ggridges::Aus_athletes |>
  dplyr::filter(sport %in% sports) |>
  ggplot(aes(height, sport, fill = sex)) +
  geom_boxplot(position = position_dodge(preserve = "single")) +
  labs(title = "position_dodge(preserve = 'single')") +
  theme(legend.position = "none")

p2 <- ggridges::Aus_athletes |>
  dplyr::filter(sport %in% sports) |>
  ggplot(aes(height, sport, fill = sex)) +
  geom_boxplot(position = position_dodge2(preserve = "single")) +
  labs(title = "position_dodge2(preserve = 'single')") +
  theme(legend.position = "none")

p1 / p2

Created on 2025-09-15 with reprex v2.1.1

What is preventing you from using position_dodge2() in the first place?

In this example, you could.. but if you were making violins, you couldn't

library(patchwork)
library(tidyverse)

sports <- c("water polo", "swimming", "gymnastics", "field", "netball")

p1 <- ggridges::Aus_athletes |>
  dplyr::filter(sport %in% sports) |>
  ggplot(aes(height, sport, fill = sex)) +
  geom_violin(position = position_dodge(preserve = "single")) +
  labs(title = "position_dodge(preserve = 'single')") +
  theme(legend.position = "none")

p2 <- ggridges::Aus_athletes |>
  dplyr::filter(sport %in% sports) |>
  ggplot(aes(height, sport, fill = sex)) +
  geom_violin(position = position_dodge2(preserve = "single")) +
  labs(title = "position_dodge2(preserve = 'single')") +
  theme(legend.position = "none")

p1 / p2

Created on 2025-09-30 with reprex v2.1.1

Right, I think the geom_violin(position = "dodge2") is a known case, but I seem to have lost the exact issue. Are violin plots the only bad ones?

The current default behaviour of position_dodge(preserve = "single") is not consistent with position_dodge2(preserve = "single") or position_dodge(preserve = "total").

By default, dodge centres where preserve = "total", but does not where preserve = "single".

The help for preserve says: "Should dodging preserve the "total" width of all elements at a position, or the width of a "single" element?"

So nothing about changing it's positioning. Just about the width of it.
 
I also think centreing default behaviour is likely to be better most of the time.

Image

I also think centreing default behaviour is likely to be better most of the time.

I disagree here. You have all sorts of situations where this is an undesired default, such as #3022 & #3345.

I'm not considering changing the defaults, but I would consider making position_dodge2() work with violins or allowing a preserve = "single_centered" option.

That would be useful. Or maybe a different argument to control how stuff is aligned?

Still not getting in what situation anyone would want things from different categories all positioned to the left. I'm sure there is some situation, but I would that thought it would be edge-case

Image

Still not getting in what situation anyone would want things from different categories all positioned to the left.

In no situation would one want this. However, without further information, ggplot2 doesn't know that e.g. gymnastics and water polo are categorically different (due to sex). It only sees position and group, and the two are often conflated.

Ah okay. So to clarify, is the situation that they could not also be centred by default for position_dodge(preserve = "single") or you think that the amount of breakingness is not worth the change?

I think the breakage is not worth the change. The position adjustment system is delicate and people are attached to how they work.
You can always extend a position adjustment to work exactly as you need it to.

Fair enough. Will leave to you to close, or do what you want with this.

Thanks for all the ggplot2 work - v4 has so much awesome stuff!

I think the right think to do here is to explore if we can have an option like preserve = "centered". I'll open a second one for geom_violin(position = "dodge2")