Apply a function to list-elements of a list
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 elements list-elements
).
lmap(.x, .f, ...) lmap_if(.x, .p, .f, ..., .else = NULL) lmap_at(.x, .at, .f, ...)
.x |
A list or data frame. |
.f |
A function that takes and returns a list or data frame. |
... |
Additional arguments passed on to the mapped function. |
.p |
A single predicate function, a formula describing such a
predicate function, or a logical vector of the same length as |
.else |
A function applied to elements of |
.at |
A character vector of names, positive numeric vector of
positions to include, or a negative numeric vector of positions to
exlude. Only those elements corresponding to |
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
.
If .x
is a list, a list. If .x
is a data
frame, a data frame.
# 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) 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)
Please choose more modern alternatives, such as Google Chrome or Mozilla Firefox.