2

I'm trying to compute a function whose arguments are names of a column in a data frame. I need to loop over each row and compute the function. It seems pmap is a neat way to do this, but I'm forced to specify the " ..1, ..2, " notation to indicate column positions in the data frame. That's not a very reproducible way of running this I think.

Although, it just knows the names of the columns when I use an anonymous function, instead of a named one.

library(purrr)
#> Warning: package 'purrr' was built under R version 3.6.3
toy_df <- data.frame(a = 1:10, b = 2:11, c = 3:12) 
toy_function <- function(a, b, c) {
  data.frame(result = a^2 + b^3 + log(a)*c + sin(a)*b)
}

## this fails  
toy_fail <- purrr::pmap(toy_df, ~ toy_function())
#> Error in data.frame(result = a^2 + b^3 + log(a) * c + sin(a) * b): argument "a" is missing, with no default

## this works  
toy_pass <- purrr::pmap(toy_df, ~ toy_function(..1, ..2, ..3))


## this works and I didn't need to specify the positions  
toy_also_pass <- purrr::pmap(toy_df, function(a, b, c){
  data.frame(result = a^2 + b^3 + log(a)*c + sin(a)*b)
})

Created on 2020-07-28 by the reprex package (v0.3.0)

2 Answers2

3

In base R:

do.call(rbind, do.call(Map, c(list(f = toy_function), toy_df)))
#        result
# 1    10.68294
# 2    36.50048
# 3    79.05754
# 4   145.53375
# 5   246.51252
# 6   391.37817
# 7   583.76908
# 8   822.69864
# 9  1109.29066
# 10 1452.64679

In tidyverse, the equivalent would be

purrr::invoke(purrr::pmap_dfr, list(.f = toy_function, .l = toy_df))

but it allows for a much simpler

purrr::pmap_dfr(toy_df, toy_function)
r2evans
  • 141,215
  • 6
  • 77
  • 149
2

Here, we don't need the anonymous function call as the column names are the same as the arguments to the function and it matches

purrr::pmap(toy_df, toy_function)

Or another option is

purrr::pmap(toy_df, ~  do.call(toy_function, as.list(c(...))))

Or using rowwise

library(dplyr)
toy_df %>%
     rowwise %>%
     transmute(result = toy_function(a, b, c))          
akrun
  • 874,273
  • 37
  • 540
  • 662
  • Oh dang that's interesting - thank you! Do we need a tilde only when we specify .x, .y in a map2 ? Is there ever a need for it in pmap? – Dilsher Singh Dhillon Jul 28 '20 at 23:09
  • *"is there a need"*? it's a lot for convenience, when the names and/or positions don't match up perfectly. When that's the case, in "traditional R", one would write `function(...) { ... }`, a verbosely-written anonymous function. `rlang` allows for a more terse description using the tilde format (though it only works here because `pmap` "knows" about rlang's tilde-funcs). – r2evans Jul 28 '20 at 23:46