Skip to content

Commit efc53cc

Browse files
authored
Fix geom_ribbon(na.rm) (#6244)
* custom `GeomRibbon$handle_na` method * modify test * add news bullet
1 parent 4efa5cb commit efc53cc

File tree

4 files changed

+38
-3
lines changed

4 files changed

+38
-3
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# ggplot2 (development version)
22

3+
* `geom_ribbon()` now appropriately warns about, and removes, missing values
4+
(@teunbrand, #6243).
35
* `guide_*()` can now accept two inside legend theme elements:
46
`legend.position.inside` and `legend.justification.inside`, allowing inside
57
legends to be placed at different positions. Only inside legends with the same

R/geom-ribbon.R

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,31 @@ GeomRibbon <- ggproto("GeomRibbon", Geom,
126126

127127
draw_key = draw_key_polygon,
128128

129-
handle_na = function(data, params) {
129+
handle_na = function(self, data, params) {
130+
131+
vars <- vapply(
132+
strsplit(self$required_aes, "|", fixed = TRUE),
133+
`[[`, i = 1, character(1)
134+
)
135+
if (params$flipped_aes || any(data$flipped_aes) %||% FALSE) {
136+
vars <- switch_orientation(vars)
137+
}
138+
vars <- c(vars, self$non_missing_aes)
139+
140+
missing <- detect_missing(data, vars, finite = FALSE)
141+
if (!any(missing)) {
142+
return(data)
143+
}
144+
# We're rearranging groups to account for missing values
145+
data$group <- vec_identify_runs(data_frame0(missing, data$group))
146+
data <- vec_slice(data, !missing)
147+
148+
if (!params$na.rm) {
149+
cli::cli_warn(
150+
"Removed {sum(missing)} row{?s} containing missing values or values \\
151+
outside the scale range ({.fn {snake_class(self)}})."
152+
)
153+
}
130154
data
131155
},
132156

@@ -135,7 +159,6 @@ GeomRibbon <- ggproto("GeomRibbon", Geom,
135159
flipped_aes = FALSE, outline.type = "both") {
136160
data <- check_linewidth(data, snake_class(self))
137161
data <- flip_data(data, flipped_aes)
138-
if (na.rm) data <- data[stats::complete.cases(data[c("x", "ymin", "ymax")]), ]
139162
data <- data[order(data$group), ]
140163

141164
# Check that aesthetics are constant

tests/testthat/_snaps/geom-ribbon.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,7 @@
2323

2424
`outline.type` must be one of "both", "upper", "lower", or "full", not "test".
2525

26+
# NAs are dropped from the data
27+
28+
Removed 1 row containing missing values or values outside the scale range (`geom_ribbon()`).
29+

tests/testthat/test-geom-ribbon.R

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@ test_that("geom_ribbon() checks the aesthetics", {
1313
expect_snapshot_error(geom_ribbon(aes(year, ymin = level - 5, ymax = level + 5), outline.type = "test"))
1414
})
1515

16-
test_that("NAs are not dropped from the data", {
16+
test_that("NAs are dropped from the data", {
1717
df <- data_frame(x = 1:5, y = c(1, 1, NA, 1, 1))
1818

1919
p <- ggplot(df, aes(x))+
2020
geom_ribbon(aes(ymin = y - 1, ymax = y + 1))
21+
p <- ggplot_build(p)
2122

2223
expect_equal(get_layer_data(p)$ymin, c(0, 0, NA, 0, 0))
24+
expect_snapshot_warning(
25+
grob <- get_layer_grob(p)[[1]]
26+
)
27+
# We expect the ribbon to be broken up into 2 parts
28+
expect_length(grob$children, 2)
2329
})
2430

2531
test_that("geom_ribbon works in both directions", {

0 commit comments

Comments
 (0)