From b7e11e1e22dca7199daefb884fb66ecfe21be499 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 2 Oct 2025 15:50:59 +0200 Subject: [PATCH 1/5] adapt `position_jitterdodge()` for parity with `position_dodge()` --- R/position-jitterdodge.R | 24 +++++++++++++++++++----- man/position_jitterdodge.Rd | 4 ++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/R/position-jitterdodge.R b/R/position-jitterdodge.R index 35fed2cd72..fa10e438ec 100644 --- a/R/position-jitterdodge.R +++ b/R/position-jitterdodge.R @@ -21,6 +21,7 @@ #' geom_point(pch = 21, position = position_jitterdodge()) position_jitterdodge <- function(jitter.width = NULL, jitter.height = 0, dodge.width = 0.75, reverse = FALSE, + preserve = "total", seed = NA) { if (!is.null(seed) && is.na(seed)) { seed <- sample.int(.Machine$integer.max, 1L) @@ -31,6 +32,7 @@ position_jitterdodge <- function(jitter.width = NULL, jitter.height = 0, jitter.width = jitter.width, jitter.height = jitter.height, dodge.width = dodge.width, + preserve = arg_match0(preserve, c("total", "single")), reverse = reverse, seed = seed ) @@ -45,6 +47,8 @@ PositionJitterdodge <- ggproto("PositionJitterdodge", Position, jitter.height = NULL, dodge.width = NULL, reverse = NULL, + default_aes = aes(order = NULL), + preserve = "total", required_aes = c("x", "y"), @@ -53,27 +57,37 @@ PositionJitterdodge <- ggproto("PositionJitterdodge", Position, data <- flip_data(data, flipped_aes) width <- self$jitter.width %||% (resolution(data$x, zero = FALSE, TRUE) * 0.4) - ndodge <- vec_unique(data[c("group", "PANEL", "x")]) - ndodge <- vec_group_id(ndodge[c("PANEL", "x")]) - ndodge <- max(tabulate(ndodge, attr(ndodge, "n"))) + if (identical(self$preserve, "total")) { + n <- NULL + } else { + n <- vec_unique(data[c("group", "PANEL", "x")]) + n <- vec_group_id(n[c("PANEL", "x")]) + n <- max(tabulate(n, attr(n, "n"))) + } list( dodge.width = self$dodge.width %||% 0.75, jitter.height = self$jitter.height %||% 0, - jitter.width = width / (ndodge + 2), + jitter.width = width / ((n %||% 1) + 2), + n = n, seed = self$seed, flipped_aes = flipped_aes, reverse = self$reverse %||% FALSE ) }, + setup_data = function(self, data, params) { + PositionDodge$setup_data(data = data, params = params) + }, + compute_panel = function(data, params, scales) { data <- flip_data(data, params$flipped_aes) data <- collide( data, params$dodge.width, - "position_jitterdodge", + name = "position_jitterdodge", strategy = pos_dodge, + n = params$n, check.width = FALSE, reverse = !params$reverse # for consistency with `position_dodge2()` ) diff --git a/man/position_jitterdodge.Rd b/man/position_jitterdodge.Rd index ca5bb8e30c..150a34e4d6 100644 --- a/man/position_jitterdodge.Rd +++ b/man/position_jitterdodge.Rd @@ -9,6 +9,7 @@ position_jitterdodge( jitter.height = 0, dodge.width = 0.75, reverse = FALSE, + preserve = "total", seed = NA ) } @@ -24,6 +25,9 @@ the default \code{position_dodge()} width.} \item{reverse}{If \code{TRUE}, will reverse the default stacking order. This is useful if you're rotating both the plot and legend.} +\item{preserve}{Should dodging preserve the \code{"total"} width of all elements +at a position, or the width of a \code{"single"} element?} + \item{seed}{A random seed to make the jitter reproducible. Useful if you need to apply the same jitter twice, e.g., for a point and a corresponding label. From 8fce6cd6c8310d064c826ee7c8d2001f8b55d524 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 2 Oct 2025 16:02:12 +0200 Subject: [PATCH 2/5] add test --- tests/testthat/test-position-jitterdodge.R | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/testthat/test-position-jitterdodge.R b/tests/testthat/test-position-jitterdodge.R index fb3274e61a..96b3da1c2a 100644 --- a/tests/testthat/test-position-jitterdodge.R +++ b/tests/testthat/test-position-jitterdodge.R @@ -9,3 +9,24 @@ test_that("position_jitterdodge preserves widths", { rep(0.45, nrow(ld)) ) }) + +test_that("position_jitterdodge can preserve total or single width", { + + df <- data_frame(x = c("a", "b", "b"), y = 1:3) + + # Total + p <- ggplot(df, aes(x, y, group = y)) + + geom_point(position = position_jitterdodge( + preserve = "total", dodge.width = 1, + jitter.width = 0, jitter.height = 0 + )) + expect_equal(get_layer_data(p)$x, new_mapped_discrete(c(1, 1.75, 2.25))) + + # Single + p <- ggplot(df, aes(x, y, group = y)) + + geom_point(position = position_jitterdodge( + preserve = "single", dodge.width = 1, + jitter.width = 0, jitter.height = 0 + )) + expect_equal(get_layer_data(p)$x, new_mapped_discrete(c(0.75, 1.75, 2.25))) +}) From 9000585ca13af8142e4aa448408cfcd7f6042637 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 2 Oct 2025 16:35:24 +0200 Subject: [PATCH 3/5] add news bullet --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 3e92547b2a..a1510d66ca 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,7 @@ ### Bug fixes +* Added `preserve` argument to `position_jitterdodge()` (@teunbrand, #6584). * Fixed regression where `draw_key_rect()` stopped using `fill` colours (@mitchelloharawild, #6609). * Fixed regression where `scale_{x,y}_*()` threw an error when an expression From 52036686a144b05ebef4a90546b6a48c5909892f Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 7 Oct 2025 11:14:42 +0200 Subject: [PATCH 4/5] jitter unflipped data, fix #6535 --- R/position-jitterdodge.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/position-jitterdodge.R b/R/position-jitterdodge.R index fa10e438ec..441e482512 100644 --- a/R/position-jitterdodge.R +++ b/R/position-jitterdodge.R @@ -91,7 +91,7 @@ PositionJitterdodge <- ggproto("PositionJitterdodge", Position, check.width = FALSE, reverse = !params$reverse # for consistency with `position_dodge2()` ) - data <- compute_jitter(data, params$jitter.width, params$jitter.height, params$seed) - flip_data(data, params$flipped_aes) + data <- flip_data(data, params$flipped_aes) + compute_jitter(data, params$jitter.width, params$jitter.height, params$seed) } ) From b2b2d801256f4376fe5771f05f33623b8de2832f Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 7 Oct 2025 15:40:05 +0200 Subject: [PATCH 5/5] add additional bullet --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index a1510d66ca..dcba539e14 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,8 @@ ### Bug fixes * Added `preserve` argument to `position_jitterdodge()` (@teunbrand, #6584). +* Fixed `position_jitterdodge(jitter.height, jitter.width)` applying to the + wrong dimension with flipped geoms (@teunbrand, #6535). * Fixed regression where `draw_key_rect()` stopped using `fill` colours (@mitchelloharawild, #6609). * Fixed regression where `scale_{x,y}_*()` threw an error when an expression