diff --git a/R/stat-bin.r b/R/stat-bin.r index 90e7d61a3d..e34ee01a77 100644 --- a/R/stat-bin.r +++ b/R/stat-bin.r @@ -23,6 +23,12 @@ #' @param breaks Alternatively, you can supply a numeric vector giving #' the bin boundaries. Overrides `binwidth`, `bins`, `center`, #' and `boundary`. +#' +#' Can also be a function that returns a numeric vector of bin boundaries +#' calculated from unscaled x. Here, "unscaled x" +#' refers to the original x values in the data, before application of any +#' scale transformation. When specifying a function along with a grouping +#' structure, the function will be called once per group. #' @param closed One of `"right"` or `"left"` indicating whether right #' or left edges of bins are included in the bin. #' @param pad If `TRUE`, adds empty bins at either end of x. This ensures @@ -143,6 +149,9 @@ StatBin <- ggproto("StatBin", Stat, width = NULL) { x <- flipped_names(flipped_aes)$x if (!is.null(breaks)) { + if (is.function(breaks)) { + breaks <- breaks(data[[x]]) + } if (!scales[[x]]$is_discrete()) { breaks <- scales[[x]]$transform(breaks) } diff --git a/man/geom_histogram.Rd b/man/geom_histogram.Rd index ab1cd32de5..636e8515e6 100644 --- a/man/geom_histogram.Rd +++ b/man/geom_histogram.Rd @@ -129,7 +129,13 @@ outside the range of the data.} \item{breaks}{Alternatively, you can supply a numeric vector giving the bin boundaries. Overrides \code{binwidth}, \code{bins}, \code{center}, -and \code{boundary}.} +and \code{boundary}. + +Can also be a function that returns a numeric vector of bin boundaries +calculated from unscaled x. Here, "unscaled x" +refers to the original x values in the data, before application of any +scale transformation. When specifying a function along with a grouping +structure, the function will be called once per group.} \item{closed}{One of \code{"right"} or \code{"left"} indicating whether right or left edges of bins are included in the bin.} diff --git a/man/stat_summary.Rd b/man/stat_summary.Rd index cf97b6d756..f300b78a26 100644 --- a/man/stat_summary.Rd +++ b/man/stat_summary.Rd @@ -106,7 +106,13 @@ bin width of a time variable is the number of seconds.} \item{breaks}{Alternatively, you can supply a numeric vector giving the bin boundaries. Overrides \code{binwidth}, \code{bins}, \code{center}, -and \code{boundary}.} +and \code{boundary}. + +Can also be a function that returns a numeric vector of bin boundaries +calculated from unscaled x. Here, "unscaled x" +refers to the original x values in the data, before application of any +scale transformation. When specifying a function along with a grouping +structure, the function will be called once per group.} \item{na.rm}{If \code{FALSE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} diff --git a/tests/testthat/test-stat-bin.R b/tests/testthat/test-stat-bin.R index 09f3675971..bd91d8ffbf 100644 --- a/tests/testthat/test-stat-bin.R +++ b/tests/testthat/test-stat-bin.R @@ -64,6 +64,14 @@ test_that("can use breaks argument", { expect_equal(out$count, c(1, 2)) }) +test_that("breaks computes bin boundaries for function input", { + df <- data.frame(x = c(0, 0, 0, 1:3)) + out <- layer_data(ggplot(df, aes(x)) + + geom_histogram(breaks = function(x) c(0, 0.5, 2.5, 7.5))) + + expect_equal(out$count, c(3, 2, 1)) +}) + test_that("fuzzy breaks are used when cutting", { df <- data_frame(x = c(-1, -0.5, -0.4, 0)) p <- ggplot(df, aes(x)) +