diff --git a/NEWS.md b/NEWS.md index 1715a4c82f..6c444cd08f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,7 @@ * Fixed spurious warnings from `sec_axis()` with `breaks = NULL` (#5713). * Patterns and gradients are now also enabled in `geom_sf()` (@teunbrand, #5716). +* `stat_bin()` deals with non-finite breaks better (@teunbrand, #5665). # ggplot2 3.5.0 diff --git a/R/bin.R b/R/bin.R index 5cb1a948ee..8ca4bff921 100644 --- a/R/bin.R +++ b/R/bin.R @@ -1,10 +1,14 @@ bins <- function(breaks, closed = "right", - fuzz = 1e-08 * stats::median(diff(breaks))) { + fuzz = NULL) { check_numeric(breaks) closed <- arg_match0(closed, c("right", "left")) - breaks <- sort(breaks) + # Adapted base::hist - this protects from floating point rounding errors + fuzz <- fuzz %||% 1e-08 * stats::median(diff(breaks[is.finite(breaks)])) + if (!is.finite(fuzz)) { # happens when 0 or 1 finite breaks are given + fuzz <- .Machine$double.eps * 1e3 + } if (closed == "right") { fuzzes <- c(-fuzz, rep.int(fuzz, length(breaks) - 1)) } else { diff --git a/tests/testthat/test-stat-bin.R b/tests/testthat/test-stat-bin.R index d15a19fcff..6ab5ec96b2 100644 --- a/tests/testthat/test-stat-bin.R +++ b/tests/testthat/test-stat-bin.R @@ -112,6 +112,13 @@ test_that("stat_bin() provides width (#3522)", { # Underlying binning algorithm -------------------------------------------- +test_that("bins() computes fuzz with non-finite breaks", { + test <- bins(breaks = c(-Inf, 1, Inf)) + expect_equal(test$fuzzy, test$breaks, tolerance = 1e-10) + difference <- test$fuzzy - test$breaks + expect_equal(difference[2], 1000 * .Machine$double.eps, tolerance = 0) +}) + comp_bin <- function(df, ...) { plot <- ggplot(df, aes(x = x)) + stat_bin(...) layer_data(plot)