Skip to content

Commit 7665f47

Browse files
committed
Accept vars() specs in facet_grid()
1 parent 0699a46 commit 7665f47

File tree

3 files changed

+131
-38
lines changed

3 files changed

+131
-38
lines changed

R/facet-grid-.r

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ NULL
77
#' faceting variables. It is most useful when you have two discrete
88
#' variables, and all combinations of the variables exist in the data.
99
#'
10-
#' @param facets a formula with the rows (of the tabular display) on the LHS
11-
#' and the columns (of the tabular display) on the RHS; the dot in the
12-
#' formula is used to indicate there should be no faceting on this dimension
13-
#' (either row or column). The formula can also be provided as a string
14-
#' instead of a classical formula object
15-
#' @param margins either a logical value or a character
16-
#' vector. Margins are additional facets which contain all the data
17-
#' for each of the possible values of the faceting variables. If
18-
#' `FALSE`, no additional facets are included (the
19-
#' default). If `TRUE`, margins are included for all faceting
20-
#' variables. If specified as a character vector, it is the names of
21-
#' variables for which margins are to be created.
10+
#' @param rows,cols A set of variables or expressions quoted by
11+
#' [vars()] and defining faceting groups on the rows or columns
12+
#' dimension. The names will be shown if a relevant `labeller` is
13+
#' set.
14+
#'
15+
#' For backward compatibility with the historical interface, `rows`
16+
#' can also be a formula with the rows (of the tabular display) on
17+
#' the LHS and the columns (of the tabular display) on the RHS; the
18+
#' dot in the formula is used to indicate there should be no
19+
#' faceting on this dimension (either row or column). The formula
20+
#' can also be provided as a string instead of a classical formula
21+
#' object.
2222
#' @param scales Are scales shared across all facets (the default,
2323
#' `"fixed"`), or do they vary across rows (`"free_x"`),
2424
#' columns (`"free_y"`), or both rows and columns (`"free"`)
@@ -50,6 +50,15 @@ NULL
5050
#' @param drop If `TRUE`, the default, all factor levels not used in the
5151
#' data will automatically be dropped. If `FALSE`, all factor levels
5252
#' will be shown, regardless of whether or not they appear in the data.
53+
#' @param margins either a logical value or a character
54+
#' vector. Margins are additional facets which contain all the data
55+
#' for each of the possible values of the faceting variables. If
56+
#' `FALSE`, no additional facets are included (the
57+
#' default). If `TRUE`, margins are included for all faceting
58+
#' variables. If specified as a character vector, it is the names of
59+
#' variables for which margins are to be created.
60+
#' @param facets This argument is soft-deprecated, please us `rows`
61+
#' and `cols` instead.
5362
#' @export
5463
#' @examples
5564
#' p <- ggplot(mpg, aes(displ, cty)) + geom_point()
@@ -129,7 +138,27 @@ NULL
129138
#' mg + facet_grid(vs + am ~ gear, margins = c("gear", "am"))
130139
#' }
131140
#' @importFrom plyr as.quoted
132-
facet_grid <- function(facets, margins = FALSE, scales = "fixed", space = "fixed", shrink = TRUE, labeller = "label_value", as.table = TRUE, switch = NULL, drop = TRUE) {
141+
facet_grid <- function(rows = NULL,
142+
cols = NULL,
143+
scales = "fixed",
144+
space = "fixed",
145+
shrink = TRUE,
146+
labeller = "label_value",
147+
as.table = TRUE,
148+
switch = NULL,
149+
drop = TRUE,
150+
margins = FALSE,
151+
facets = NULL) {
152+
# `facets` is soft-deprecated and renamed to `rows`
153+
if (!is.null(facets)) {
154+
rows <- facets
155+
}
156+
# Should become a warning in a future release
157+
if (is.logical(cols)) {
158+
margins <- cols
159+
cols <- NULL
160+
}
161+
133162
scales <- match.arg(scales, c("fixed", "free_x", "free_y", "free"))
134163
free <- list(
135164
x = any(scales %in% c("free_x", "free")),
@@ -146,21 +175,17 @@ facet_grid <- function(facets, margins = FALSE, scales = "fixed", space = "fixed
146175
stop("switch must be either 'both', 'x', or 'y'", call. = FALSE)
147176
}
148177

149-
facets <- as_facets_list(facets)
150-
151-
n <- length(facets)
152-
if (!n) {
153-
stop("FIXME: Internal error grid?")
154-
}
178+
facets_list <- grid_as_facets_list(rows, cols)
179+
n <- length(facets_list)
155180
if (n > 2L) {
156181
stop("A grid facet specification can't have more than two dimensions", call. = FALSE)
157182
}
158183
if (n == 1L) {
159-
rows <- list()
160-
cols <- facets[[1]]
184+
rows <- quos()
185+
cols <- facets_list[[1]]
161186
} else {
162-
rows <- facets[[1]]
163-
cols <- facets[[2]]
187+
rows <- facets_list[[1]]
188+
cols <- facets_list[[2]]
164189
}
165190

166191
# Check for deprecated labellers
@@ -173,6 +198,33 @@ facet_grid <- function(facets, margins = FALSE, scales = "fixed", space = "fixed
173198
as.table = as.table, switch = switch, drop = drop)
174199
)
175200
}
201+
grid_as_facets_list <- function(rows, cols) {
202+
is_rows_vars <- is.null(rows) || rlang::is_quosures(rows)
203+
if (!is_rows_vars) {
204+
if (!is.null(cols)) {
205+
stop("`rows` must be `NULL` or a `vars()` list if `cols` is a `vars()` list", call. = FALSE)
206+
}
207+
return(as_facets_list(rows))
208+
}
209+
210+
is_cols_vars <- is.null(cols) || rlang::is_quosures(cols)
211+
if (!is_cols_vars) {
212+
stop("`cols` must be `NULL` or a `vars()` specification", call. = FALSE)
213+
}
214+
215+
if (is.null(rows)) {
216+
rows <- quos()
217+
} else {
218+
rows <- rlang::quos_auto_name(rows)
219+
}
220+
if (is.null(cols)) {
221+
cols <- quos()
222+
} else {
223+
cols <- rlang::quos_auto_name(cols)
224+
}
225+
226+
list(rows, cols)
227+
}
176228

177229
#' @rdname ggplot2-ggproto
178230
#' @format NULL

man/facet_grid.Rd

Lines changed: 25 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-facet-.r

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,37 @@ test_that("facet_wrap() accept vars()", {
7676
expect_identical(layer_data(p1), layer_data(p2))
7777
})
7878

79+
test_that("facet_grid() accepts vars()", {
80+
grid <- facet_grid(vars(a = foo))
81+
expect_identical(grid$params$rows, quos(a = foo))
82+
83+
grid <- facet_grid(vars(a = foo), vars(b = bar))
84+
expect_identical(grid$params$rows, quos(a = foo))
85+
expect_identical(grid$params$cols, quos(b = bar))
86+
87+
grid <- facet_grid(vars(foo), vars(bar))
88+
expect_identical(grid$params$rows, quos(foo = foo))
89+
expect_identical(grid$params$cols, quos(bar = bar))
90+
91+
expect_equal(facet_grid(vars(am, vs)), facet_grid(am + vs ~ .))
92+
expect_equal(facet_grid(vars(am, vs), vars(cyl)), facet_grid(am + vs ~ cyl))
93+
expect_equal(facet_grid(NULL, vars(cyl)), facet_grid(. ~ cyl))
94+
expect_equal(facet_grid(vars(am, vs), TRUE), facet_grid(am + vs ~ ., margins = TRUE))
95+
})
96+
97+
test_that("facet_grid() fails if passed both a formula and a vars()", {
98+
expect_error(facet_grid(~foo, vars()), "`rows` must be `NULL` or a `vars\\(\\)` list if")
99+
})
100+
101+
test_that("can't pass formulas to `cols`", {
102+
expect_error(facet_grid(NULL, ~foo), "`cols` must be `NULL` or a `vars\\(\\)`")
103+
})
104+
105+
test_that("can still pass `margins` as second argument", {
106+
grid <- facet_grid(~foo, TRUE)
107+
expect_true(grid$params$margins)
108+
})
109+
79110
test_that("vars() accepts optional names", {
80111
wrap <- facet_wrap(vars(A = a, b))
81112
expect_named(wrap$params$facets, c("A", "b"))

0 commit comments

Comments
 (0)