Skip to content

Variable panel size (space) for facet_wrap() #5956

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ggplot2 (development version)

* `facet_wrap()` can have `space = "free_x"` with 1-row layouts and
`space = "free_y"` with 1-column layouts (@teunbrand)
* Secondary axes respect `n.breaks` setting in continuous scales (@teunbrand, #4483).
* Layers can have names (@teunbrand, #4066).
* (internal) improvements to `pal_qualitative()` (@teunbrand, #5013)
Expand Down
37 changes: 34 additions & 3 deletions R/facet-wrap.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
#' @param scales Should scales be fixed (`"fixed"`, the default),
#' free (`"free"`), or free in one dimension (`"free_x"`,
#' `"free_y"`)?
#' @param space If `"fixed"` (default), all panels have the same size and
#' the number of rows and columns in the layout can be arbitrary. If
#' `"free_x"`, panels have widths proportional to the length of the x-scale,
#' but the layout is constrained to one row. If `"free_y"`, panels have
#' heights proportional to the length of the y-scale, but the layout is
#' constrained to one column.
#' @param strip.position By default, the labels are displayed on the top of
#' the plot. Using `strip.position` it is possible to place the labels on
#' either of the four sides by setting \code{strip.position = c("top",
Expand Down Expand Up @@ -109,9 +115,9 @@
#' geom_point() +
#' facet_wrap(vars(class), dir = "tr")
facet_wrap <- function(facets, nrow = NULL, ncol = NULL, scales = "fixed",
shrink = TRUE, labeller = "label_value", as.table = TRUE,
switch = deprecated(), drop = TRUE, dir = "h",
strip.position = 'top', axes = "margins",
space = "fixed", shrink = TRUE, labeller = "label_value",
as.table = TRUE, switch = deprecated(), drop = TRUE,
dir = "h", strip.position = 'top', axes = "margins",
axis.labels = "all") {
scales <- arg_match0(scales %||% "fixed", c("fixed", "free_x", "free_y", "free"))
dir <- arg_match0(dir, c("h", "v", "lt", "tl", "lb", "bl", "rt", "tr", "rb", "br"))
Expand All @@ -128,6 +134,30 @@
y = any(scales %in% c("free_y", "free"))
)

# We cannot have free space in both directions
space <- arg_match0(space, c("free_x", "free_y", "fixed"))
space_free <- list(x = space == "free_x", y = space == "free_y")
if (space_free$x) {
if ((nrow %||% 1) != 1 || !is.null(ncol)) {
cli::cli_warn(
"Cannot use {.code space = \"free_x\"} with custom \\
{.arg nrow} or {.arg ncol}."
)
}
ncol <- NULL
nrow <- 1L
}
if (space_free$y) {
if ((ncol %||% 1) != 1 || !is.null(nrow)) {
cli::cli_warn(
"Cannot use {.code space= \"free_y\"} with custom \\
{.arg nrow} or {.arg ncol}."
)

Check warning on line 155 in R/facet-wrap.R

View check run for this annotation

Codecov / codecov/patch

R/facet-wrap.R#L152-L155

Added lines #L152 - L155 were not covered by tests
}
ncol <- 1L
nrow <- NULL
}

# If scales are free, always draw the axes
draw_axes <- arg_match0(axes, c("margins", "all_x", "all_y", "all"))
draw_axes <- list(
Expand Down Expand Up @@ -174,6 +204,7 @@
drop = drop,
ncol = ncol,
nrow = nrow,
space_free = space_free,
labeller = labeller,
dir = dir,
draw_axes = draw_axes,
Expand Down
8 changes: 8 additions & 0 deletions man/facet_wrap.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions tests/testthat/_snaps/facet-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@

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

---

Cannot use `space = "free_x"` with custom `nrow` or `ncol`.

---

Need 3 panels, but together `nrow` and `ncol` only provide 1.
Expand Down
21 changes: 21 additions & 0 deletions tests/testthat/test-facet-layout.R
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,25 @@ test_that("grid: drop = FALSE preserves unused levels", {
expect_equal(as.character(grid_ab$b), as.character(rep(4:1, 4)))
})

test_that("wrap: space = 'free_x/y' sets panel sizes", {

df <- data.frame(x = 1:3)
p <- ggplot(df, aes(x, x)) +
geom_point() +
scale_x_continuous(limits = c(0, NA), expand = c(0, 0)) +
scale_y_continuous(limits = c(0, NA), expand = c(0, 0))

# Test free_x
gt <- ggplotGrob(p + facet_wrap(~x, scales = "free_x", space = "free_x"))
test <- gt$widths[panel_cols(gt)$l]
expect_equal(as.numeric(test), 1:3)

# Test free_y
gt <- ggplotGrob(p + facet_wrap(~x, scales = "free_y", space = "free_y"))
test <- gt$heights[panel_rows(gt)$t]
expect_equal(as.numeric(test), 1:3)
})

# Missing behaviour ----------------------------------------------------------

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

expect_snapshot_warning(facet_wrap(~test, nrow = 2, space = "free_x"))

p <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
facet_wrap(~gear, ncol = 1, nrow = 1)
Expand Down
Loading