tibble(words = c("mango", "lychee", "blueberry")) |>
rowwise() |>
mutate(length = nchar(words)) |>
ungroup()# A tibble: 3 × 2
words length
<chr> <int>
1 mango 5
2 lychee 6
3 blueberry 9
EE BIOL C177/C234
Imagine we want to count the characters in several words. Let’s see them side-by-side:
✅ Rowwise Approach
map() 🔎The syntax is exceptionally clean:
.x: The vector or list you want to loop over..f: The function to apply to each element..x, runs it through .f, and outputs a list.Because map() returns a list by default, mutate(length = map(words, nchar)) creates a list-column:
# A tibble: 3 × 2
words length
<chr> <list>
1 mango <int [1]>
2 lychee <int [1]>
3 blueberry <int [1]>
Note how <list [1]> is stored under length. To get a flat vector, we need to handle it.
🛣️ Road 1: Unnest
map_*()Before map_vec()
map_vec() was introduced in late 2022 (purrr 1.0.0). Before that, developers had to write type-specific functions to get vectors: - map_dbl(words, nchar) (for numbers) - map_chr(words, nchar) (for characters) - map_lgl(words, nchar) (for logicals)
In older repositories, you will see map_dbl() or map_chr() frequently!
map() call.📦 Named Helper
\(x) Syntax 🔍Let’s break down \(x) runif(x, min = -1, max = 1):
\(x): Represents “this is a function taking one argument called x”.runif(x, ...): The body of the function. For each element of .x in map(), R binds the element’s value to x and evaluates this expression.Note
In standard R, \(x) is identical to writing function(x). It is simply a shorter, modern shorthand!
In older codebases, you will see a formula-style syntax using ~ and . instead:
~: Marks the beginning of the formula-based anonymous function..: Represents the value being passed (the placeholder).\(x) lambda syntax as it is cleaner and less ambiguous!map2(): Working with Two Inputs 👥When you need to iterate over two columns simultaneously element-by-element, use map2():
pmap(): Three or More Inputs 👥👥If you have three or more vectors/columns, use pmap(). You must wrap the columns in a list():
tibble(
num_samples = c(3, 4, 5),
sample_min = c(-1, -2, -3),
sample_max = c(2, 3, 4)
) |>
mutate(
sample = pmap(list(num_samples, sample_min, sample_max),
\(n, min, max) runif(n, min = min, max = max))
)# A tibble: 3 × 4
num_samples sample_min sample_max sample
<dbl> <dbl> <dbl> <list>
1 3 -1 2 <dbl [3]>
2 4 -2 3 <dbl [4]>
3 5 -3 4 <dbl [5]>
map() vs rowwise() ⚔️Both solve the same basic problem, but they have distinct strengths:
map(.x, .f) applies function .f element-wise to .x.map_vec() directly returns a flat vector instead of a list.\(x) to declare anonymous functions inline for clean, one-off logic.map2(), and three or more with pmap(list(...)).rowwise() be your gateway drug, but master map() for production-grade functional pipelines!purrr: Functional Programming