Skip to content

Commit 6298aec

Browse files
authored
Fix overlap identification in position_dodge2() (#5939)
* fix and vectorise `find_x_overlaps()` * add news bullet * add test * deal with `NA`s more properly * pass `end` correctly
1 parent 01eff23 commit 6298aec

File tree

3 files changed

+24
-9
lines changed

3 files changed

+24
-9
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@
125125
* `geom_hline()` and `geom_vline()` now have `position` argument
126126
(@yutannihilation, #4285).
127127
* New function `get_strip_labels()` to retrieve facet labels (@teunbrand, #4979)
128+
* Fixed bug in `position_dodge2()`'s identification of range overlaps
129+
(@teunbrand, #5938, #4327).
128130

129131
# ggplot2 3.5.1
130132

R/position-dodge2.R

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,19 @@ pos_dodge2 <- function(df, width, n = NULL, padding = 0.1) {
132132

133133
# Find groups of overlapping elements that need to be dodged from one another
134134
find_x_overlaps <- function(df) {
135-
overlaps <- numeric(nrow(df))
136-
overlaps[1] <- counter <- 1
137135

138-
for (i in seq_asc(2, nrow(df))) {
139-
if (is.na(df$xmin[i]) || is.na(df$xmax[i - 1]) || df$xmin[i] >= df$xmax[i - 1]) {
140-
counter <- counter + 1
141-
}
142-
overlaps[i] <- counter
143-
}
144-
overlaps
136+
start <- df$xmin
137+
nonzero <- df$xmax != df$xmin
138+
missing <- is.na(df$xmin) | is.na(df$xmax)
139+
start <- vec_fill_missing(start, "downup")
140+
end <- vec_fill_missing(df$xmax, "downup")
141+
142+
# For end we take largest end seen so far of previous observation
143+
end <- cummax(c(end[1], end[-nrow(df)]))
144+
# Start new group when 'start >= end' for non zero-width ranges
145+
# For zero-width ranges, start must be strictly larger than end
146+
overlaps <- cumsum(start > end | (start == end & nonzero))
147+
# Missing ranges always get separate group
148+
overlaps[missing] <- seq_len(sum(missing)) + max(overlaps, na.rm = TRUE)
149+
match(overlaps, unique0(overlaps))
145150
}

tests/testthat/test-position-dodge2.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,11 @@ test_that("groups are different when two blocks have externall touching point",{
118118
)
119119
expect_equal(find_x_overlaps(df1), seq_len(2))
120120
})
121+
122+
test_that("overlaps are identified correctly", {
123+
df <- data.frame(
124+
xmin = c(1, 2, 3, 5),
125+
xmax = c(4, 3, 4, 6)
126+
)
127+
expect_equal(find_x_overlaps(df), c(1, 1, 1, 2))
128+
})

0 commit comments

Comments
 (0)