Passing a function argument to other arguments which are functions themselves - r

Assume I have an outer function that has a numeric argument and an argument which is a function itself (inner function). How can I pass the value of the numeric argument of the outer function as an argument to the inner function? Consider this toy example:
innerfun <- function(M){
1:M
}
outerfun <- function(x, fun){
x * fun
}
outerfun(x = 3, fun = innerfun(M = 3)) ## works
outerfun(x = 3, fun = innerfun(M = x)) ## error because innerfun can't find 'x'
outerfun(x = 3, fun = innerfun(M = get("x"))) ## doesn't work either...
So what I want to do is to call innerfun at the moment the arguments of outerfun are evaluated, using those outerfun-arguments in the call to innerfun. Any ideas or suggestions?

I would do something like this :
outerfun <- function(x, fun,...){
x * fun(x,...)
}
innerfun <- function(M){
seq_len(M) ## safer than 1:M
}
outerfun(x=3, innerfun)
[1] 3 6 9
Note that If inner function has more than one argument, it still works :
innerfun2 <- function(M,Z){
seq(M+Z)
}
outerfun(x=3, innerfun2,Z=3)
[1] 3 6 9 12 15 18

Add a "global" variable:
param = 3;
outerfun(x = param, fun = innerfun(M = param))

Related

Apply function to cartesian product of numeric and function type

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).
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
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.

Call function in R with arguments given in array form

I have a function in R with a lot of default arguments, that I want to use multiple times. I want to be able to change those without having to write it all the time.
This is easy in Python like this:
def f(x, y, optional1=1, optional2=2, optional3=3):
return (x+y+optional1+optional2+optional3)
args = (10, 20)
print(f(1,2, *args)) #this prints 36 = 1+2+10+20+3
now I have an R function f = function(x,y, optional1=1, optional2=2, optional3=3) but I have not found a way of doing something similar to the example above.
I was hoping something could be done with the ellipsis (...) but seems like that's only for a variable amount of arguments in the function definition.
Doing something like
g = function(x,y){
return(f(x,y, 10, 20, 30)
}
would work but I was wondering if there was a cleaner and more readable way of doing this.
Thanks.
There are a few ways, but you can use do.call().
f <- function(x, y, optional1 = 1, optional2 = 2, optional3 = 3){
return(x + y + optional1 + optional2 + optional3)
}
f(1, 2, 10, 20)
# [1] 36
Using do.call() this will look like the following.
args <- list(optional1 = 10, optional2 = 20)
do.call(f, c(x = 1, y = 2, args))
# [1] 36
Another way using rlang saves a bit of typing, but is generally the same thing.
library(rlang)
exec(f, x = 1, y = 2, !!!args)
#[1] 36
If you are doing this a lot, you can take the approach you started and wrap a function.
g <- function(x, y, arg_list) {
do.call(f, c(x = x, y = y, arg_list))
}
g(1, 2, args)
# [1] 36

List objects as function arguments with overridable list element defaults

I have an R function which takes a large number of arguments (18) which I would like to pass in as a list. When I am running this function by hand, so to speak, I generally want to use all the defaults but one or two, but I also want to run this same function many times with various combinations of default and non-default items, which I would like to assemble programmatically as lists.
I know that I could just have my 18+ arguments as individual formals and then assemble them into a list inside the function, but I wish I could have a list as a default for a formal, and then have the elements have defaults as well. Like this:
> f <<- function(x, y = list(a=0, b=3)) {with(y, (x + a + b))}
> f(1)
[1] 4
> f(x=1, y$a = 1)
Error: unexpected '=' in "f(x=1, y$a ="
(or alternatively)
In y$a <- 1 :
Error in eval(substitute(expr), data, enclos = parent.frame()) :
object 'a' not found
except with the output of 5 rather than an error. I suspect there is no way to do this, because R does not recognise the assignments in the list as creating defaults, but only as creating named elements. But maybe with the assignment form of formals? or through some clever use of do.call?
Here are some alternatives:
1) modifyList Use modifyList to process the defaults.
f1 <- function(x, y = list()) {
defaults <- list(a = 0, b = 3)
with(modifyList(defaults, y), {
x + a + b
})
}
f1(x = 1)
## [1] 4
f1(x = 1, y = list(a = 1))
## [1] 5
2) do.call Another possibility is to have two functions. The first does not use a list and the second (which is the one the user calls) does using do.call to invoke the first.
f2impl <- function(x, a = 0, b = 3) x + a + b
f2 <- function(x, y = list()) do.call("f2impl", c(x, y))
f2(x = 1)
## [1] 4
f2(x = 1, y = list(a = 1))
## [1] 5

