25
votes

I am using R purrr:::pmap with three inputs. It is not clear how I can refer explicitly to these inputs in the formula call? When using map2, the formula call goes as ~ .x + .y. But how to do when using pmap?

Reproducing Hadley's example from http://r4ds.had.co.nz/lists.html

library(purrr)
mu <- list(5, 10, -3)
sigma <- list(1, 5, 10)
n <- list(1, 3, 5)

args2 <- list(mean = mu, sd = sigma, n = n)
pmap(args2, rnorm)

If I want to refer explicitly to the input arguments when calling rnorm, I can use:

pmap(args2, function(mean, sd, n) rnorm(n, mean, sd))

But say I want to do this with the formula approach. How do I do that? This for example does not work:

pmap(args2, ~rnorm(n=.n, mean=.mean, sd=.sd))

Thanks!!

3

3 Answers

17
votes

Since version 0.2.3 you can use ..1, ..2, ..3 and so on:

pmap(args2, ~ rnorm(..3, ..1, ..2))

But... I've already ran into trouble with this syntax, for instance with replicate:

pmap(list(1, 2), ~ replicate(n = ..1, expr = ..2))
# Error in FUN(X[[i]], ...) : the ... list does not contain 2 elements

Probably because of:

print(replicate)
# function (n, expr, simplify = "array") 
#   sapply(integer(n), eval.parent(substitute(function(...) expr)), 
#          simplify = simplify)

It seems the function(...) expr in substitute() does not play well with ..2, being interpreted as the second element of ... which is empty.

Note that pmap(list(1, 2), ~ replicate(n = ..1, expr = .y)) still works.

14
votes

You can use with(...) to solve this :

pmap(args2, ~with(list(...),rnorm(n, mean, sd)))
# [[1]]
# [1] 2.733528
# 
# [[2]]
# [1] 4.0967533 6.4926143 0.6083532
# 
# [[3]]
# [1]  1.8836592 -0.2090425 -4.0030168  1.1834931  3.2771316

More explanations here: Harnessing .f list names with purrr::pmap

5
votes

It seems that pmap cannot access the arguments in a list by its name in the formula interface. You can check in https://github.com/hadley/purrr/issues/203.

For instance you can do :

pmap(list(1:2, 5:6), ~ .x + .y)

Thus the first element of the list is referred by .xand the second by .y. However if you try to name the argument of the list as in

pmap(list(a = 1:2, b =  5:6), ~ .a + .b)

then you will have the error:

Error in .f(a = .l[[c(1L, i)]], b = .l[[c(2L, i)]], ...) : 
  unused arguments (a = .l[[c(1, i)]], b = .l[[c(2, i)]])

I think that in the formula interface of the function pmap the best you could do if you want to use the formula interface and not use function(mean , sd, n) is to :

  1. not name the elements of your list
  2. not use more than two arguments (in order to use the implicit name .x and .y)

You can thus use thus fix the value of your third argument n (for instance n = 4) you want and then run:

mu <- list(5, 10, -3)
sigma <- list(1, 5, 10)
set.seed(1)
pmap(list(mu,sigma), ~ rnorm(mean = .x, sd = .y, n = 4))

Which will return :

[[1]]
[1] 4.373546 5.183643 4.164371 6.595281

[[2]]
[1] 11.647539  5.897658 12.437145 13.691624

[[3]]
[1]  2.7578135 -6.0538839 12.1178117  0.8984324

[[4]]
[1]  9.136278  4.355900 14.374793 10.865199