Skip to Content
Course content

52: purrr Functional Programming In Depth

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you have a stack of a hundred envelopes that all need the same official company stamp on the top-right corner. You could pick up one envelope, stamp it, put it down, and then reach for the next one. That's essentially what a for loop does. It's manual, it's repetitive, and if you get distracted halfway through, you might lose your place.

Now, imagine you have a stamping machine. You feed the entire stack into the machine, and it applies the stamp to every single envelope in one continuous, automated motion. You don't manage the "looping" yourself; you just define the stamp (the function) and the stack (the data), and the machine handles the rest. That is the essence of purrr.

Here is how that maps to the code we're about to write:

  • The Stack: This is your vector or list. In R, this is often a list of file paths, a list of data frames, or a character vector of IDs.
  • The Stamp: This is the function you want to apply. It could be a built-in function like mean() or a custom function you wrote to clean a specific type of messy string.
  • The Machine: This is the map() family of functions. They take the stack and the stamp and produce a new stack of results.

Trading loops for type-stability

I'll be honest with you: you can do almost anything purrr does using a for loop. But the reason I prefer purrr—and why you should too—is type-stability. When you use a standard for loop, you have to pre-initialize an empty vector or list to hold your results. If you forget, or if you initialize it as the wrong type, R will either complain or start "coercing" your data into something you didn't want, which is a nightmare to debug.

purrr solves this with specialized versions of map(). If you know you want a numeric vector back, don't use map() (which always returns a list); use map_dbl(). If you want strings, use map_chr(). It forces you to be intentional about what your code is producing.

library(purrr)

# Let's say we have a list of numeric vectors
my_data <- list(a = 1:5, b = 6:10, c = 11:15)

# map() returns a list. Good for complex objects, overkill for single numbers.
list_results <- map(my_data, mean) 

# map_dbl() returns a numeric vector. Clean, concise, and predictable.
vec_results <- map_dbl(my_data, mean)
# Result: c(a = 3, b = 8, c = 13)

The shorthand that saves your fingers

Writing a full function just to do one tiny thing inside a map() call feels tedious. This is where the tilde ~ comes in. It's basically a shortcut for saying "create an anonymous function here." Inside these shortcuts, we use .x to represent the "current item" being stamped.

Check out the difference here. Instead of writing a whole function(x) { ... } block, we do this:

# I want to square every number and then add 10
# The old way:
map_dbl(my_data, function(x) sum(x^2) + 10)

# The purrr way:
map_dbl(my_data, ~ sum(.x^2) + 10)

Taming the "List-Column" beast

In the real world, you'll often encounter "list-columns" in a tibble. This is where a single cell in a data frame actually contains an entire list or another data frame. It looks intimidating, but it's actually the superpower of the Tidyverse. You can use map() directly inside a mutate() call to perform calculations on those lists without ever leaving the data frame.

For example, if you have a column where each cell is a list of temperature readings for a day, you can create a new column for the "daily average" by mapping mean across that column. It keeps your data tidy and avoids the need to split your data into separate objects and merge them back together later.




📋 Practical Task

Processing Quarterly Financial CSVs

You have been handed four CSV files representing quarterly sales: "q1_sales.csv", "q2_sales.csv", "q3_sales.csv", and "q4_sales.csv". Each file has the same columns: date, product_id, and revenue.

Your goal is to write a purrr pipeline that does the following:

  1. Creates a character vector of the four file names.
  2. Uses map() to read all four files into a list of data frames.
  3. Uses map_dbl() on that list to calculate the total sum of revenue for each quarter.
  4. Combines these four totals into a single numeric vector.

Constraint: Do not use a for loop. Use the map family of functions to handle the iteration. Assume the files exist in your current working directory.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.