Skip to content

Commit 7e67db3

Browse files
authored
Compute jitter width by panel (#6330)
* isolate jitter functionality * postpone invoking resolution * add test * append to related news bullet
1 parent 016b78f commit 7e67db3

File tree

4 files changed

+48
-39
lines changed

4 files changed

+48
-39
lines changed

NEWS.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@
249249
and (non-text) margins inherit from (@teunbrand, #5622).
250250
* `geom_ribbon()` can have varying `fill` or `alpha` in linear coordinate
251251
systems (@teunbrand, #4690).
252-
* `geom_tile()` computes default widths and heights per panel instead of
253-
per layer (@teunbrand, #5740).
252+
* `geom_tile()` and `position_jitter()` computes default widths and heights
253+
per panel instead of per layer (@teunbrand, #5740, #3722).
254254
* The `fill` of the `panel.border` theme setting is ignored and forced to be
255255
transparent (#5782).
256256
* `stat_align()` skips computation when there is only 1 group and therefore

R/position-jitter.R

+30-21
Original file line numberDiff line numberDiff line change
@@ -68,30 +68,39 @@ PositionJitter <- ggproto("PositionJitter", Position,
6868
seed <- self$seed
6969
}
7070
list(
71-
width = self$width %||% (resolution(data$x, zero = FALSE, TRUE) * 0.4),
72-
height = self$height %||% (resolution(data$y, zero = FALSE, TRUE) * 0.4),
71+
width = self$width,
72+
height = self$height,
7373
seed = seed
7474
)
7575
},
7676

77-
compute_layer = function(self, data, params, layout) {
78-
trans_x <- if (params$width > 0) function(x) jitter(x, amount = params$width)
79-
trans_y <- if (params$height > 0) function(x) jitter(x, amount = params$height)
80-
81-
# Make sure x and y jitter is only calculated once for all position aesthetics
82-
x_aes <- intersect(ggplot_global$x_aes, names(data))
83-
x <- if (length(x_aes) == 0) 0 else data[[x_aes[1]]]
84-
y_aes <- intersect(ggplot_global$y_aes, names(data))
85-
y <- if (length(y_aes) == 0) 0 else data[[y_aes[1]]]
86-
dummy_data <- data_frame0(x = x, y = y, .size = nrow(data))
87-
fixed_jitter <- with_seed_null(params$seed, transform_position(dummy_data, trans_x, trans_y))
88-
x_jit <- fixed_jitter$x - x
89-
y_jit <- fixed_jitter$y - y
90-
# Avoid nan values, if x or y has Inf values
91-
x_jit[is.infinite(x)] <- 0
92-
y_jit[is.infinite(y)] <- 0
93-
94-
# Apply jitter
95-
transform_position(data, function(x) x + x_jit, function(x) x + y_jit)
77+
compute_panel = function(self, data, params, scales) {
78+
compute_jitter(data, params$width, params$height, seed = params$seed)
9679
}
9780
)
81+
82+
compute_jitter <- function(data, width = NULL, height = NULL, seed = NA) {
83+
84+
width <- width %||% (resolution(data$x, zero = FALSE, TRUE) * 0.4)
85+
height <- height %||% (resolution(data$y, zero = FALSE, TRUE) * 0.4)
86+
87+
trans_x <- if (width > 0) function(x) jitter(x, amount = width)
88+
trans_y <- if (height > 0) function(x) jitter(x, amount = height)
89+
90+
x_aes <- intersect(ggplot_global$x_aes, names(data))
91+
x <- if (length(x_aes) == 0) 0 else data[[x_aes[1]]]
92+
93+
y_aes <- intersect(ggplot_global$y_aes, names(data))
94+
y <- if (length(y_aes) == 0) 0 else data[[y_aes[1]]]
95+
96+
jitter <- data_frame0(x = x, y = y, .size = nrow(data))
97+
jitter <- with_seed_null(seed, transform_position(jitter, trans_x, trans_y))
98+
99+
x_jit <- jitter$x - x
100+
x_jit[is.infinite(x)] <- 0
101+
102+
y_jit <- jitter$y - y
103+
y_jit[is.infinite(y)] <- 0
104+
105+
transform_position(data, function(x) x + x_jit, function(x) x + y_jit)
106+
}

R/position-jitterdodge.R

+1-16
Original file line numberDiff line numberDiff line change
@@ -77,22 +77,7 @@ PositionJitterdodge <- ggproto("PositionJitterdodge", Position,
7777
check.width = FALSE,
7878
reverse = !params$reverse # for consistency with `position_dodge2()`
7979
)
80-
81-
trans_x <- if (params$jitter.width > 0) function(x) jitter(x, amount = params$jitter.width)
82-
trans_y <- if (params$jitter.height > 0) function(x) jitter(x, amount = params$jitter.height)
83-
84-
x_aes <- intersect(ggplot_global$x_aes, names(data))
85-
y_aes <- intersect(ggplot_global$y_aes, names(data))
86-
87-
x <- if (length(x_aes) == 0) 0 else data[[x_aes[1]]]
88-
y <- if (length(y_aes) == 0) 0 else data[[y_aes[1]]]
89-
dummy_data <- data_frame0(x = x, y = y, .size = nrow(data))
90-
91-
fixed_jitter <- with_seed_null(params$seed, transform_position(dummy_data, trans_x, trans_y))
92-
x_jit <- fixed_jitter$x - x
93-
y_jit <- fixed_jitter$y - y
94-
95-
data <- transform_position(data, function(x) x + x_jit, function(x) x + y_jit)
80+
data <- compute_jitter(data, params$jitter.width, params$jitter.height, params$seed)
9681
flip_data(data, params$flipped_aes)
9782
}
9883
)

tests/testthat/test-position-jitter.R

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
test_that("automatic jitter width considers panels", {
2+
3+
df <- data.frame(x = c(1, 2, 100, 200), f = c("A", "A", "B", "B"))
4+
5+
auto <- position_jitter(seed = 0)
6+
fixed <- position_jitter(seed = 0, width = 0.5)
7+
8+
p <- ggplot(df, aes(x, 1)) + facet_wrap(vars(f))
9+
10+
fixed <- layer_data(p + geom_point(position = fixed))$x - df$x
11+
auto <- layer_data(p + geom_point(position = auto))$x - df$x
12+
13+
# Magic number 0.4 comes from default resolution multiplier
14+
expect_equal(fixed / 0.5, auto / c(0.4, 0.4, 40, 40))
15+
})

0 commit comments

Comments
 (0)