1

I try to write a simple function wrapping around the purrr::pmap_dbl() function.

I have the following data:

df <- data.frame(
          col1 = 1:5, 
          col2 = 2:6, 
          col3 = 3:7
)

And the following function:

addfn <- function(x, y){
   x^2 + y
}

Then I would like to write a function like:

testfn <- function(data, a, b){
    purrr::pmap_dbl(data, function(a, b, ...) addfn(a, b))
}

Unfortunately, testfn(df, col1, col2) gives an error in this case. I would like to obtain the same output as the output obtained by:

purrr::pmap_dbl(df, function(col1, col2, ...) addfn(col1, col2))

What is the way to do this?

mharinga
  • 1,708
  • 10
  • 23

3 Answers3

4

You can select into your df before using it in map:

library(rlang)
library(dplyr)
library(purrr)
testfn <- function(data, x, y){
  data <- select(data, !!enquo(x), !!enquo(y))
  pmap_dbl(data, ~ addfn(.x, .y))
}

testfn(df, col1, col3) 
Colin FAY
  • 4,849
  • 1
  • 12
  • 29
  • This is a nice work around. However, I would like to know whether it is possible to call the column names directly. – mharinga Feb 02 '18 at 12:27
  • One of the main objectives of using `tidyverse` (e.g. `dplyr` and `purrr`) is to have a code that is easy to follow. I can't see how @ColinFAY 's is improving in that regard compared to the `base` R packages, see comment by akrun below. – nadizan Feb 02 '18 at 12:36
1

1) testfn First, note that using testfn from the question that this already works:

testfn(unname(df[c("col1", "col3")]))
## [1]  4  8 14 22 32

2) testfn2 and we can modify that to:

testfn2 <- function(data, ...) {
  data %>% select(...) %>% unname %>% purrr::pmap_dbl(addfn)
}

testfn2(df, col1, col3)
## [1]  4  8 14 22 32

testfn2(df, "col1", "col3")
## [1]  4  8 14 22 32

3) base R Of course as has been pointed out we don't really need purrr for this. This works in base R because addfn is vectorized:

with(df, addfn(col1, col3))
## [1]  4  8 14 22 32

or if addfn were not vectorized then:

with(df, mapply(addfn, col1, col3))
## [1]  4  8 14 22 32

and we could write this function using standard evaluation:

testfn3 <- function(data, a, b) mapply(addfn, df[[a]], df[[b]])

testfn3(df, "col1", "col3")
## [1]  4  8 14 22 32

or using non-standard evaluation:

testfn4 <- function(data, ...) eval.parent(substitute(
  with(data, mapply(addfn, ...))
))

testfn4(df, col1, col3)
## [1]  4  8 14 22 32
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
0

Or you could use apply functions.

addfn <- function(x){
  apply(x,1,function(y) y[1]^2 + y[2])
}

addfn(df[,1:2])
[1]  3  7 13 21 31

addfn(df[,2:3])
[1]  7 13 21 31 43
nadizan
  • 1,323
  • 10
  • 23
  • 1
    If you are using `base R` methods, then `x[,1]^2 + x[,2]` would be more compact and faster – akrun Feb 02 '18 at 12:21
  • Of course, but it seemed like the topic starter wanted specifically a function. But I might have misunderstood. – nadizan Feb 02 '18 at 12:33