From 958fc445227aec4404607d58302133a8d2f1ba60 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:39:11 +0100 Subject: [PATCH 1/9] transform midpoint --- R/scale-gradient.R | 20 +++++++++++++++----- R/scale-steps.R | 18 ++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/R/scale-gradient.R b/R/scale-gradient.R index 95ee2824b2..2c01832ec6 100644 --- a/R/scale-gradient.R +++ b/R/scale-gradient.R @@ -95,26 +95,36 @@ scale_fill_gradient <- function(..., low = "#132B43", high = "#56B1F7", space = #' @rdname scale_gradient #' @export scale_colour_gradient2 <- function(..., low = muted("red"), mid = "white", high = muted("blue"), - midpoint = 0, space = "Lab", na.value = "grey50", guide = "colourbar", + midpoint = 0, space = "Lab", na.value = "grey50", + trans = "identity", guide = "colourbar", aesthetics = "colour") { + trans <- as.trans(trans) + trans_mid <- trans$transform(midpoint) + check_transformation(midpoint, trans_mid, trans$name, call = expr(midpoint)) + continuous_scale( aesthetics, palette = div_gradient_pal(low, mid, high, space), - na.value = na.value, guide = guide, ..., - rescaler = mid_rescaler(mid = midpoint) + na.value = na.value, trans = trans, guide = guide, ..., + rescaler = mid_rescaler(mid = trans_mid) ) } #' @rdname scale_gradient #' @export scale_fill_gradient2 <- function(..., low = muted("red"), mid = "white", high = muted("blue"), - midpoint = 0, space = "Lab", na.value = "grey50", guide = "colourbar", + midpoint = 0, space = "Lab", na.value = "grey50", + trans = "identity", guide = "colourbar", aesthetics = "fill") { + trans <- as.trans(trans) + trans_mid <- trans$transform(midpoint) + check_transformation(midpoint, trans_mid, trans$name, call = expr(midpoint)) + continuous_scale( aesthetics, palette = div_gradient_pal(low, mid, high, space), na.value = na.value, guide = guide, ..., - rescaler = mid_rescaler(mid = midpoint) + rescaler = mid_rescaler(mid = trans_mid) ) } diff --git a/R/scale-steps.R b/R/scale-steps.R index 5bbba07cb9..146503a6ed 100644 --- a/R/scale-steps.R +++ b/R/scale-steps.R @@ -52,10 +52,15 @@ scale_colour_steps <- function(..., low = "#132B43", high = "#56B1F7", space = " #' @rdname scale_steps #' @export scale_colour_steps2 <- function(..., low = muted("red"), mid = "white", high = muted("blue"), - midpoint = 0, space = "Lab", na.value = "grey50", guide = "coloursteps", + midpoint = 0, space = "Lab", na.value = "grey50", + trans = "identity", guide = "coloursteps", aesthetics = "colour") { + trans <- as.trans(trans) + trans_mid <- trans$transform(midpoint) + check_transformation(midpoint, trans_mid, trans$name, call = expr(midpoint)) + binned_scale(aesthetics, palette = div_gradient_pal(low, mid, high, space), - na.value = na.value, guide = guide, rescaler = mid_rescaler(mid = midpoint), ...) + na.value = na.value, guide = guide, rescaler = mid_rescaler(mid = trans_mid), ...) } #' @rdname scale_steps #' @export @@ -75,10 +80,15 @@ scale_fill_steps <- function(..., low = "#132B43", high = "#56B1F7", space = "La #' @rdname scale_steps #' @export scale_fill_steps2 <- function(..., low = muted("red"), mid = "white", high = muted("blue"), - midpoint = 0, space = "Lab", na.value = "grey50", guide = "coloursteps", + midpoint = 0, space = "Lab", na.value = "grey50", + trans = "identity", guide = "coloursteps", aesthetics = "fill") { + trans <- as.trans(trans) + trans_mid <- trans$transform(midpoint) + check_transformation(midpoint, trans_mid, trans$name, call = expr(midpoint)) + binned_scale(aesthetics, palette = div_gradient_pal(low, mid, high, space), - na.value = na.value, guide = guide, rescaler = mid_rescaler(mid = midpoint), ...) + na.value = na.value, guide = guide, rescaler = mid_rescaler(mid = trans_mid), ...) } #' @rdname scale_steps #' @export From 1ad3b3f28a9a666bd2b75099ca2e54685384e80e Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:39:18 +0100 Subject: [PATCH 2/9] add test --- tests/testthat/test-scale-gradient.R | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/testthat/test-scale-gradient.R b/tests/testthat/test-scale-gradient.R index fafa2226fe..8e661dc0fa 100644 --- a/tests/testthat/test-scale-gradient.R +++ b/tests/testthat/test-scale-gradient.R @@ -9,3 +9,19 @@ test_that("points outside the limits are plotted as NA", { correct_fill <- c("#B26D65", "#DCB4AF", "orange") expect_equal(layer_data(p)$fill, correct_fill) }) + +test_that("midpoints are transformed", { + + scale <- scale_colour_gradient2(midpoint = 1, trans = "identity") + scale$train(c(0, 3)) + expect_equal(scale$rescale(c(0, 3)), c(0.25, 1)) + + scale <- scale_colour_gradient2(midpoint = 10, trans = "log10") + scale$train(scale$transform(c(0, 1000))) + ans <- scale$rescale(c(0, 3), c(0.25, 1)) + + expect_warning( + scale_colour_gradient2(midpoint = 0, trans = "log10"), + "introduced infinite values" + ) +}) From 799280ceadbe6885fb611f37714a0e2a9dd44de1 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:48:58 +0100 Subject: [PATCH 3/9] check_transformation takes an argument name to report --- R/scale-.R | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/R/scale-.R b/R/scale-.R index eb4248048d..70262119ab 100644 --- a/R/scale-.R +++ b/R/scale-.R @@ -1286,13 +1286,17 @@ scale_flip_position <- function(scale) { invisible() } -check_transformation <- function(x, transformed, name, call = NULL) { - if (any(is.finite(x) != is.finite(transformed))) { - cli::cli_warn( - "{.field {name}} transformation introduced infinite values.", - call = call - ) +check_transformation <- function(x, transformed, name, arg = NULL, call = NULL) { + if (!any(is.finite(x) != is.finite(transformed))) { + return(invisible()) + } + if (is.null(arg)) { + end <- "." + } else { + end <- paste0(" in {.arg {arg}}.") } + msg <- paste0("{.field {name}} transformation introduced infinite values", end) + cli::cli_warn(msg, call = call) } trans_support_nbreaks <- function(trans) { From 17bbfb114dbb440d19a0a5fd1859b95f73da8353 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:49:29 +0100 Subject: [PATCH 4/9] handle transformation in `mid_rescaler()` --- R/scale-gradient.R | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/R/scale-gradient.R b/R/scale-gradient.R index 2c01832ec6..c8759acb8e 100644 --- a/R/scale-gradient.R +++ b/R/scale-gradient.R @@ -128,9 +128,16 @@ scale_fill_gradient2 <- function(..., low = muted("red"), mid = "white", high = ) } -mid_rescaler <- function(mid) { +mid_rescaler <- function(mid, trans = "identity", + arg = caller_arg(mid), call = caller_env()) { + trans <- as.trans(trans) + trans_mid <- trans$transform(mid) + check_transformation( + mid, trans_mid, trans$name, + arg = arg, call = call + ) function(x, to = c(0, 1), from = range(x, na.rm = TRUE)) { - rescale_mid(x, to, from, mid) + rescale_mid(x, to, from, trans_mid) } } From ac3501d4f5d00765c9e004a45e89d30122a72681 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:49:56 +0100 Subject: [PATCH 5/9] repeat less --- R/scale-gradient.R | 14 +++----------- R/scale-steps.R | 12 ++++-------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/R/scale-gradient.R b/R/scale-gradient.R index c8759acb8e..fe3ccefb7c 100644 --- a/R/scale-gradient.R +++ b/R/scale-gradient.R @@ -98,15 +98,11 @@ scale_colour_gradient2 <- function(..., low = muted("red"), mid = "white", high midpoint = 0, space = "Lab", na.value = "grey50", trans = "identity", guide = "colourbar", aesthetics = "colour") { - trans <- as.trans(trans) - trans_mid <- trans$transform(midpoint) - check_transformation(midpoint, trans_mid, trans$name, call = expr(midpoint)) - continuous_scale( aesthetics, palette = div_gradient_pal(low, mid, high, space), na.value = na.value, trans = trans, guide = guide, ..., - rescaler = mid_rescaler(mid = trans_mid) + rescaler = mid_rescaler(mid = midpoint, trans = trans) ) } @@ -116,15 +112,11 @@ scale_fill_gradient2 <- function(..., low = muted("red"), mid = "white", high = midpoint = 0, space = "Lab", na.value = "grey50", trans = "identity", guide = "colourbar", aesthetics = "fill") { - trans <- as.trans(trans) - trans_mid <- trans$transform(midpoint) - check_transformation(midpoint, trans_mid, trans$name, call = expr(midpoint)) - continuous_scale( aesthetics, palette = div_gradient_pal(low, mid, high, space), - na.value = na.value, guide = guide, ..., - rescaler = mid_rescaler(mid = trans_mid) + na.value = na.value, trans = trans, guide = guide, ..., + rescaler = mid_rescaler(mid = midpoint, trans = trans) ) } diff --git a/R/scale-steps.R b/R/scale-steps.R index 146503a6ed..9e5f148936 100644 --- a/R/scale-steps.R +++ b/R/scale-steps.R @@ -55,12 +55,10 @@ scale_colour_steps2 <- function(..., low = muted("red"), mid = "white", high = m midpoint = 0, space = "Lab", na.value = "grey50", trans = "identity", guide = "coloursteps", aesthetics = "colour") { - trans <- as.trans(trans) - trans_mid <- trans$transform(midpoint) - check_transformation(midpoint, trans_mid, trans$name, call = expr(midpoint)) binned_scale(aesthetics, palette = div_gradient_pal(low, mid, high, space), - na.value = na.value, guide = guide, rescaler = mid_rescaler(mid = trans_mid), ...) + na.value = na.value, trans = trans, guide = guide, + rescaler = mid_rescaler(mid = midpoint, trans = trans), ...) } #' @rdname scale_steps #' @export @@ -83,12 +81,10 @@ scale_fill_steps2 <- function(..., low = muted("red"), mid = "white", high = mut midpoint = 0, space = "Lab", na.value = "grey50", trans = "identity", guide = "coloursteps", aesthetics = "fill") { - trans <- as.trans(trans) - trans_mid <- trans$transform(midpoint) - check_transformation(midpoint, trans_mid, trans$name, call = expr(midpoint)) binned_scale(aesthetics, palette = div_gradient_pal(low, mid, high, space), - na.value = na.value, guide = guide, rescaler = mid_rescaler(mid = trans_mid), ...) + na.value = na.value, trans = trans, guide = guide, + rescaler = mid_rescaler(mid = midpoint, trans = trans), ...) } #' @rdname scale_steps #' @export From f1139d97b877e4117daeaa2963ddd847bdd29e65 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:54:06 +0100 Subject: [PATCH 6/9] add news bullet --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index a15a099b5c..9a39263527 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # ggplot2 (development version) +* (breaking) In the `scale_{colour/fill}_gradient2()` and + `scale_{colour/fill}_steps2()` functions, the `midpoint` argument is + transformed by the scale transformation (#3198). + * In the theme element hierarchy, parent elements that are a strict subclass of child elements now confer their subclass upon the children (#5457). From 1f2d38585ae496ab0a39f9ba16ef1276c8c6fd29 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Mon, 13 Nov 2023 12:55:28 +0100 Subject: [PATCH 7/9] redocument --- R/scale-gradient.R | 1 + man/scale_gradient.Rd | 27 +++++++++++++++------------ man/scale_steps.Rd | 27 +++++++++++++++------------ 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/R/scale-gradient.R b/R/scale-gradient.R index fe3ccefb7c..1ca4291cd2 100644 --- a/R/scale-gradient.R +++ b/R/scale-gradient.R @@ -90,6 +90,7 @@ scale_fill_gradient <- function(..., low = "#132B43", high = "#56B1F7", space = } #' @inheritParams scales::div_gradient_pal +#' @inheritParams continuous_scale #' @param midpoint The midpoint (in data value) of the diverging scale. #' Defaults to 0. #' @rdname scale_gradient diff --git a/man/scale_gradient.Rd b/man/scale_gradient.Rd index 35d57f2b68..98f541b08f 100644 --- a/man/scale_gradient.Rd +++ b/man/scale_gradient.Rd @@ -46,6 +46,7 @@ scale_colour_gradient2( midpoint = 0, space = "Lab", na.value = "grey50", + trans = "identity", guide = "colourbar", aesthetics = "colour" ) @@ -58,6 +59,7 @@ scale_fill_gradient2( midpoint = 0, space = "Lab", na.value = "grey50", + trans = "identity", guide = "colourbar", aesthetics = "fill" ) @@ -159,18 +161,6 @@ bounds values with \code{NA}. \item \code{\link[scales:oob]{scales::squish()}} for squishing out of bounds values into range. \item \code{\link[scales:oob]{scales::squish_infinite()}} for squishing infinite values into range. }} - \item{\code{trans}}{For continuous scales, the name of a transformation object -or the object itself. Built-in transformations include "asn", "atanh", -"boxcox", "date", "exp", "hms", "identity", "log", "log10", "log1p", "log2", -"logit", "modulus", "probability", "probit", "pseudo_log", "reciprocal", -"reverse", "sqrt" and "time". - -A transformation object bundles together a transform, its inverse, -and methods for generating breaks and labels. Transformation objects -are defined in the scales package, and are called \verb{_trans}. If -transformations require arguments, you can call them from the scales -package, e.g. \code{\link[scales:boxcox_trans]{scales::boxcox_trans(p = 2)}}. -You can create your own transformation with \code{\link[scales:trans_new]{scales::trans_new()}}.} \item{\code{expand}}{For position scales, a vector of range expansion constants used to add some padding around the data to ensure that they are placed some distance away from the axes. Use the convenience function \code{\link[=expansion]{expansion()}} @@ -203,6 +193,19 @@ same time, via \code{aesthetics = c("colour", "fill")}.} \item{midpoint}{The midpoint (in data value) of the diverging scale. Defaults to 0.} +\item{trans}{For continuous scales, the name of a transformation object +or the object itself. Built-in transformations include "asn", "atanh", +"boxcox", "date", "exp", "hms", "identity", "log", "log10", "log1p", "log2", +"logit", "modulus", "probability", "probit", "pseudo_log", "reciprocal", +"reverse", "sqrt" and "time". + +A transformation object bundles together a transform, its inverse, +and methods for generating breaks and labels. Transformation objects +are defined in the scales package, and are called \verb{_trans}. If +transformations require arguments, you can call them from the scales +package, e.g. \code{\link[scales:boxcox_trans]{scales::boxcox_trans(p = 2)}}. +You can create your own transformation with \code{\link[scales:trans_new]{scales::trans_new()}}.} + \item{colours, colors}{Vector of colours to use for n-colour gradient.} \item{values}{if colours should not be evenly positioned along the gradient diff --git a/man/scale_steps.Rd b/man/scale_steps.Rd index 4ce18b6839..60764819af 100644 --- a/man/scale_steps.Rd +++ b/man/scale_steps.Rd @@ -30,6 +30,7 @@ scale_colour_steps2( midpoint = 0, space = "Lab", na.value = "grey50", + trans = "identity", guide = "coloursteps", aesthetics = "colour" ) @@ -63,6 +64,7 @@ scale_fill_steps2( midpoint = 0, space = "Lab", na.value = "grey50", + trans = "identity", guide = "coloursteps", aesthetics = "fill" ) @@ -142,18 +144,6 @@ bounds values with \code{NA}. \item \code{\link[scales:oob]{scales::squish()}} for squishing out of bounds values into range. \item \code{\link[scales:oob]{scales::squish_infinite()}} for squishing infinite values into range. }} - \item{\code{trans}}{For continuous scales, the name of a transformation object -or the object itself. Built-in transformations include "asn", "atanh", -"boxcox", "date", "exp", "hms", "identity", "log", "log10", "log1p", "log2", -"logit", "modulus", "probability", "probit", "pseudo_log", "reciprocal", -"reverse", "sqrt" and "time". - -A transformation object bundles together a transform, its inverse, -and methods for generating breaks and labels. Transformation objects -are defined in the scales package, and are called \verb{_trans}. If -transformations require arguments, you can call them from the scales -package, e.g. \code{\link[scales:boxcox_trans]{scales::boxcox_trans(p = 2)}}. -You can create your own transformation with \code{\link[scales:trans_new]{scales::trans_new()}}.} \item{\code{expand}}{For position scales, a vector of range expansion constants used to add some padding around the data to ensure that they are placed some distance away from the axes. Use the convenience function \code{\link[=expansion]{expansion()}} @@ -186,6 +176,19 @@ same time, via \code{aesthetics = c("colour", "fill")}.} \item{midpoint}{The midpoint (in data value) of the diverging scale. Defaults to 0.} +\item{trans}{For continuous scales, the name of a transformation object +or the object itself. Built-in transformations include "asn", "atanh", +"boxcox", "date", "exp", "hms", "identity", "log", "log10", "log1p", "log2", +"logit", "modulus", "probability", "probit", "pseudo_log", "reciprocal", +"reverse", "sqrt" and "time". + +A transformation object bundles together a transform, its inverse, +and methods for generating breaks and labels. Transformation objects +are defined in the scales package, and are called \verb{_trans}. If +transformations require arguments, you can call them from the scales +package, e.g. \code{\link[scales:boxcox_trans]{scales::boxcox_trans(p = 2)}}. +You can create your own transformation with \code{\link[scales:trans_new]{scales::trans_new()}}.} + \item{colours, colors}{Vector of colours to use for n-colour gradient.} \item{values}{if colours should not be evenly positioned along the gradient From 46401b3b3adb7911b751578c31e0b82a312ab867 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Mon, 13 Nov 2023 12:59:51 +0100 Subject: [PATCH 8/9] Better call wiring --- R/scale-.R | 2 +- tests/testthat/test-scale-gradient.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/scale-.R b/R/scale-.R index 70262119ab..7696f51639 100644 --- a/R/scale-.R +++ b/R/scale-.R @@ -583,7 +583,7 @@ check_breaks_labels <- function(breaks, labels, call = NULL) { default_transform <- function(self, x) { new_x <- self$trans$transform(x) - check_transformation(x, new_x, self$trans$name, self$call) + check_transformation(x, new_x, self$trans$name, call = self$call) new_x } diff --git a/tests/testthat/test-scale-gradient.R b/tests/testthat/test-scale-gradient.R index 8e661dc0fa..01cbebd9ab 100644 --- a/tests/testthat/test-scale-gradient.R +++ b/tests/testthat/test-scale-gradient.R @@ -17,7 +17,7 @@ test_that("midpoints are transformed", { expect_equal(scale$rescale(c(0, 3)), c(0.25, 1)) scale <- scale_colour_gradient2(midpoint = 10, trans = "log10") - scale$train(scale$transform(c(0, 1000))) + scale$train(scale$transform(c(1, 1000))) ans <- scale$rescale(c(0, 3), c(0.25, 1)) expect_warning( From b383fa38434a3067eb229b70aa8a23c4e8c2ef94 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 14 Dec 2023 12:53:49 +0100 Subject: [PATCH 9/9] update trans -> transform --- tests/testthat/test-scale-gradient.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-scale-gradient.R b/tests/testthat/test-scale-gradient.R index 01cbebd9ab..d37cb6d80d 100644 --- a/tests/testthat/test-scale-gradient.R +++ b/tests/testthat/test-scale-gradient.R @@ -21,7 +21,7 @@ test_that("midpoints are transformed", { ans <- scale$rescale(c(0, 3), c(0.25, 1)) expect_warning( - scale_colour_gradient2(midpoint = 0, trans = "log10"), + scale_colour_gradient2(midpoint = 0, transform = "log10"), "introduced infinite values" ) })