R: Apply a function to list-elements of a list

lmap {purrr} | R Documentation

## Apply a function to list-elements of a list

### Description

`lmap()`, `lmap_at()` and `lmap_if()` are similar to `map()`, `map_at()` and `map_if()`, with the difference that they operate exclusively on functions that take and return a list (or data frame). Thus, instead of mapping the elements of a list (as in `.x[[i]]`), they apply a function `.f` to each subset of size 1 of that list (as in `.x[i]`). We call those those elements `list-elements`).

### Usage

    lmap(.x, .f, ...)

    lmap_if(.x, .p, .f, ...)

    lmap_at(.x, .at, .f, ...)

### Arguments

`.x` | A list or data frame.
`.f` | A function that takes and returns a list or data frame.
`...` | Additional arguments passed on to `.f`.
`.p` | A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as `.x`. Alternatively, if the elements of `.x` are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where `.p` evaluates to `TRUE` will be modified.
`.at` | A character vector of names or a numeric vector of positions. Only those elements corresponding to `.at` will be modified.

### Details

Mapping the list-elements `.x[i]` has several advantages. It makes it possible to work with functions that exclusively take a list or data frame. It enables `.f` to access the attributes of the encapsulating list, like the name of the components it receives. It also enables `.f` to return a larger list than the list-element of size 1 it got as input. Conversely, `.f` can also return empty lists. In these cases, the output list is reshaped with a different size than the input list `.x`.

### Value

If `.x` is a list, a list. If `.x` is a data frame, a data frame.

### See Also

Other map variants: `imap`, `invoke`, `map2`, `map`, `modify`

### Examples

    # Let's write a function that returns a larger list or an empty list
    # depending on some condition. This function also uses the names
    # metadata available in the attributes of the list-element
    maybe_rep <- function(x) {
      n <- rpois(1, 2)
      out <- rep_len(x, n)
      if (length(out) > 0) {
        names(out) <- paste0(names(x), seq_len(n))
      }
      out
    }

    # The output size varies each time we map f()
    x <- list(a = 1:4, b = letters[5:7], c = 8:9, d = letters[10])
    x %>% lmap(maybe_rep)

    # We can apply f() on a selected subset of x
    x %>% lmap_at(c("a", "d"), maybe_rep)

    # Or only where a condition is satisfied
    x %>% lmap_if(is.character, maybe_rep)

    # A more realistic example would be a function that takes discrete
    # variables in a dataset and turns them into disjunctive tables, a
    # form that is amenable to fitting some types of models.

    # A disjunctive table contains only 0 and 1 but has as many columns
    # as unique values in the original variable. Ideally, we want to
    # combine the names of each level with the name of the discrete
    # variable in order to identify them. Given these requirements, it
    # makes sense to have a function that takes a data frame of size 1
    # and returns a data frame of variable size.
    disjoin <- function(x, sep = "_") {
      name <- names(x)
      x <- as.factor(x[[1]])

      out <- lapply(levels(x), function(level) {
        as.numeric(x == level)
      })

      names(out) <- paste(name, levels(x), sep = sep)
      tibble::as_tibble(out)
    }

    # Now, we are ready to map disjoin() on each categorical variable of a
    # data frame:
    iris %>% lmap_if(is.factor, disjoin)
    mtcars %>% lmap_at(c("cyl", "vs", "am"), disjoin)

[Package purrr version 0.2.4 Index]