How can I decorate a function in R?

I'm trying to instrument some functions in R, by replacing them with my own versions which record some information and then call the original versions. My trouble is, how do I construct the call such that it exactly replicates the original arguments.
First, we'll set up an environment
env <- new.env()
One way I could do this is by getting the call and modifying it. However, I don't know how to do this with a primitive function.
## Option 1: get the call and modify it
env$`[` <- function(x, i, j, ...) {
my.call <- match.call()
## This isn't allowed.
my.call[[1]] <- as.name(.Primitive("["))
record.some.things.about(i, j)
eval(my.call, envir = parent.frame())
}
Alternatively, I could just get the arguments and use do.call. However, I haven't worked out how to extract the arguments correctly.
## Option 2: do a call with the arguments
env$`[` <- function(x, i, j, ...) {
## This outputs a list, so if 'i' is missing, then 'j' ends up in the 'i' position.
## If I could get an alist here instead, I could keep 'j' in the right position.
my.args <- match.call()[-1]
record.some.things.about(i, j)
do.call(
.Primitive("["),
my.args,
envir = parent.frame()
)
}
Then, if we've done things right, we can eval some expression which uses [ inside the enviroment we've constructed, and our instrumented function will be called instead:
## Should return data.frame(b = c(4, 5, 6))
evalq(
data.frame(a = c(1, 2, 3), b = c(4, 5, 6))[, "b"],
envir = env
)
Can anyone help/advise?
Use trace:
trace(`[.data.frame`, quote({print("hello!")}), at = 1)
iris[1,]
#Tracing `[.data.frame`(iris, 1, ) step 1
#[1] "hello!"
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#1 5.1 3.5 1.4 0.2 setosa
Can't you just capture everything for original function args with triple dots arg, that gets passed on to the original function?
sum <- function(..., na.rm = TRUE) {
print("my sum") # here is where you can "record some info"
base::sum(..., na.rm = na.rm) # then call original function w/ ...
}
base::sum(5,5, NA)
##[1] NA
# your function
sum(5,5, NA)
##[1] "my sum"
##[1] 10

In R - Passing functions and their parameters as parameters to top order function

I am trying to do something much more complex than my example below, but the basic idea is encapsulated in this example:
pass_thru <- function(FUN,params){
n <- length(FUN)
out <- list()
for(i in 1:n){
temp <- get(FUN[i],mode="function")
out[[i]] <- temp(params[[i]])
}
return(out)
}
fun1 <- function(x,y,z){
x+y+z
}
fun2 <- function(l,m,n){
l*m*n
}
FUN = c("fun1","fun2")
params = list(c(x=1,y=2,z=3,
l=4,m=5,n=6))
pass_thru(FUN,params)
The passing and parsing of FUN within pass_thru() works fine, but passing params as a list only works if every element of params is a single value (only one parameter passed to each function of FUN). I am not sure how to get multiple parameters to each function in FUN to be passed to the appropriate FUN.
What I really want to be able to do is to pass some of the parameters to each FUN in my call to pass_thru(), and have the body of pass_thru calculate the rest of the parameters to be passed to fun1 and fun2.
I am trying to create a flexible architecture upfront for large-scale data analysis, so having the ability to pass functions as well as any or all of those functions' parameters to other functions would be of great help. Thank you for any insights you have into this question!
Use do.call like this and also fix params as shown:
params <- list(list(x = 1, y = 2, z = 3), list(l = 4, m = 5, n = 6))
pass_thru <- function(FUN, params) Map(do.call, FUN, params)
pass_thru(FUN, params)
giving:
$fun1
[1] 6
$fun2
[1] 120
If you really did want:
params2 <- list(x = 1, y = 2, z = 3, l = 4, m = 5, n = 6)
then try this noting that the params[intersect(...)] part picks out the relevant parameters for the function call:
pass_thru2 <- function(FUN, params) {
runf <- function(f) do.call(f, params[intersect(names(params), names(formals(f)))])
lapply(FUN, runf)
}
pass_thru2(FUN, params2)
giving the same result.

Resources