map2() and pmap() for Multiple Inputs
Iterate over two or more lists simultaneously with map2() and pmap().
map2() and pmap() for Multiple Inputs is a free R Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Iterating Over Multiple Inputs
map() iterates over one input at a time. But many functions need two or more parallel inputs. purrr's map2() handles two inputs; pmap() handles any number. Both maintain the same type-safe return value guarantee.
library(purrr)
# Simulate: add corresponding elements from two vectors
x <- c(1, 2, 3, 4)
y <- c(10, 20, 30, 40)
# map2 applies fn(x[i], y[i]) for each i
result <- map2(x, y, function(a, b) a + b)
print(result)map2() — Two Parallel Inputs
map2(.x, .y, .f) applies .f(x_i, y_i) to corresponding elements of .x and .y. Both vectors must have the same length (or one has length 1 and is recycled). Returns a list.
library(purrr)
names_vec <- c('Alice','Bob','Carol')
scores_vec <- c(85, 92, 78)
# Build a personalized message for each student
map2_chr(names_vec, scores_vec,
~paste(.x, 'scored', .y, 'points'))map2() with ~ Shorthand
The formula shorthand ~expr works with map2(): .x refers to the first input and .y refers to the second. This keeps two-argument anonymous functions concise.
library(purrr)
lower_bounds <- c(0, 10, 20, 50)
upper_bounds <- c(9, 19, 49, 100)
# Create labels for each range
map2_chr(lower_bounds, upper_bounds,
~paste0('[', .x, '-', .y, ']'))Typed map2() Variants
Like map(), map2() has typed variants for type-safe returns: map2_dbl(), map2_chr(), map2_lgl(), map2_int(), and map2_df(). Use the variant matching your expected output type.
library(purrr)
actual <- c(100, 150, 200, 120)
target <- c(110, 140, 195, 130)
# Numeric: percentage achievement
map2_dbl(actual, target, ~round(100 * .x / .y, 1))
# Logical: did they meet their target?
map2_lgl(actual, target, ~.x >= .y)pmap() — Any Number of Inputs
pmap(.l, .f) takes a list of vectors as its first argument. Each vector is a separate input stream. The function receives corresponding elements from all input streams simultaneously.
library(purrr)
# Three parallel inputs
params <- list(
name = c('Alice','Bob','Carol'),
score = c(85, 92, 78),
grade = c('B','A','C')
)
pmap_chr(params, function(name, score, grade) {
paste(name, ':', score, '(', grade, ')')
})pmap() with a Data Frame
A data frame is a list of columns — so you can pass a data frame directly as the first argument to pmap(). The function receives one row's values as named arguments, matching column names to parameter names.
library(purrr)
params_df <- data.frame(
mean_val = c(0, 5, 10),
sd_val = c(1, 2, 3),
n = c(100, 50, 200)
)
# Generate samples from different normal distributions
set.seed(42)
results <- pmap(params_df, function(mean_val, sd_val, n) {
rnorm(n, mean=mean_val, sd=sd_val)
})
map_dbl(results, mean)Iterating Over Parameter Grids
A common use of pmap(): iterate over a grid of model parameters. Use expand.grid() or tidyr::expand_grid() to create the parameter combinations, then pmap() to run the experiment for each row.
library(purrr)
# Create a parameter grid
grid <- expand.grid(
learning_rate = c(0.01, 0.1),
epochs = c(10, 50),
batch_size = c(32, 64)
)
# Simulate model training results
set.seed(42)
grid$accuracy <- pmap_dbl(grid, function(learning_rate, epochs, batch_size) {
base <- 0.5 + 0.3 * log10(epochs) + 0.1 * learning_rate
round(min(base + rnorm(1, 0, 0.02), 0.99), 3)
})
print(grid[order(-grid$accuracy),])map2_df() — Building Data Frames
map2_df() (or map2_dfr()) applies a function to two parallel inputs and row-binds the resulting data frames. This is powerful for comparing two sets of items or merging parallel datasets.
library(purrr)
q1_sales <- list(East=100, West=200, North=80)
q2_sales <- list(East=120, West=190, North=95)
map2_df(q1_sales, q2_sales, function(q1, q2) {
data.frame(
q1 = q1,
q2 = q2,
change = q2 - q1,
pct = round(100*(q2-q1)/q1, 1)
)
}, .id = 'region')Combining pmap() with nest()
Pairing pmap() with nested data frames and model parameters unlocks a powerful pattern: store hyperparameters and data in one data frame, then pmap() over rows to fit models with each parameter set.
library(purrr)
library(dplyr)
# Simulate polynomial regression with different degrees
set.seed(42)
x <- 1:20
y <- 2*x + 0.5*x^2 + rnorm(20, 0, 10)
df <- data.frame(x=x, y=y)
configs <- data.frame(degree = 1:3)
configs$r2 <- map_dbl(configs$degree, function(d) {
m <- lm(y ~ poly(x, d), data=df)
summary(m)$r.squared
})
print(configs)Error Handling with possibly()
In map2()/pmap() workflows, one bad input can crash the entire operation. Wrap your function with possibly(fn, otherwise=NA) to catch errors and return a default value, keeping the pipeline running even when some inputs fail.
library(purrr)
safe_log <- possibly(log, otherwise=NA_real_)
values <- list(10, -5, 100, 0, 50)
# Without possibly(): log() warns for negative numbers
# With possibly(): errors return NA, computation continues
map_dbl(values, safe_log)Practical: Cross-Validation with pmap()
A practical pmap() application: run k-fold cross-validation by iterating over fold assignments. pmap() dispatches each fold's training and test data to the fitting function, returning a performance metric per fold.
library(purrr)
set.seed(42)
df <- data.frame(x=1:20, y=2*(1:20)+rnorm(20,0,3))
df$fold <- sample(rep(1:5, 4))
# Compute RMSE for each held-out fold
folds <- 1:5
rmse_vals <- map_dbl(folds, function(k) {
train <- df[df$fold != k, ]
test <- df[df$fold == k, ]
m <- lm(y ~ x, data=train)
preds <- predict(m, newdata=test)
sqrt(mean((test$y - preds)^2))
})
cat('Per-fold RMSE:', round(rmse_vals, 2), '\n')
cat('Mean RMSE:', round(mean(rmse_vals), 2))Quick Check
Which purrr function is appropriate for iterating over more than two parallel input vectors simultaneously?
Recap: map2() and pmap()
Key takeaways for multiple-input mapping:
map2(.x, .y, .f)— iterates over two parallel inputs;.xand.yin~shorthandmap2_dbl/chr/lgl/int/df()— typed variants for type-safe returnspmap(.l, .f)— iterates over a list of any number of vectors simultaneously- Pass a data frame to
pmap()— column names become function parameter names - Use
expand.grid()+pmap()for parameter grid search - Wrap functions with
possibly(fn, NA)for safe error handling
library(purrr)
# pmap with a data frame: each row = one function call
params <- data.frame(
mean_val = c(0, 10, 100),
sd_val = c(1, 5, 20),
label = c('control','mid','high')
)
pmap_chr(params, function(mean_val, sd_val, label) {
sprintf('%s: mean=%.0f, sd=%.0f', label, mean_val, sd_val)
})Frequently asked questions
Is the “map2() and pmap() for Multiple Inputs” lesson free?
Yes — the full text of “map2() and pmap() for Multiple Inputs” is free to read here on the web, and the R Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the R Academy course, upgrade to CoddyKit PRO.
What will I learn in “map2() and pmap() for Multiple Inputs”?
Iterate over two or more lists simultaneously with map2() and pmap(). You practise R Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start R Academy?
No prior experience is required. R Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “map2() and pmap() for Multiple Inputs” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this R Academy lesson?
Yes. Every R Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- map() and Typed Variants
- map2() and pmap() for Multiple Inputs
- reduce(), accumulate(), and walk()
- keep(), discard(), and List Filtering