Skip to content

Commit e7e7566

Browse files
committed
Merge branch 'richierocks-master'
2 parents c032862 + 736ec3e commit e7e7566

File tree

3 files changed

+96
-0
lines changed

3 files changed

+96
-0
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
ggplot2 1.0.1.9000
22
----------------------------------------------------------------
33

4+
* `facet_wrap()` more carefully checks its `nrow` and `ncol` arguments
5+
to ensure that they're specified correctly (@richierocks, #962)
6+
47
* Improved the calculation of segments needed to draw the curve representing
58
a line when plotted in polar coordinates. In some cases, the last segment
69
of a multi-segment line was not drawn (@BrianDiggs, #952)

R/facet-wrap.r

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ facet_wrap <- function(facets, nrow = NULL, ncol = NULL, scales = "fixed",
6161
y = any(scales %in% c("free_y", "free"))
6262
)
6363

64+
nrow <- sanitise_dim(nrow)
65+
ncol <- sanitise_dim(ncol)
66+
6467
facet(
6568
facets = as.quoted(facets), free = free, shrink = shrink,
6669
as.table = as.table, drop = drop,
@@ -246,3 +249,59 @@ facet_axes.wrap <- function(facet, panel, coord, theme) {
246249
facet_vars.wrap <- function(facet) {
247250
paste(lapply(facet$facets, paste, collapse = ", "), collapse = " ~ ")
248251
}
252+
253+
#' Sanitise the number of rows or columns
254+
#'
255+
#' Cleans up the input to be an integer greater than or equal to one, or
256+
#' \code{NULL}. Intended to be used on the \code{nrow} and \code{ncol}
257+
#' arguments of \code{facet_wrap}.
258+
#' @param n Hopefully an integer greater than or equal to one, or \code{NULL},
259+
#' though other inputs are handled.
260+
#' @return An integer greater than or equal to one, or \code{NULL}.
261+
#' @note If the length of the input is greater than one, only the first element
262+
#' is returned, with a warning.
263+
#' If the input is not an integer, it will be coerced to be one.
264+
#' If the value is less than one, \code{NULL} is returned, effectively ignoring
265+
#' the argument.
266+
#' Multiple warnings may be generated.
267+
#' @examples
268+
#' # Valid input just gets returns unchanged
269+
#' sanitise_dim(1)
270+
#' sanitise_dim(NULL)
271+
#' \dontrun{
272+
#' # Only the first element of vectors get returned
273+
#' sanitise_dim(10:1)
274+
#' # Non-integer values are coerced to integer
275+
#' sanitise_dim(pi)
276+
#' # Missing values, values less than one and non-numeric values are
277+
#' # treated as NULL
278+
#' sanitise_dim(NA_integer_)
279+
#' sanitise_dim(0)
280+
#' sanitise_dim("foo")
281+
#' }
282+
#' @noRd
283+
sanitise_dim <- function(n) {
284+
xname <- paste0("`", deparse(substitute(n)), "`")
285+
if (length(n) == 0) {
286+
if (!is.null(n)) {
287+
warning(xname, " has length zero and will be treated as NULL.",
288+
call. = FALSE)
289+
}
290+
return(NULL)
291+
}
292+
if (length(n) > 1) {
293+
warning("Only the first value of ", xname, " will be used.", call. = FALSE)
294+
n <- n[1]
295+
}
296+
if (!is.numeric(n) || (!is.na(n) && n != round(n))) {
297+
warning("Coercing ", xname, " to be an integer.", call. = FALSE)
298+
n <- as.integer(n)
299+
}
300+
if (is.na(n) || n < 1) {
301+
warning(xname, " is missing or less than 1 and will be treated as NULL.",
302+
call. = FALSE)
303+
return(NULL)
304+
}
305+
n
306+
}
307+

inst/tests/test-sanitise-dim.r

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
context("sanitise_dim")
2+
3+
test_that("sanitise_dim returns NULL for zero-length inputs, with appropriate warnings", {
4+
expect_identical(sanitise_dim(NULL), NULL)
5+
n <- integer()
6+
y <- expect_identical(suppressWarnings(sanitise_dim(n)), NULL)
7+
expect_warning(sanitise_dim(n), "`n` has length zero and will be treated as NULL.")
8+
})
9+
10+
test_that("sanitise_dim returns the first element or NULL for non-positive integer inputs, with appropriate warnings", {
11+
n <- 1:2
12+
expect_identical(suppressWarnings(sanitise_dim(n)), 1L)
13+
expect_warning(sanitise_dim(n), "Only the first value of `n` will be used.")
14+
n2 <- 0:1
15+
expect_identical(suppressWarnings(sanitise_dim(n2)), NULL)
16+
expect_warning(sanitise_dim(n2), "Only the first value of `n2` will be used.")
17+
expect_warning(sanitise_dim(n2), "`n2` is missing or less than 1 and will be treated as NULL.")
18+
})
19+
20+
test_that("sanitise_dim returns a NULL for missing inputs, with appropriate warnings", {
21+
n <- NA_integer_
22+
expect_identical(suppressWarnings(sanitise_dim(n)), NULL)
23+
expect_warning(sanitise_dim(n), "`n` is missing or less than 1 and will be treated as NULL.")
24+
})
25+
26+
test_that("sanitise_dim returns a positive integer or NULL for non-integer inputs, with appropriate warnings", {
27+
n <- 1.5
28+
expect_identical(suppressWarnings(sanitise_dim(n)), 1L)
29+
expect_warning(sanitise_dim(n), "Coercing `n` to be an integer.")
30+
n2 <- 0.9999999
31+
expect_identical(suppressWarnings(sanitise_dim(n2)), NULL)
32+
expect_warning(sanitise_dim(n2), "Coercing `n2` to be an integer.")
33+
expect_warning(sanitise_dim(n2), "`n2` is missing or less than 1 and will be treated as NULL.")
34+
})

0 commit comments

Comments
 (0)