R: Produce all combinations of list elements

cross_n {purrr} | R Documentation

## Produce all combinations of list elements

### Description

`cross2()` returns the product set of the elements of `.x` and `.y`. `cross3()` takes an additional `.z` argument. `cross_n()` takes a list `.l` and returns the cartesian product of all its elements in a list, with one combination by element. `cross_d()` is like `cross_n()` but returns a data frame, with one combination by row.

### Usage

    cross_n(.l, .filter = NULL)

    cross2(.x, .y, .filter = NULL)

    cross3(.x, .y, .z, .filter = NULL)

    cross_d(.l, .filter = NULL)

### Arguments

`.l` | A list of lists or atomic vectors. Alternatively, a data frame. `cross_d()` requires all elements to be named.
`.filter` | A predicate function that takes the same number of arguments as the number of variables to be combined.
`.x, .y, .z` | Lists or atomic vectors.

### Details

`cross_n()`, `cross2()` and `cross3()` return the cartesian product is returned in wide format. This makes it more amenable to mapping operations. `cross_d()` returns the output in long format just as `expand.grid()` does. This is adapted to rowwise operations.

When the number of combinations is large and the individual elements are heavy memory-wise, it is often useful to filter unwanted combinations on the fly with `.filter`. It must be a predicate function that takes the same number of arguments as the number of crossed objects (2 for `cross2()`, 3 for `cross3()`, `length(.l)` for `cross_n()`) and returns `TRUE` or `FALSE`. The combinations where the predicate function returns `TRUE` will be removed from the result.

### Value

`cross2()`, `cross3()` and `cross_n()` always return a list. `cross_d()` always returns a data frame. `cross_n()` returns a list where each element is one combination so that the list can be directly mapped over. `cross_d()` returns a data frame where each row is one combination.

### See Also

`expand.grid()`

### Examples

    # We build all combinations of names, greetings and separators from our
    # list of data and pass each one to paste()
    data <- list(
      id = c("John", "Jane"),
      greeting = c("Hello.", "Bonjour."),
      sep = c("! ", "... ")
    )

    data %>%
      cross_n() %>%
      map(lift(paste))

    # cross_n() returns the combinations in long format: many elements,
    # each representing one combination. With cross_d() we'll get a
    # data frame in long format: crossing three objects produces a data
    # frame of three columns with each row being a particular
    # combination. This is the same format that expand.grid() returns.
    args <- data %>% cross_d()

    # In case you need a list in long format (and not a data frame)
    # just run as.list() after cross_d()
    args %>% as.list()

    # This format is often less pratical for functional programming
    # because applying a function to the combinations requires a loop
    out <- vector("list", length = nrow(args))
    for (i in seq_along(out))
      out[[i]] <- map(args, i) %>% invoke(paste, .)
    out

    # It's easier to transpose and then use invoke_map()
    args %>% transpose() %>% map_chr(~ invoke(paste, .))

    # Unwanted combinations can be filtered out with a predicate function
    filter <- function(x, y) x >= y
    cross2(1:5, 1:5, .filter = filter) %>% str()

    # To give names to the components of the combinations, we map
    # setNames() on the product:
    seq_len(3) %>%
      cross2(., ., .filter = `==`) %>%
      map(setNames, c("x", "y"))

    # Alternatively we can encapsulate the arguments in a named list
    # before crossing to get named components:
    seq_len(3) %>%
      list(x = ., y = .) %>%
      cross_n(.filter = `==`)

[Package purrr version 0.2.2 Index]