Applying a list of functions to a list of arguments - r

What is the R-idiomatic way of creating a list gotten by applying a list of functions to a list of arguments?
For example, given a list of functions and a list arguments, equal in number (say, three),
fncs <- list(f1, f2, f3)
args <- list(x1, x2, x3)
I want to get the list of function values,
fnc_vals <- list(f1(x1), f2(x2), f3(x3))
In other words, I want to apply a generalized form of the base-R function mapply to the lists fncs and args (which applies a single function to a list of arguments).
This is can easily be done:
dapply <- function(args, fncs, ...) { # "diagonal" apply
lapply(seq_along(args), function(i) fncs[[i]](args[[i]], ...))
}
fnc_vals <- dapply(args, fncs)
However, to my eyes, this is clumsy, because it would be more natural—if possible in R, without contortions—to run pairwise through the functions and arguments to produce the desired function-value list; in Python, that would go like this:
fncs = [f1, f2, f3]
args = [x1, x2, x3]
fnc_vals = [f(x) for f, x in zip(fncs, args)]
Can this “zippy” construction be implemented in R, as well?

You can use Map (which is mapply with different defaults) with do.call:
fncs <- list(mean, median, sd)
args <- list(1:5, 11:13, c(1,1,1,2))
Map(function(f, x) do.call(f, list(x)), fncs, args)
#[[1]]
#[1] 3
#
#[[2]]
#[1] 12
#
#[[3]]
#[1] 0.5
The anonymous function is necessary because do.call needs a list for its second argument.
Or since you write an anonymous function anyway, you can just use Map(function(f, x) f(x), fncs, args) as #akrun points out.

Related

Perform a series of operations on a vector in R without a loop

I've got a vector that I want to perform the same operation on multiple times in series, modifying one argument, and I can't figure out a way to do it without a loop.
For example, say I want to take the vector c(1, 2, 3) and I want to perform a series of logs on it. In a loop, I would do this:
foo <- 30:32
for(anum in c(2,3,4)){
foo <- log(foo, base = anum)
}
A way to do it without a loop would be:
30:32 |>
log(base = 2) |>
log(base = 3) |>
log(base = 4)
What I'm wanting is to have a function that will take 2:4 as an argument and do this. So something like:
30:32 |> serialfunction(log, 2:4)
Does a function like this exist?
Thanks!
There's not really a built-in function that does exactly that. But you can do this with Reduce. For example
Reduce(function(x, y) {log(x, base=y)}, 2:4, init=30:32)
# [1] 0.2669627 0.2713007 0.2754373
You could create serial function with
serialfunction <- function(x, fun, y) {
Reduce(function(x, y) {fun(x, y)}, y, init=x)
}
30:32 |> serialfunction(log, 2:4)
# [1] 0.2669627 0.2713007 0.2754373

Specify which argument is supplied by X in lapply

I've got a function, e.g. fun(a, b = 1, c = 3, ...), that takes a number of arguments, some of which have default values. I would like to call this function using lapply(X, FUN, ...), but specify explicitly which argument I would like X to supply. In the example above, the X vector could be supplied for a or b or c, or xyz in the ....
Normally I might call lapply(1:5, fun, a = 4) and I imagine it would use 1:5 as the b argument.
Is there a way to make that more explicit?
What if I want to use the default argument for b and use 1:5 for c?
What if I want to use 1:5 as an xyz argument in the ...?
Normally I might call lapply(1:5, fun, a = 4) and I imagine it would use 1:5 as the b argument.
Yes, your imagination is correct. lapply uses positional matching to pass its X parameter to the function. Normal rules of argument matching apply, which means exact matching of named parameters takes precedence.
An alternative would of course be to wrap fun in an anonymous function:
lapply(1:5, function(b, a, ...) fun(a = a, b = b, ...), a = 4)
One way to handle you use case would be to simply call your own function inside the custom function which lapply exposes to you:
lst <- list(v1=c(1:3), v2="Hello", v3=5)
result <- lapply(lst, function(x) {
y <- FUN(x, a, b, c, ...) # Here FUN() is your own function
return(y)
})

R - how do I run each function in a list of functions with parameters given by a vector

Suppose I have a list of functions and a vector of parameter values:
functions <- list(a = function(x) x *2, b = function(x) x*3, c = function(x) x * 4)
paramVector <- c(2, 2, 1)
Now I want the following functionality of calling each function with the corresponding parameter and collating the result into a vector:
result <- c()
for (idx in 1:length(functions)) {
result[idx] <- functions[[idx]](paramVector[idx])
}
result
Is there a way to do this without the for loop?
To iterate over the functions and paramVector objects at the same time, use Map. For example
Map(function(f,p) f(p), functions, paramVector)
Note that Map() always returns a list. You can also use mapply() which will attempt to simplify to a vector
mapply(function(f,p) f(p), functions, paramVector)

R df process each column by a different function provided in a list of functions

I guess my problem is very simple, but I could not find the solution in web yet.
I would like to modify a data frame with a set of functions.
The functions are defined in a list. They may have more than one argument, but one arg is always the value found on the related column in a df.
I used build in BOD data set just for convinience. The list could be this:
funs <- list(
fn1 = function(x) x+1,
fn2 = function(x) x-1
)
The function call could look like this:
searchedFunc(BOD, funs)
So after modificatin Time column values are added by 1 and demand column values are subtracted by one.
You can use sapply to be more flexible
funs <- list(
fn1 = function(x) x+1,
fn2 = function(x) x-1
)
searchedFunc <- function(df, fns) {
sapply(seq(along.with=fns), function(i) fns[[i]](df[, i]))
}
searchedFunc(BOD, funs)
Hope it helps,
alex

Apply family of functions for functions with multiple arguments

I would like to use a function from the apply family (in R) to apply a function of two arguments to two matrices. I assume this is possible. Am I correct? Otherwise, it would seem that I have to put the two matrices into one, and redefine my function in terms of the new matrix.
Here's an example of what I'd like to do:
a <- matrix(1:6,nrow = 3,ncol = 2)
b <- matrix(7:12,nrow = 3,ncol = 2)
foo <- function(vec1,vec2){
d <- sample(vec1,1)
f <- sample(vec2,1)
result <- c(d,f)
return(result)
}
I would like to apply foo to a and b.
(Strictly answering the question, not pointing you to a better approach for you particular use here....)
mapply is the function from the *apply family of functions for applying a function while looping through multiple arguments.
So what you want to do here is turn each of your matrices into a list of vectors that hold its rows or columns (you did not specify). There are many ways to do that, I like to use the following function:
split.array.along <- function(X, MARGIN) {
require(abind)
lapply(seq_len(dim(X)[MARGIN]), asub, x = X, dims = MARGIN)
}
Then all you have to do is run:
mapply(foo, split.array.along(a, 1),
split.array.along(b, 1))
Like sapply, mapply tries to put your output into an array if possible. If instead you prefer the output to be a list, add SIMPLIFY = FALSE to the mapply call, or equivalently, use the Map function:
Map(foo, split.array.along(a, 1),
split.array.along(b, 1))
You could adjust foo to take one argument (a single matrix), and use apply in the function body.
Then you can use lapply on foo to sample from each column of each matrix.
> a <- matrix(1:6,nrow = 3,ncol = 2)
> b <- matrix(7:12,nrow = 3,ncol = 2)
> foo <- function(x){
apply(x, 2, function(z) sample(z, 1))
}
> lapply(list(a, b), foo)
## [[1]]
## [1] 1 6
## [[2]]
## [1] 8 12

Resources