From e2ae8ac2320d20efc4f414d846c7c5455e5bb460 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 18 Apr 2024 14:33:30 +0200 Subject: [PATCH 1/5] protect against early NAs --- R/scale-.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/scale-.R b/R/scale-.R index 0b374d72ef..08ee0efa53 100644 --- a/R/scale-.R +++ b/R/scale-.R @@ -961,6 +961,12 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale, ) } pal <- self$palette(n) + if (n != length(limits)) { + # ensure NAs in non-end positions are reinserted + tmp <- rep(vec_cast(self$na.value, pal), length(limits)) + tmp[!is.na(limits)] <- pal + pal <- tmp + } self$palette.cache <- pal self$n.breaks.cache <- n } From 92decaec18c220c31731d3898db6dd712eac7078 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 18 Apr 2024 14:57:04 +0200 Subject: [PATCH 2/5] add test --- tests/testthat/test-scales.R | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/testthat/test-scales.R b/tests/testthat/test-scales.R index 691f0920aa..782a4bc3bb 100644 --- a/tests/testthat/test-scales.R +++ b/tests/testthat/test-scales.R @@ -730,3 +730,28 @@ test_that("Discrete scales with only NAs return `na.value`", { sc$train(x) expect_equal(sc$map(x), c(NA_real_, NA_real_)) }) + +test_that("discrete scales work with NAs in arbitrary positions", { + # Prevents intermediate caching of palettes + map <- function(x, limits) { + sc <- scale_colour_manual( + values = c("red", "green", "blue"), + na.value = "gray" + ) + sc$map(x, limits) + } + + # All inputs should yield output regardless of where NA is + input <- c("A", "B", "C", NA) + output <- c("red", "green", "blue", "gray") + + test <- map(x, limits = c("A", "B", "C", NA)) + expect_equal(test, output) + + test <- map(x, limits = c("A", NA, "B", "C")) + expect_equal(test, output) + + test <- map(x, limits = c(NA, "A", "B", "C")) + expect_equal(test, output) + +}) From 8cc991c2317ce4df944e53199b0aa6510c568330 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 18 Apr 2024 15:12:06 +0200 Subject: [PATCH 3/5] add news bullet --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index eef76f1cec..10ac0cabb9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ # ggplot2 (development version) +* Fixed bug where `na.value` was incorrectly mapped to non-`NA` values + (@teunbrand, #5756). * When facets coerce the faceting variables to factors, the 'ordered' class is dropped (@teunbrand, #5666). * `coord_map()` and `coord_polar()` throw informative warnings when used From 85e3506bd5aaaceee238ee8131c20325ec049e83 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 18 Apr 2024 15:19:13 +0200 Subject: [PATCH 4/5] fix typos --- tests/testthat/test-scales.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-scales.R b/tests/testthat/test-scales.R index 782a4bc3bb..79bd27af22 100644 --- a/tests/testthat/test-scales.R +++ b/tests/testthat/test-scales.R @@ -745,13 +745,13 @@ test_that("discrete scales work with NAs in arbitrary positions", { input <- c("A", "B", "C", NA) output <- c("red", "green", "blue", "gray") - test <- map(x, limits = c("A", "B", "C", NA)) + test <- map(input, limits = c("A", "B", "C", NA)) expect_equal(test, output) - test <- map(x, limits = c("A", NA, "B", "C")) + test <- map(input, limits = c("A", NA, "B", "C")) expect_equal(test, output) - test <- map(x, limits = c(NA, "A", "B", "C")) + test <- map(input, limits = c(NA, "A", "B", "C")) expect_equal(test, output) }) From baa2a98f8a6095ee303502577aa33af97f7a8a3c Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 18 Apr 2024 15:20:59 +0200 Subject: [PATCH 5/5] simplify by discarding `NA` limits --- R/scale-.R | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/R/scale-.R b/R/scale-.R index 08ee0efa53..6233caf54f 100644 --- a/R/scale-.R +++ b/R/scale-.R @@ -947,7 +947,8 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale, transform = identity, map = function(self, x, limits = self$get_limits()) { - n <- sum(!is.na(limits)) + limits <- limits[!is.na(limits)] + n <- length(limits) if (n < 1) { return(rep(self$na.value, length(x))) } @@ -961,12 +962,6 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale, ) } pal <- self$palette(n) - if (n != length(limits)) { - # ensure NAs in non-end positions are reinserted - tmp <- rep(vec_cast(self$na.value, pal), length(limits)) - tmp[!is.na(limits)] <- pal - pal <- tmp - } self$palette.cache <- pal self$n.breaks.cache <- n }