Skip to content

Commit d72caf5

Browse files
authored
guide_coloursteps(even.steps = FALSE) works with discrete data (#5783)
* discard uneven steps warning * simplify `extract_decor()` * take literal breaks as `.value` if uneven steps * replace individual attribute with the parsed result * add test * add news bullet * fix parsed attribute bug
1 parent 125b1e0 commit d72caf5

File tree

4 files changed

+46
-33
lines changed

4 files changed

+46
-33
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
* When legends detect the presence of values in a layer, `NA` is now detected
3030
if the data contains values outside the given breaks (@teunbrand, #5749).
3131
* `annotate()` now warns about `stat` or `position` arguments (@teunbrand, #5151)
32+
* `guide_coloursteps(even.steps = FALSE)` now works with discrete data that has
33+
been formatted by `cut()` (@teunbrand, #3877).
3234

3335
# ggplot2 3.5.0
3436

R/guide-bins.R

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,7 @@ GuideBins <- ggproto(
326326
}
327327
)
328328

329-
parse_binned_breaks = function(scale, breaks = scale$get_breaks(),
330-
even.steps = TRUE) {
329+
parse_binned_breaks = function(scale, breaks = scale$get_breaks()) {
331330

332331
breaks <- breaks[!is.na(breaks)]
333332
if (length(breaks) == 0) {
@@ -343,12 +342,6 @@ parse_binned_breaks = function(scale, breaks = scale$get_breaks(),
343342
all_breaks <- unique0(c(limits[1], breaks, limits[2]))
344343
bin_at <- all_breaks[-1] - diff(all_breaks) / 2
345344
} else {
346-
if (isFALSE(even.steps)) {
347-
cli::cli_warn(paste0(
348-
"{.code even.steps = FALSE} is not supported when used with a ",
349-
"discrete scale."
350-
))
351-
}
352345
bin_at <- breaks
353346
nums <- as.character(breaks)
354347
nums <- strsplit(gsub("\\(|\\)|\\[|\\]", "", nums), ",\\s?")

R/guide-colorsteps.R

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,19 @@ GuideColoursteps <- ggproto(
9898
return(Guide$extract_key(scale, aesthetic))
9999
}
100100

101-
parsed <- parse_binned_breaks(scale, breaks, even.steps)
101+
parsed <- parse_binned_breaks(scale, breaks)
102102
if (is.null(parsed)) {
103103
return(parsed)
104104
}
105105
limits <- parsed$limits
106106
breaks <- parsed$breaks
107107

108108
key <- data_frame0(!!aesthetic := scale$map(breaks))
109-
key$.value <- seq_along(breaks)
109+
if (even.steps) {
110+
key$.value <- seq_along(breaks)
111+
} else {
112+
key$.value <- breaks
113+
}
110114
key$.label <- scale$get_labels(breaks)
111115

112116
if (breaks[1] %in% limits) {
@@ -117,35 +121,34 @@ GuideColoursteps <- ggproto(
117121
key[[1]][nrow(key)] <- NA
118122
}
119123
# To avoid having to recalculate these variables in other methods, we
120-
# attach these as attributes. It might not be very elegant, but it works.
121-
attr(key, "limits") <- parsed$limits
122-
attr(key, "bin_at") <- parsed$bin_at
123-
return(key)
124+
# attach the parsed values as attributes. It might not be very elegant,
125+
# but it works.
126+
attr(key, "parsed") <- parsed
127+
key
124128
},
125129

126130
extract_decor = function(scale, aesthetic, key,
127131
reverse = FALSE, even.steps = TRUE,
128132
nbin = 100, alpha = NA,...) {
133+
134+
parsed <- attr(key, "parsed")
135+
breaks <- parsed$breaks %||% scale$get_breaks()
136+
limits <- parsed$limits %||% scale$get_limits()
137+
138+
breaks <- sort(unique0(c(limits, breaks)))
139+
n <- length(breaks)
140+
bin_at <- parsed$bin_at %||% ((breaks[-1] + breaks[-n]) / 2)
141+
129142
if (even.steps) {
130-
bin_at <- attr(key, "bin_at", TRUE)
131-
bar <- data_frame0(
132-
colour = alpha(scale$map(bin_at), alpha),
133-
min = seq_along(bin_at) - 1,
134-
max = seq_along(bin_at),
135-
.size = length(bin_at)
136-
)
137-
} else {
138-
breaks <- unique(sort(c(scale$get_limits(), scale$get_breaks())))
139-
n <- length(breaks)
140-
bin_at <- (breaks[-1] + breaks[-n]) / 2
141-
bar <- data_frame0(
142-
colour = alpha(scale$map(bin_at), alpha),
143-
min = head(breaks, -1),
144-
max = tail(breaks, -1),
145-
.size = length(bin_at)
146-
)
143+
breaks <- seq_len(n) - 1L
147144
}
148-
return(bar)
145+
146+
data_frame0(
147+
colour = alpha(scale$map(bin_at), alpha),
148+
min = breaks[-n],
149+
max = breaks[-1],
150+
.size = length(bin_at)
151+
)
149152
},
150153

151154
extract_params = function(scale, params, direction = "vertical", title = waiver(), ...) {
@@ -166,7 +169,7 @@ GuideColoursteps <- ggproto(
166169

167170
if (show.limits) {
168171
key <- params$key
169-
limits <- attr(key, "limits", TRUE) %||% scale$get_limits()
172+
limits <- attr(key, "parsed")$limits %||% scale$get_limits()
170173
key <- key[c(NA, seq_len(nrow(key)), NA), , drop = FALSE]
171174
n <- nrow(key)
172175
key$.value[c(1, n)] <- range(params$decor$min, params$decor$max)

tests/testthat/test-guides.R

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,21 @@ test_that("guide_coloursteps and guide_bins return ordered breaks", {
302302
expect_true(all(diff(key$.value) > 0))
303303
})
304304

305+
test_that("guide_coloursteps can parse (un)even steps from discrete scales", {
306+
307+
val <- cut(1:10, breaks = c(0, 3, 5, 10), include.lowest = TRUE)
308+
scale <- scale_colour_viridis_d()
309+
scale$train(val)
310+
311+
g <- guide_coloursteps(even.steps = TRUE)
312+
decor <- g$train(scale = scale, aesthetics = "colour")$decor
313+
expect_equal(decor$max - decor$min, rep(1/3, 3))
314+
315+
g <- guide_coloursteps(even.steps = FALSE)
316+
decor <- g$train(scale = scale, aesthetics = "colour")$decor
317+
expect_equal(decor$max - decor$min, c(0.3, 0.2, 0.5))
318+
})
319+
305320

306321
test_that("guide_colourbar merging preserves both aesthetics", {
307322
# See issue 5324

0 commit comments

Comments
 (0)