Skip to content

Commit c108758

Browse files
authored
Variable panel size (space) for facet_wrap() (#5956)
* add space argument * add test * document * add news bullet * accept new snapshot
1 parent 78b3f3a commit c108758

File tree

5 files changed

+69
-3
lines changed

5 files changed

+69
-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+
* `facet_wrap()` can have `space = "free_x"` with 1-row layouts and
4+
`space = "free_y"` with 1-column layouts (@teunbrand)
35
* Secondary axes respect `n.breaks` setting in continuous scales (@teunbrand, #4483).
46
* Layers can have names (@teunbrand, #4066).
57
* (internal) improvements to `pal_qualitative()` (@teunbrand, #5013)

R/facet-wrap.R

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ NULL
1818
#' @param scales Should scales be fixed (`"fixed"`, the default),
1919
#' free (`"free"`), or free in one dimension (`"free_x"`,
2020
#' `"free_y"`)?
21+
#' @param space If `"fixed"` (default), all panels have the same size and
22+
#' the number of rows and columns in the layout can be arbitrary. If
23+
#' `"free_x"`, panels have widths proportional to the length of the x-scale,
24+
#' but the layout is constrained to one row. If `"free_y"`, panels have
25+
#' heights proportional to the length of the y-scale, but the layout is
26+
#' constrained to one column.
2127
#' @param strip.position By default, the labels are displayed on the top of
2228
#' the plot. Using `strip.position` it is possible to place the labels on
2329
#' either of the four sides by setting \code{strip.position = c("top",
@@ -109,9 +115,9 @@ NULL
109115
#' geom_point() +
110116
#' facet_wrap(vars(class), dir = "tr")
111117
facet_wrap <- function(facets, nrow = NULL, ncol = NULL, scales = "fixed",
112-
shrink = TRUE, labeller = "label_value", as.table = TRUE,
113-
switch = deprecated(), drop = TRUE, dir = "h",
114-
strip.position = 'top', axes = "margins",
118+
space = "fixed", shrink = TRUE, labeller = "label_value",
119+
as.table = TRUE, switch = deprecated(), drop = TRUE,
120+
dir = "h", strip.position = 'top', axes = "margins",
115121
axis.labels = "all") {
116122
scales <- arg_match0(scales %||% "fixed", c("fixed", "free_x", "free_y", "free"))
117123
dir <- arg_match0(dir, c("h", "v", "lt", "tl", "lb", "bl", "rt", "tr", "rb", "br"))
@@ -128,6 +134,30 @@ facet_wrap <- function(facets, nrow = NULL, ncol = NULL, scales = "fixed",
128134
y = any(scales %in% c("free_y", "free"))
129135
)
130136

137+
# We cannot have free space in both directions
138+
space <- arg_match0(space, c("free_x", "free_y", "fixed"))
139+
space_free <- list(x = space == "free_x", y = space == "free_y")
140+
if (space_free$x) {
141+
if ((nrow %||% 1) != 1 || !is.null(ncol)) {
142+
cli::cli_warn(
143+
"Cannot use {.code space = \"free_x\"} with custom \\
144+
{.arg nrow} or {.arg ncol}."
145+
)
146+
}
147+
ncol <- NULL
148+
nrow <- 1L
149+
}
150+
if (space_free$y) {
151+
if ((ncol %||% 1) != 1 || !is.null(nrow)) {
152+
cli::cli_warn(
153+
"Cannot use {.code space= \"free_y\"} with custom \\
154+
{.arg nrow} or {.arg ncol}."
155+
)
156+
}
157+
ncol <- 1L
158+
nrow <- NULL
159+
}
160+
131161
# If scales are free, always draw the axes
132162
draw_axes <- arg_match0(axes, c("margins", "all_x", "all_y", "all"))
133163
draw_axes <- list(
@@ -174,6 +204,7 @@ facet_wrap <- function(facets, nrow = NULL, ncol = NULL, scales = "fixed",
174204
drop = drop,
175205
ncol = ncol,
176206
nrow = nrow,
207+
space_free = space_free,
177208
labeller = labeller,
178209
dir = dir,
179210
draw_axes = draw_axes,

man/facet_wrap.Rd

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

tests/testthat/_snaps/facet-layout.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222

2323
`nrow` must be a whole number or `NULL`, not the number 1.5.
2424

25+
---
26+
27+
Cannot use `space = "free_x"` with custom `nrow` or `ncol`.
28+
2529
---
2630

2731
Need 3 panels, but together `nrow` and `ncol` only provide 1.

tests/testthat/test-facet-layout.R

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,25 @@ test_that("grid: drop = FALSE preserves unused levels", {
172172
expect_equal(as.character(grid_ab$b), as.character(rep(4:1, 4)))
173173
})
174174

175+
test_that("wrap: space = 'free_x/y' sets panel sizes", {
176+
177+
df <- data.frame(x = 1:3)
178+
p <- ggplot(df, aes(x, x)) +
179+
geom_point() +
180+
scale_x_continuous(limits = c(0, NA), expand = c(0, 0)) +
181+
scale_y_continuous(limits = c(0, NA), expand = c(0, 0))
182+
183+
# Test free_x
184+
gt <- ggplotGrob(p + facet_wrap(~x, scales = "free_x", space = "free_x"))
185+
test <- gt$widths[panel_cols(gt)$l]
186+
expect_equal(as.numeric(test), 1:3)
187+
188+
# Test free_y
189+
gt <- ggplotGrob(p + facet_wrap(~x, scales = "free_y", space = "free_y"))
190+
test <- gt$heights[panel_rows(gt)$t]
191+
expect_equal(as.numeric(test), 1:3)
192+
})
193+
175194
# Missing behaviour ----------------------------------------------------------
176195

177196
a3 <- data_frame(
@@ -207,6 +226,8 @@ test_that("facet_wrap throws errors at bad layout specs", {
207226
expect_snapshot_error(facet_wrap(~test, nrow = -1))
208227
expect_snapshot_error(facet_wrap(~test, nrow = 1.5))
209228

229+
expect_snapshot_warning(facet_wrap(~test, nrow = 2, space = "free_x"))
230+
210231
p <- ggplot(mtcars) +
211232
geom_point(aes(mpg, disp)) +
212233
facet_wrap(~gear, ncol = 1, nrow = 1)

0 commit comments

Comments
 (0)