diff --git a/NEWS.md b/NEWS.md index 56dfdeaaf8..56421587ff 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # ggplot2 (development version) +* Binned scales are now compatible with `trans = "date"` and `trans = "time"` + (@teunbrand, #4217). * `ggsave()` warns when multiple `filename`s are given, and only writes to the first file (@teunbrand, #5114). * Fixed a regression in `geom_hex()` where aesthetics were replicated across diff --git a/R/scale-.r b/R/scale-.r index b0065ffd52..01f3b74d5d 100644 --- a/R/scale-.r +++ b/R/scale-.r @@ -1075,10 +1075,13 @@ ScaleBinned <- ggproto("ScaleBinned", Scale, # Ensure terminal bins are same width if limits not set if (is.null(self$limits)) { # Remove calculated breaks if they coincide with limits - breaks <- setdiff(breaks, limits) + breaks <- breaks[!breaks %in% limits] nbreaks <- length(breaks) if (nbreaks >= 2) { - new_limits <- c(2 * breaks[1] - breaks[2], 2 * breaks[nbreaks] - breaks[nbreaks - 1]) + new_limits <- c( + breaks[1] + (breaks[1] - breaks[2]), + breaks[nbreaks] + (breaks[nbreaks] - breaks[nbreaks - 1]) + ) if (breaks[nbreaks] > limits[2]) { new_limits[2] <- breaks[nbreaks] breaks <- breaks[-nbreaks] diff --git a/tests/testthat/test-scale-binned.R b/tests/testthat/test-scale-binned.R index dd22a7e549..bb5969a586 100644 --- a/tests/testthat/test-scale-binned.R +++ b/tests/testthat/test-scale-binned.R @@ -4,3 +4,39 @@ test_that("binned scales only support continuous data", { p <- ggplot(mtcars) + geom_point(aes(disp, mpg, colour = as.character(gear))) + scale_color_binned() expect_snapshot_error(ggplot_build(p)) }) + +test_that('binned scales can calculate breaks on dates', { + + data <- seq(as.Date("2000-01-01"), as.Date("2020-01-01"), length.out = 100) + + scale <- scale_x_binned(trans = "date") + scale$train(scale$transform(data)) + breaks <- scale$trans$inverse(scale$get_breaks()) + + expect_s3_class(breaks, "Date") + expect_equal( + unname(breaks), + as.Date(paste0(seq(2002, 2018, by = 2), "-01-01")) + ) +}) + +test_that('binned scales can calculate breaks on date-times', { + data <- seq( + strptime("2000-01-01", "%Y-%m-%d"), + strptime("2020-01-01", "%Y-%m-%d"), + length.out = 100 + ) + + scale <- scale_x_binned(trans = "time") + scale$train(scale$transform(data)) + breaks <- scale$trans$inverse(scale$get_breaks()) + + expect_s3_class(breaks, "POSIXct") + expect_equal( + unname(unclass(breaks)), + unclass(as.POSIXct(strptime( + paste0(seq(2002, 2018, by = 2), "-01-01"), + "%Y-%m-%d" + ))) + ) +})