🔬 Tidyverse Series – Post 9: Functional Programming in R with purrr
Link to heading
🛠 Why {purrr}
?
Link to heading
Iteration in R can be clunky using for-loops, especially when working with lists and complex data structures. {purrr}
provides elegant functional programming tools that make iteration cleaner, safer, and more expressive.
🔹 Why Use {purrr}
?
Link to heading
✔️ Replaces slow and verbose for-loops
✔️ Works seamlessly with lists and tibbles
✔️ Encourages a functional programming approach
✔️ Improves error handling in iterative tasks
✔️ Simplifies nested operations
Let’s explore the key functions that make {purrr}
a game-changer.
📚 Key {purrr}
Functions
Link to heading
Function | Purpose |
---|---|
map() |
Apply a function to each element of a list |
map_dbl() , map_chr() , map_lgl() |
Ensure output as numeric, character, or logical vectors |
map2() |
Iterate over two lists at once |
pmap() |
Iterate over multiple lists simultaneously |
possibly() , safely() |
Handle errors gracefully |
reduce() |
Perform iterative reduction (e.g., cumulative sums, merging) |
walk() |
Apply functions for side effects (e.g., printing, logging) |
📊 Example 1: Applying a Function to a List of Values Link to heading
Imagine we need to take the square root of a list of numbers, handling potential errors.
➡️ Base R approach using a for-loop: Link to heading
numbers <- list(4, 9, 16, 25, -1)
results <- c()
for (x in numbers) {
results <- c(results, sqrt(x))
}
✔️ Works, but not scalable for more complex cases.
➡️ {purrr}
approach using map()
:
Link to heading
library(purrr)
numbers <- list(4, 9, 16, 25, -1)
results <- map(numbers, sqrt)
✅ More concise and readable
✅ Handles list structures effortlessly
✅ Easily extendable to more complex functions
📊 Example 2: Handling Errors with possibly()
Link to heading
Some operations might fail. {purrr}
helps us handle errors gracefully using possibly()
.
➡️ Handling errors in a safe way: Link to heading
safe_sqrt <- possibly(sqrt, otherwise = NA)
results <- map(numbers, safe_sqrt)
✅ Prevents the entire operation from failing due to a single error
📊 Example 3: Iterating Over Two Lists with map2()
Link to heading
We can iterate over two lists simultaneously.
➡️ Multiply corresponding elements in two lists: Link to heading
list1 <- list(2, 4, 6)
list2 <- list(3, 5, 7)
map2(list1, list2, ~ .x * .y)
✅ Efficiently processes paired elements from two lists.
📊 Example 4: Working with Data Frames Using pmap()
Link to heading
When we need to apply a function across multiple columns in a dataframe, pmap()
is the best choice.
➡️ Example: Compute a BMI column Link to heading
library(dplyr)
df <- tibble(Weight = c(70, 80, 65), Height = c(1.75, 1.82, 1.68))
df <- df %>%
mutate(BMI = pmap_dbl(list(Weight, Height), ~ .x / (.y^2)))
✅ Computes BMI for each row efficiently.
📊 Example 5: Reducing a List Using reduce()
Link to heading
If we need to perform cumulative operations, reduce()
is our go-to function.
➡️ Compute the cumulative sum of a list: Link to heading
values <- list(1, 2, 3, 4, 5)
sum_result <- reduce(values, `+`)
✅ Returns the total sum of all values in the list.
📊 Example 6: Using walk()
for Side Effects
Link to heading
walk()
is similar to map()
but is used when the function has side effects (e.g., printing, saving files, logging messages).
➡️ Print each element in a list: Link to heading
walk(numbers, print)
✅ Runs functions without returning output, useful for logging or debugging.
📈 Complete Workflow: Functional Programming with {purrr}
Link to heading
Let’s put everything together into a cohesive pipeline.
library(purrr)
library(dplyr)
# Sample dataset
df <- tibble(
Name = c("Alice", "Bob", "Charlie"),
Score1 = c(85, 90, 78),
Score2 = c(80, 95, 88)
)
# Apply functions using purrr
df <- df %>%
mutate(
Avg_Score = map2_dbl(Score1, Score2, ~ mean(c(.x, .y))),
Score_Summary = pmap_chr(list(Name, Score1, Score2),
~ paste(.x, "has an average score of", mean(c(.y, .z))))
)
✅ Uses map2()
and pmap()
to efficiently compute new variables
📌 Key Takeaways Link to heading
✅ {purrr}
makes iteration in R more intuitive and powerful.
✅ map()
and related functions streamline repetitive tasks.
✅ possibly()
and safely()
help with robust error handling.
✅ map2()
and pmap()
simplify multi-list operations.
✅ reduce()
and walk()
offer advanced functional programming workflows.
📌 Next up: String Manipulation Made Easy with stringr
! Stay tuned! 🚀
👇 How do you currently handle iteration in R? Let’s discuss!
#Tidyverse #purrr #RStats #DataScience #Bioinformatics #OpenScience #ComputationalBiology