Home > database >  Apply function to cartesian product of numeric and function type
Apply function to cartesian product of numeric and function type

Time:08-04

I have a function

eval_ = function(f, i) f(i)

for a list of functions, say

fns = list(function(x) x**2, function(y) -y)

and a vector of integers, say

is = 1:2

I would like to get eval_ evaluated at all combinations of fns and is.

I tried the following:

cross = expand.grid(fns, is)
names(cross) = c("f", "i")
results = sapply(1:nrow(cross), function(i) do.call(eval_, cross[i,]))

This throws an error:

Error in f(i) : could not find function "f"

I think that the underlying problem is, that cross is a data.frame and can not carry functions. Hence, it puts the function into a list and then carries a list (indeed, class(cross[1,][[1]]) yields "list". My ugly hack is to change the third line to:

results = sapply(
    1:nrow(cross), 
    function(i) do.call(eval_, list(f = cross[i,1][[1]], i = cross[i,2]))
)

results
#[1]  1 -1  4 -2

This works, but it defeats the purpose of do.call and is very cumbersome.

Is there a nice solution for this kind of problem?

Note: I would like a solution that generalizes well to cases where the cross product is not only over two, but possibly an arbitrary amount of lists, e.g. functions that map R^n into R.

Edit: For a more involved example, I think of the following: fns = list(mean, sum, median) is1 = c(1, 2, 4, 9), ..., isn = c(3,6,1,2) and my goal is to evaluate the functions on the cartesian product spanned by is1, ..., isn, e.g. on the n-dimensional vector c(4, ..., 6).

CodePudding user response:

An attempt for my "more involved example" with n = 2.

Let X = expand.grid(c(1, 2, 4, 9), c(3,6,1,2)). The following pattern generalizes to higher dimensions:

nfns = length(fns)
nn = nrow(X)
res = array(0, c(nfns, nn))
for(i in 1:nfns){
    res[i,] = apply(X, MARGIN = 1, FUN = fns[[i]])
}

The shape of the margin of X (i.e. nrow(X)) must correspond to the shape of the slice res[i,] (i.e. nn). The function must map the complement of the margin of X (i.e. slices of the form X[i,]) to a scalar. Note that a function that is not scalar has components that are scalar, i.e. in a non-scalar case, we would loop over all components of the function.

CodePudding user response:

You can use mapply() for this:

eval_ <- function(f, i) f(i)
fns <- list(function(x) x**2, function(y) -y)
is <- 1:2
cross <- expand.grid(fns = fns, is = is)
cross$result <- mapply(eval_, cross$fn, cross$is)
print(cross)
#>                  fns is result
#> 1 function (x) , x^2  1      1
#> 2  function (y) , -y  1     -1
#> 3 function (x) , x^2  2      4
#> 4  function (y) , -y  2     -2
  • Related