Search code examples
rfunctional-programmingpurrrmagrittr

R: `Error in f(x): could not find function "f"` when trying to use column of functions as argument in a tibble


I'm experimenting with using functions in dataframes (tidyverse tibbles) in R and I ran into some difficulties. The following is a minimal (trivial) example of my problem.

Suppose I have a function that takes in three arguments: x and y are numbers, and f is a function. It performs f(x) + y and returns the output:

func_then_add = function(x, y, f) {
  result = f(x) + y
  return(result)
}

And I have some simple functions it might use as f:

squarer = function(x) {
  result = x^2
  return(result)
}

cuber = function(x) {
  result = x^3
  return(result)
}

Done on its own, func_then_add works as advertised:

> func_then_add(5, 2, squarer)
[1] 27
> func_then_add(6, 11, cuber)
[1] 227

But lets say I have a dataframe (tidyverse tibble) with two columns for the numeric arguments, and one column for which function I want:

library(tidyverse)
library(magrittr)

test_frame = tribble(
  ~arg_1, ~arg_2, ~func,
  5, 2, squarer,
  6, 11, cuber
)

> test_frame
# A tibble: 2 x 3
  arg_1 arg_2 func  
  <dbl> <dbl> <list>
1     5     2 <fn>  
2     6    11 <fn>  

I then want to make another column result that is equal to func_then_add applied to those three columns. It should be 27 and 227 like before. But when I try this, I get an error:

> test_frame %>% mutate(result=func_then_add(.$arg_1, .$arg_2, .$func))
Error in f(x) : could not find function "f"

Why does this happen, and how do I get what I want properly? I confess that I'm new to "functional programming", so maybe I'm just making an obvious syntax error ...


Solution

  • Not the most elegant but we can do:

    test_frame %>% 
      mutate(Res= map(seq_along(.$func), function(x)
            func_then_add(.$arg_1, .$arg_2, .$func[[x]]))) 
    

    EDIT: The above maps both over the entire data which isn't really what OP desires. As suggested by @January this can be better applied as:

    Result <- test_frame %>% 
      mutate(Res= map(seq_along(.$func), function(x)
           func_then_add(.$arg_1[x], .$arg_2[x], .$func[[x]])))
    
    Result$Res 
    

    The above again is not very efficient since it returns a list. A better alternative(again as suggested by @January is to use map_dbl which returns the same data type as its objects:

    test_frame %>% 
       mutate(Res= map_dbl(seq_along(.$func), function(x)
           func_then_add(.$arg_1[x], .$arg_2[x], .$func[[x]])))
    # A tibble: 2 x 4
      arg_1 arg_2 func     Res
      <dbl> <dbl> <list> <dbl>
    1     5     2 <fn>      27
    2     6    11 <fn>     227