Specify which argument is supplied by X in lapply - r

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

Related

Performing a function's criterion within another

This is fairly difficult for me to explain, however, I wish to use a function that's within a function, that can be used towards the variables.
For example, given that the function looks similar to this:
test <- function(a, b, c){
a <- ...
b <- ...
c <- ...
}
#used likeso:
test(a = ..., b = ..., c = ...)
Is it possible to use it likeso:
test(a = ..., c(b = ...))
And what's an example of how this function would look like? I'm looking for a function like this because I'm trying to index a function within a function, that can be used like the second function above.
I know that this can be achieved with two separate function, though, I'm asking as to whether its possible with one function, whilst having another function indexed within it?
Use do.call like this:
test <- function(a, b, c) a + b + c
do.call("test", list(a = 1, b = 2, c = 3))
## [1] 6

R: Use formula notation to define a function, like purrr::map(.f=)

In the gtsummary package, there are many functions that take other functions as arguments. At the moment, the user must pass a proper function, but I would like to update to allow users pass functions with shortcut notation using formula syntax (similar to how purrr::map() allows users to pass either purrr::map(.f = mean) or purrr::map(.f = ~mean(.x)). There are other functions throughout the tidyverse, for example, in dplyr that use similar notation. Here's the description from the purrr::map() help file:
I wrote a small function that converts the formula syntax into a new function. BUT, this only accepts the dot notation (e.g. ~mean(.)). How can I generalize this to accept ., .x, and ..1? In my use case, I need to call them with foo(1:5) (not referencing the argument name), and the function will only have a single argument.
# convert a formula to a function
formula_to_function <- function(x) {
function(.) eval(rlang::f_rhs(x), list(.))
}
# create a new function that is the mean of a vector
foo <- formula_to_function(~mean(., na.rm= TRUE))
# evaluate function
foo(1:5)
#> [1] 3
Created on 2020-07-03 by the reprex package (v0.3.0)
Thank you!
Internally, purrr::map uses purrr::as_mapper to parse the .f argument into a function. See here (line 110). So one option is to directly use purrr::as_mapper in your function, or you can try to rewrite as_mapper yourself (here's the implementation).
match.funfn in gsubfn is like match.fun in base R except it also accepts a formula. The formula can use any variables and it is such that any free variables (used but not defined) are assumed to be arguments in the order encountered.
library(gsubfn)
f <- function(x, y, z, fun) {
fun <- match.funfn(fun)
fun(x, y, z)
}
# test
f(1, 2, 3, ~ a + b + c)
## [1] 6
Optionally you can specify the arguments on the LHS of the formula
# same
f(1, 2, 3, a + b + c ~ a + b + c)
## [1] 6
or pass a function
# same
f(1, 2, 3, function(a, b, c) a + b + c)
## [1] 6
There are additional features so see the gsubfn package documentation for more info.
Also it is possible to pass formulas as function arguments to general functions by prefacing the function called with fn$ .
library(gsubfn)
f2 <- function(x, y, z, fun) {
fun(x, y, z)
}
fn$f2(1, 2, 3, ~ a + b + c)
## [1] 6

Applying a list of functions to a list of arguments

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.

R: sequentially applying an arbitrarty list of functions with arguments to a matrix

I have a list of filtering functions f1,f2,f3,f4,.... which take a matrix m and a number of options as input and return a subset of the rows of matrix as output. Now I would like to be able to define in an orderly way some meta-filtering function settings metaf1, metaf2, metaf3,... which would specify the sequential application of a specified nr of filtering functions, e.g. first f2 and then f3, using given options for each. I would like to store these filtering settings in a list of say class "metafiltering", and then have another function apply the filtering steps specified in a given metafiltering object. My idea would be able to in this way allow filtering settings to be stored and applied in an orderly way. How would I achieve this in the most elegant way in R? Or is there perhaps other convenient methods to achieve something like this?
EDIT: to give an example, say I have matrix
m=replicate(10, rnorm(20))
and filtering functions (these are just examples, obviously mine are more complicated :-) )
f1=function(m,opt1,opt2) {
return(m[(m[,2]>opt1)&(m[,1]>opt2),])
}
f2=function(m,opt1) {
return(m[(m[,3]>opt1),])
}
And I have defined the following metafiltering settings of specific class which would specify two functions which would have to be applied sequentially to matrix m
metafilterfuncs=list(fun1=f1(opt1=0.1,opt2=0.2),fun2=f2(opt1=0.5))
class("metafilterfuncs")="metafiltering"
The question I have then is how I could apply the filtering steps of an arbitrary metafiltering function object to given matrix m using the specified functions and settings?
You can do something like this :
You define a sort of functions pieplines where you give a priority for each function.
pipelines <- c(f1=100,f2=300,f3=200)
I define 3 dummy functions here for test:
f1 <- function(m,a) m + a
f2 <- function(m,b) m + b
f3 <- function(m,c) m + c
For each function , you store the argument in another list :
args <- list(f1=c(a=1),f2=c(b=2),f3=c(c=3))
Then you apply your functions :
m <- matrix(1:2,ncol=2)
for (func in names(pipelines[order(pipelines)]))
{
m <- do.call(func,list(m,args[[func]]))
}
pryr has a function, compose, like what you need, but it doesn't quite cut it. The compose function requires the functions to be given one by one, not in a list, and it cannot take arguments. It's also oddly placed in that package. A similar function can be found in plyr, namely each. But this function does not apply functions sequentially, but individually and outputs a named vector (list?).
agstudy provided a solution above, but it suffers from a problem: it can only take scalar arguments because it gives the arguments in a named vector. The solution to this is to use a named list instead. So, here's an improved function to replace the one in pryr.
compose2 = function(x, funcs, args, msg_intermediate = F) {
if (length(funcs) != length(args)) stop("length of functions and arguments must match")
for (i in seq_along(funcs)) {
x = do.call(what = funcs[[i]], args = c(x, args[[i]]))
if ((i != length(funcs)) && msg_intermediate) message(x)
}
x
}
msg_intermediate is a nice debugging argument that messages the intermediate results, so one can easier understand what happens.
Test it:
adder = function(x, n) x + n
compose2(0,
funcs = list(adder, adder, adder),
args = list(list(n = 1), list(n = 2), list(n = 3)),
msg_intermediate = T
)
Outputs:
1
3
[1] 6
This is what you get when you take 0, then add 1 (=1), then add 2 (=3), then add 3 (=6).
The args argument for compose2 takes a list of lists, so that one can supply non-scalar function arguments. Here's an example:
add_div = function(x, n, d) (x + n) / d
compose2(0,
funcs = list(add_div, add_div, add_div),
args = list(list(n = 1, d = 1), list(n = 2, d = 2), list(n = 3, d = 3)),
msg_intermediate = T
)
Output:
1
1.5
[1] 1.5
Which is what you get when you take 0, add 1, divide by 1 (=1), then take 1, add 2 then divide by 2 (=1.5), then take 1.5, add 3 and then divide by 3 (=1.5).

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