Skip to content

Commit b0ed08c

Browse files
committed
Add .vary parameter to expand_grid(), Fixes #1543
1 parent 9966c04 commit b0ed08c

File tree

5 files changed

+157
-10
lines changed

5 files changed

+157
-10
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
* tidyr now requires dplyr >=1.1.0 (#1568, @catalamarti).
77

8+
* `expand_grid()` gains a new `.vary` argument, allowing users to control whether combinations vary fastest or slowest (default). (#1543, @JamesHWade).
9+
810
# tidyr 1.3.1
911

1012
* `pivot_wider` now uses `.by` and `|>` syntax for the dplyr helper message to

R/expand.R

+22-5
Original file line numberDiff line numberDiff line change
@@ -162,30 +162,46 @@ nesting <- function(..., .name_repair = "check_unique") {
162162
#' `expand_grid()` is heavily motivated by [expand.grid()].
163163
#' Compared to `expand.grid()`, it:
164164
#'
165-
#' * Produces sorted output (by varying the first column the slowest, rather
166-
#' than the fastest).
165+
#' * Produces sorted output (by varying the first column the slowest by default).
167166
#' * Returns a tibble, not a data frame.
168167
#' * Never converts strings to factors.
169168
#' * Does not add any additional attributes.
170169
#' * Can expand any generalised vector, including data frames.
170+
#' * Allows for fastest or slowest varying combinations.
171171
#'
172172
#' @param ... Name-value pairs. The name will become the column name in the
173173
#' output.
174174
#' @inheritParams tibble::as_tibble
175+
#' @param .vary Character string specifying the order of combination variation.
176+
#' Must be either "slowest" (default) or "fastest".
177+
#' * "slowest": Varies the first column the slowest (default, compatible with
178+
#' base R's expand.grid()).
179+
#' * "fastest": Varies the first column the fastest (compatible with
180+
#' purrr::cross() family).
181+
#'
175182
#' @return A tibble with one column for each input in `...`. The output
176183
#' will have one row for each combination of the inputs, i.e. the size
177-
#' be equal to the product of the sizes of the inputs. This implies
184+
#' will be equal to the product of the sizes of the inputs. This implies
178185
#' that if any input has length 0, the output will have zero rows.
186+
#' The order of combinations depends on the `.vary` parameter.
187+
#'
179188
#' @export
180189
#' @examples
190+
#' # Default behavior (slowest varying)
181191
#' expand_grid(x = 1:3, y = 1:2)
182-
#' expand_grid(l1 = letters, l2 = LETTERS)
192+
#'
193+
#' # Fastest varying (like purrr::cross())
194+
#' expand_grid(x = 1:3, y = 1:2, .vary = "fastest")
195+
#'
196+
#' expand_grid(l1 = letters[1:3], l2 = LETTERS[1:2])
183197
#'
184198
#' # Can also expand data frames
185199
#' expand_grid(df = tibble(x = 1:2, y = c(2, 1)), z = 1:3)
200+
#'
186201
#' # And matrices
187202
#' expand_grid(x1 = matrix(1:4, nrow = 2), x2 = matrix(5:8, nrow = 2))
188-
expand_grid <- function(..., .name_repair = "check_unique") {
203+
expand_grid <- function(..., .name_repair = "check_unique", .vary = c("slowest", "fastest")) {
204+
.vary <- rlang::arg_match(.vary)
189205
out <- grid_dots(...)
190206

191207
names <- names2(out)
@@ -202,6 +218,7 @@ expand_grid <- function(..., .name_repair = "check_unique") {
202218

203219
out <- vec_expand_grid(
204220
!!!out,
221+
.vary = .vary,
205222
.name_repair = "minimal",
206223
.error_call = current_env()
207224
)

man/expand_grid.Rd

+25-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/expand.md

+8
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,11 @@
6464
Error:
6565
! `..1` must be a vector, not a <lm> object.
6666

67+
# expand_grid() throws an error for invalid .vary parameter
68+
69+
Code
70+
expand_grid(x = 1:2, y = 1:2, .vary = "invalid")
71+
Condition
72+
Error in `expand_grid()`:
73+
! `.vary` must be one of "slowest" or "fastest", not "invalid".
74+

tests/testthat/test-expand.R

+100
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,103 @@ test_that("fct_unique() doesn't alter level order if `NA` is an existing level",
451451
expect_identical(fct_unique(x), x)
452452
expect_identical(levels(fct_unique(x)), c(NA, "x"))
453453
})
454+
455+
456+
# ------------------------------------------------------------------------------
457+
# .vary parameter
458+
459+
test_that("expand_grid() respects .vary parameter", {
460+
# Test default behavior (slowest varying)
461+
out_default <- expand_grid(x = 1:3, y = 1:2)
462+
expect_equal(
463+
out_default,
464+
tibble(
465+
x = c(1, 1, 2, 2, 3, 3),
466+
y = c(1, 2, 1, 2, 1, 2)
467+
)
468+
)
469+
470+
# Test fastest varying
471+
out_fastest <- expand_grid(x = 1:3, y = 1:2, .vary = "fastest")
472+
expect_equal(
473+
out_fastest,
474+
tibble(
475+
x = c(1, 2, 3, 1, 2, 3),
476+
y = c(1, 1, 1, 2, 2, 2)
477+
)
478+
)
479+
})
480+
481+
test_that("expand_grid() .vary parameter works with more than two variables", {
482+
out_slowest <- expand_grid(x = 1:2, y = 1:2, z = 1:2)
483+
expect_equal(
484+
out_slowest,
485+
tibble(
486+
x = c(1, 1, 1, 1, 2, 2, 2, 2),
487+
y = c(1, 1, 2, 2, 1, 1, 2, 2),
488+
z = c(1, 2, 1, 2, 1, 2, 1, 2)
489+
)
490+
)
491+
492+
out_fastest <- expand_grid(x = 1:2, y = 1:2, z = 1:2, .vary = "fastest")
493+
expect_equal(
494+
out_fastest,
495+
tibble(
496+
x = c(1, 2, 1, 2, 1, 2, 1, 2),
497+
y = c(1, 1, 2, 2, 1, 1, 2, 2),
498+
z = c(1, 1, 1, 1, 2, 2, 2, 2)
499+
)
500+
)
501+
})
502+
503+
test_that("expand_grid() .vary parameter works with different input types", {
504+
out_slowest <- expand_grid(x = 1:2, y = c("a", "b"), z = factor(c("low", "high")))
505+
expect_equal(
506+
out_slowest,
507+
tibble(
508+
x = c(1, 1, 1, 1, 2, 2, 2, 2),
509+
y = c("a", "a", "b", "b", "a", "a", "b", "b"),
510+
z = factor(c("low", "high", "low", "high", "low", "high", "low", "high"))
511+
)
512+
)
513+
514+
out_fastest <- expand_grid(x = 1:2, y = c("a", "b"), z = factor(c("low", "high")), .vary = "fastest")
515+
expect_equal(
516+
out_fastest,
517+
tibble(
518+
x = c(1, 2, 1, 2, 1, 2, 1, 2),
519+
y = c("a", "a", "b", "b", "a", "a", "b", "b"),
520+
z = factor(c("low", "low", "low", "low", "high", "high", "high", "high"))
521+
)
522+
)
523+
})
524+
525+
test_that("expand_grid() .vary parameter works with data frames", {
526+
df <- tibble(a = 1:2, b = c("x", "y"))
527+
out_slowest <- expand_grid(df, z = 1:2)
528+
expect_equal(
529+
out_slowest,
530+
tibble(
531+
a = c(1, 1, 2, 2),
532+
b = c("x", "x", "y", "y"),
533+
z = c(1, 2, 1, 2)
534+
)
535+
)
536+
537+
out_fastest <- expand_grid(df, z = 1:2, .vary = "fastest")
538+
expect_equal(
539+
out_fastest,
540+
tibble(
541+
a = c(1, 2, 1, 2),
542+
b = c("x", "y", "x", "y"),
543+
z = c(1, 1, 2, 2)
544+
)
545+
)
546+
})
547+
548+
test_that("expand_grid() throws an error for invalid .vary parameter", {
549+
expect_snapshot(
550+
expand_grid(x = 1:2, y = 1:2, .vary = "invalid"),
551+
error = TRUE
552+
)
553+
})

0 commit comments

Comments
 (0)