From 9802209c8a5cc57470ebdb8f460f605bead34543 Mon Sep 17 00:00:00 2001 From: Elio Campitelli Date: Thu, 28 Oct 2021 17:06:53 -0300 Subject: [PATCH 1/3] Allows `breaks` to be a function in `geom_contour()` --- R/geom-contour.r | 9 ++++++--- R/stat-contour.r | 13 +++++++++---- man/geom_contour.Rd | 11 ++++++++--- man/geom_density_2d.Rd | 11 ++++++++--- tests/testthat/test-stat-contour.R | 3 ++- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/R/geom-contour.r b/R/geom-contour.r index cc25a70da4..07d49ea9bd 100644 --- a/R/geom-contour.r +++ b/R/geom-contour.r @@ -19,9 +19,12 @@ #' @inheritParams geom_path #' @param bins Number of contour bins. Overridden by `binwidth`. #' @param binwidth The width of the contour bins. Overridden by `breaks`. -#' @param breaks Numeric vector to set the contour breaks. Overrides `binwidth` -#' and `bins`. By default, this is a vector of length ten with [pretty()] -#' breaks. +#' @param breaks One of: +#' - Numeric vector to set the contour breaks +#' - A function that takes the range of the data and binwidth as input +#' and returns breaks as output. +#' Overrides `binwidth` and `bins`. By default, this is a vector of length +#' ten with [pretty()] breaks. #' @seealso [geom_density_2d()]: 2d density contours #' @export #' @examples diff --git a/R/stat-contour.r b/R/stat-contour.r index c96d69c673..e801141a7a 100644 --- a/R/stat-contour.r +++ b/R/stat-contour.r @@ -144,10 +144,15 @@ StatContourFilled <- ggproto("StatContourFilled", Stat, #' @noRd #' contour_breaks <- function(z_range, bins = NULL, binwidth = NULL, breaks = NULL) { - if (!is.null(breaks)) { + if (is.numeric(breaks)) { return(breaks) } + breaks_fun <- fullseq + if (is.function(breaks)) { + breaks_fun <- breaks + } + # If no parameters set, use pretty bins if (is.null(bins) && is.null(binwidth)) { breaks <- pretty(z_range, 10) @@ -167,20 +172,20 @@ contour_breaks <- function(z_range, bins = NULL, binwidth = NULL, breaks = NULL) } binwidth <- diff(z_range) / (bins - 1) - breaks <- fullseq(z_range, binwidth) + breaks <- breaks_fun(z_range, binwidth) # Sometimes the above sequence yields one bin too few. # If this happens, try again. if (length(breaks) < bins + 1) { binwidth <- diff(z_range) / bins - breaks <- fullseq(z_range, binwidth) + breaks <- breaks_fun(z_range, binwidth) } return(breaks) } # if we haven't returned yet, compute breaks from binwidth - fullseq(z_range, binwidth) + breaks_fun(z_range, binwidth) } #' Compute isoband objects diff --git a/man/geom_contour.Rd b/man/geom_contour.Rd index 1277d9b84b..21096862ad 100644 --- a/man/geom_contour.Rd +++ b/man/geom_contour.Rd @@ -102,9 +102,14 @@ to the paired geom/stat.} \item{binwidth}{The width of the contour bins. Overridden by \code{breaks}.} -\item{breaks}{Numeric vector to set the contour breaks. Overrides \code{binwidth} -and \code{bins}. By default, this is a vector of length ten with \code{\link[=pretty]{pretty()}} -breaks.} +\item{breaks}{One of: +\itemize{ +\item Numeric vector to set the contour breaks +\item A function that takes the range of the data and binwidth as input +and returns breaks as output. +Overrides \code{binwidth} and \code{bins}. By default, this is a vector of length +ten with \code{\link[=pretty]{pretty()}} breaks. +}} \item{lineend}{Line end style (round, butt, square).} diff --git a/man/geom_density_2d.Rd b/man/geom_density_2d.Rd index c0daf09211..2c6e895a89 100644 --- a/man/geom_density_2d.Rd +++ b/man/geom_density_2d.Rd @@ -99,9 +99,14 @@ a call to a position adjustment function.} \describe{ \item{\code{bins}}{Number of contour bins. Overridden by \code{binwidth}.} \item{\code{binwidth}}{The width of the contour bins. Overridden by \code{breaks}.} - \item{\code{breaks}}{Numeric vector to set the contour breaks. Overrides \code{binwidth} -and \code{bins}. By default, this is a vector of length ten with \code{\link[=pretty]{pretty()}} -breaks.} + \item{\code{breaks}}{One of: +\itemize{ +\item Numeric vector to set the contour breaks +\item A function that takes the range of the data and binwidth as input +and returns breaks as output. +Overrides \code{binwidth} and \code{bins}. By default, this is a vector of length +ten with \code{\link[=pretty]{pretty()}} breaks. +}} }} \item{contour_var}{Character string identifying the variable to contour diff --git a/tests/testthat/test-stat-contour.R b/tests/testthat/test-stat-contour.R index 61df774756..c5bd76016e 100644 --- a/tests/testthat/test-stat-contour.R +++ b/tests/testthat/test-stat-contour.R @@ -32,7 +32,7 @@ test_that("contouring irregularly spaced data works", { expect_setequal(d8$y, c(2, 20/9, 16/9)) }) -test_that("contour breaks can be set manually and by bins and binwidth", { +test_that("contour breaks can be set manually and by bins and binwidth and a function", { range <- c(0, 1) expect_equal(contour_breaks(range), pretty(range, 10)) expect_identical(contour_breaks(range, breaks = 1:3), 1:3) @@ -40,6 +40,7 @@ test_that("contour breaks can be set manually and by bins and binwidth", { # shifting the range by 0.2 hits another execution branch in contour_breaks() expect_length(contour_breaks(range + 0.2, bins = 5), 6) expect_equal(resolution(contour_breaks(range, binwidth = 0.3)), 0.3) + expect_equal(contour_breaks(range), contour_breaks(range, breaks = fullseq)) }) test_that("geom_contour_filled() and stat_contour_filled() result in identical layer data", { From 594bbe334418e02db953c33ca4f1e8353a34021d Mon Sep 17 00:00:00 2001 From: Elio Campitelli Date: Fri, 29 Oct 2021 09:37:05 -0300 Subject: [PATCH 2/3] Adds support for formula interface. --- R/geom-contour.r | 4 +++- R/stat-contour.r | 2 ++ man/geom_contour.Rd | 8 +++++--- man/geom_density_2d.Rd | 8 +++++--- tests/testthat/test-stat-contour.R | 1 + 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/R/geom-contour.r b/R/geom-contour.r index 07d49ea9bd..4cdb0cd234 100644 --- a/R/geom-contour.r +++ b/R/geom-contour.r @@ -22,7 +22,9 @@ #' @param breaks One of: #' - Numeric vector to set the contour breaks #' - A function that takes the range of the data and binwidth as input -#' and returns breaks as output. +#' and returns breaks as output. A function can be created from a formula +#' (e.g. ~ fullseq(.x, .y)). +#' #' Overrides `binwidth` and `bins`. By default, this is a vector of length #' ten with [pretty()] breaks. #' @seealso [geom_density_2d()]: 2d density contours diff --git a/R/stat-contour.r b/R/stat-contour.r index e801141a7a..695af32be9 100644 --- a/R/stat-contour.r +++ b/R/stat-contour.r @@ -144,6 +144,8 @@ StatContourFilled <- ggproto("StatContourFilled", Stat, #' @noRd #' contour_breaks <- function(z_range, bins = NULL, binwidth = NULL, breaks = NULL) { + breaks <- allow_lambda(breaks) + if (is.numeric(breaks)) { return(breaks) } diff --git a/man/geom_contour.Rd b/man/geom_contour.Rd index 21096862ad..ae5b40a987 100644 --- a/man/geom_contour.Rd +++ b/man/geom_contour.Rd @@ -106,10 +106,12 @@ to the paired geom/stat.} \itemize{ \item Numeric vector to set the contour breaks \item A function that takes the range of the data and binwidth as input -and returns breaks as output. +and returns breaks as output. A function can be created from a formula +(e.g. ~ fullseq(.x, .y)). +} + Overrides \code{binwidth} and \code{bins}. By default, this is a vector of length -ten with \code{\link[=pretty]{pretty()}} breaks. -}} +ten with \code{\link[=pretty]{pretty()}} breaks.} \item{lineend}{Line end style (round, butt, square).} diff --git a/man/geom_density_2d.Rd b/man/geom_density_2d.Rd index 2c6e895a89..d2961df949 100644 --- a/man/geom_density_2d.Rd +++ b/man/geom_density_2d.Rd @@ -103,10 +103,12 @@ a call to a position adjustment function.} \itemize{ \item Numeric vector to set the contour breaks \item A function that takes the range of the data and binwidth as input -and returns breaks as output. +and returns breaks as output. A function can be created from a formula +(e.g. ~ fullseq(.x, .y)). +} + Overrides \code{binwidth} and \code{bins}. By default, this is a vector of length -ten with \code{\link[=pretty]{pretty()}} breaks. -}} +ten with \code{\link[=pretty]{pretty()}} breaks.} }} \item{contour_var}{Character string identifying the variable to contour diff --git a/tests/testthat/test-stat-contour.R b/tests/testthat/test-stat-contour.R index c5bd76016e..a48a368931 100644 --- a/tests/testthat/test-stat-contour.R +++ b/tests/testthat/test-stat-contour.R @@ -41,6 +41,7 @@ test_that("contour breaks can be set manually and by bins and binwidth and a fun expect_length(contour_breaks(range + 0.2, bins = 5), 6) expect_equal(resolution(contour_breaks(range, binwidth = 0.3)), 0.3) expect_equal(contour_breaks(range), contour_breaks(range, breaks = fullseq)) + expect_equal(contour_breaks(range), contour_breaks(range, breaks = ~fullseq(.x, .y))) }) test_that("geom_contour_filled() and stat_contour_filled() result in identical layer data", { From a65aec1b7e99d6f85af9500fbf813ab8cd868a22 Mon Sep 17 00:00:00 2001 From: Elio Campitelli Date: Fri, 29 Oct 2021 10:01:50 -0300 Subject: [PATCH 3/3] Updates NEWS.md --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index 9364b75a31..446c928ba3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,8 @@ all `values` on the legend instead, use `scale_*_manual(values = vals, limits = names(vals))`. (@teunbrand, @banfai, #4511, #4534) + +* `geom_contour()` now accepts a function in the `breaks` argument (@eliocamp, #4652). # ggplot2 3.3.5 This is a very small release focusing on fixing a couple of untenable issues