Skip to content

Fix expression labels in guide_coloursteps() and guide_bins() #6007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 2, 2024
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ggplot2 (development version)

* Binned guides now accept expressions as labels (@teunbrand, #6005)
* (internal) `Scale$get_labels()` format expressions as lists.
* When discrete breaks have names, they'll be used as labels by default
(@teunbrand, #6147).
* The helper function `is.waiver()` is now exported to help extensions to work
Expand Down
7 changes: 1 addition & 6 deletions R/guide-.R
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,8 @@ Guide <- ggproto(

mapped <- scale$map(breaks)
labels <- scale$get_labels(breaks)
# {vctrs} doesn't play nice with expressions, convert to list.
# see also https://github.com/r-lib/vctrs/issues/559
if (is.expression(labels)) {
labels <- as.list(labels)
}

key <- data_frame(mapped, .name_repair = ~ aesthetic)
key <- data_frame(!!aesthetic := mapped)
key$.value <- breaks
key$.label <- labels

Expand Down
2 changes: 1 addition & 1 deletion R/guide-axis-theta.R
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ GuideAxisTheta <- ggproto(
# labels of these positions
ends_apart <- (key$theta[n] - key$theta[1]) %% (2 * pi)
if (n > 0 && ends_apart < 0.05 && !is.null(key$.label)) {
if (is.expression(key$.label)) {
if (is.expression(key$.label[[1]])) {
combined <- substitute(
paste(a, "/", b),
list(a = key$.label[[1]], b = key$.label[[n]])
Expand Down
4 changes: 2 additions & 2 deletions R/guide-bins.R
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ GuideBins <- ggproto(
key$.show <- NA

labels <- scale$get_labels(breaks)
if (is.character(scale$labels) || is.numeric(scale$labels)) {
if (is.character(scale$labels) || is.numeric(scale$labels) || is.expression(scale$labels)) {
limit_lab <- c(NA, NA)
} else {
limit_lab <- scale$get_labels(limits)
Expand Down Expand Up @@ -265,7 +265,7 @@ GuideBins <- ggproto(

list(labels = flip_element_grob(
elements$text,
label = key$.label,
label = validate_labels(key$.label),
x = unit(key$.value, "npc"),
margin_x = FALSE,
margin_y = TRUE,
Expand Down
7 changes: 2 additions & 5 deletions R/guide-colorsteps.R
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,8 @@ GuideColoursteps <- ggproto(
breaks <- parsed$breaks

key <- data_frame0(!!aesthetic := scale$map(breaks))
if (even.steps) {
key$.value <- seq_along(breaks)
} else {
key$.value <- breaks
}
fmt <- if (even.steps) seq_along else identity
key$.value <- fmt(breaks)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really better?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll revert this if you find the former more readable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I do

key$.label <- scale$get_labels(breaks)

if (breaks[1] %in% limits) {
Expand Down
60 changes: 27 additions & 33 deletions R/scale-.R
Original file line number Diff line number Diff line change
Expand Up @@ -861,12 +861,9 @@ ScaleContinuous <- ggproto("ScaleContinuous", Scale,
labels[lengths(labels) == 0] <- ""
# Make sure each element is scalar
labels <- lapply(labels, `[`, 1)

if (any(vapply(labels, is.language, logical(1)))) {
labels <- inject(expression(!!!labels))
} else {
labels <- unlist(labels)
}
}
if (is.expression(labels)) {
labels <- as.list(labels)
}

labels
Expand Down Expand Up @@ -1074,48 +1071,42 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
return(NULL)
}

if (is.null(self$labels)) {
labels <- self$labels
if (is.null(labels)) {
return(NULL)
}

if (identical(self$labels, NA)) {
if (identical(labels, NA)) {
cli::cli_abort(
"Invalid {.arg labels} specification. Use {.code NULL}, not {.code NA}.",
call = self$call
)
}

if (is.waiver(self$labels)) {
if (is.waiver(labels)) {
if (!is.null(names(breaks))) {
return(names(breaks))
}
if (is.numeric(breaks)) {
labels <- names(breaks)
} else if (is.numeric(breaks)) {
# Only format numbers, because on Windows, format messes up encoding
format(breaks, justify = "none")
labels <- format(breaks, justify = "none")
} else {
as.character(breaks)
labels <- as.character(breaks)
}
} else if (is.function(self$labels)) {
self$labels(breaks)
} else {
if (!is.null(names(self$labels))) {
# If labels have names, use them to match with breaks
labels <- breaks

map <- match(names(self$labels), labels, nomatch = 0)
labels[map] <- self$labels[map != 0]
labels
} else {
labels <- self$labels
} else if (is.function(labels)) {
labels <- labels(breaks)
} else if (!is.null(names(labels))) {
# If labels have names, use them to match with breaks
map <- match(names(self$labels), breaks, nomatch = 0)
labels <- replace(breaks, map, labels[map != 0])
} else if (!is.null(attr(breaks, "pos"))) {
# Need to ensure that if breaks were dropped, corresponding labels are too
labels <- labels[attr(breaks, "pos")]
}

# Need to ensure that if breaks were dropped, corresponding labels are too
pos <- attr(breaks, "pos")
if (!is.null(pos)) {
labels <- labels[pos]
}
labels
}
if (is.expression(labels)) {
labels <- as.list(labels)
}
labels
},

clone = function(self) {
Expand Down Expand Up @@ -1351,6 +1342,9 @@ ScaleBinned <- ggproto("ScaleBinned", Scale,
call = self$call
)
}
if (is.expression(labels)) {
labels <- as.list(labels)
}
labels
},

Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/test-scale-binned.R
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ test_that("binned limits should not compute out-of-bounds breaks", {
test_that("binned scales can use limits and transformations simultaneously (#6144)", {
s <- scale_x_binned(
limits = function(x) x + 1,
trans = transform_log10()
transform = transform_log10()
)
s$train(c(0, 1)) # c(1, 10) in untransformed space
out <- s$get_limits()
Expand Down