I have a question regarding passing multiple arguments to a function, when using lapply in R.
When I use lapply with the syntax of lapply(input, myfun); - this is easily understandable, and I can define myfun like that:
myfun <- function(x) {
# doing something here with x
}
lapply(input, myfun);
and elements of input are passed as x argument to myfun.
But what if I need to pass some more arguments to myfunc? For example, it is defined like that:
myfun <- function(x, arg1) {
# doing something here with x and arg1
}
How can I use this function with passing both input elements (as x argument) and some other argument?
If you look up the help page, one of the arguments to lapply is the mysterious .... When we look at the Arguments section of the help page, we find the following line:
...: optional arguments to ‘FUN’.
So all you have to do is include your other argument in the lapply call as an argument, like so:
lapply(input, myfun, arg1=6)
and lapply, recognizing that arg1 is not an argument it knows what to do with, will automatically pass it on to myfun. All the other apply functions can do the same thing.
An addendum: You can use ... when you're writing your own functions, too. For example, say you write a function that calls plot at some point, and you want to be able to change the plot parameters from your function call. You could include each parameter as an argument in your function, but that's annoying. Instead you can use ... (as an argument to both your function and the call to plot within it), and have any argument that your function doesn't recognize be automatically passed on to plot.
As suggested by Alan, function 'mapply' applies a function to multiple Multiple Lists or Vector Arguments:
mapply(myfun, arg1, arg2)
See man page:
https://stat.ethz.ch/R-manual/R-devel/library/base/html/mapply.html
You can do it in the following way:
myfxn <- function(var1,var2,var3){
var1*var2*var3
}
lapply(1:3,myfxn,var2=2,var3=100)
and you will get the answer:
[[1]]
[1] 200
[[2]]
[1] 400
[[3]]
[1] 600
myfun <- function(x, arg1) {
# doing something here with x and arg1
}
x is a vector or a list and myfun in lapply(x, myfun) is called for each element of x separately.
Option 1
If you'd like to use whole arg1 in each myfun call (myfun(x[1], arg1), myfun(x[2], arg1) etc.), use lapply(x, myfun, arg1) (as stated above).
Option 2
If you'd however like to call myfun to each element of arg1 separately alongside elements of x (myfun(x[1], arg1[1]), myfun(x[2], arg1[2]) etc.), it's not possible to use lapply. Instead, use mapply(myfun, x, arg1) (as stated above) or apply:
apply(cbind(x,arg1), 1, myfun)
or
apply(rbind(x,arg1), 2, myfun).
Related
I have a do.call use-case (as result of calling mclapply) simplified to the following:
myfunc <- function(x, y) {
str(x)
#return(x + y)
}
res <- list(list(x=1,y=1),list(x=2,y=2),list(x=3,y=3))
str(res)
do.call(what="myfunc", res)
No matter what I do in myfunc it never works. I'd expect do.call to call myfunc three times and pass the resp. x and y but instead it calls it once with all the data and complains it doesn't know what to do with the remaining arguments ... what's the correct way to implement the what function such that will apply something over each list element as it does with any other base function?
UPDATE What lead to my confusion over the use of do.call is that I always saw its use to "reduce" the results of a collection. What I didn't realize is that the functions being called by do.call do handle list types and reduce scenarios e.g. rbind
lapply handles the looping, do.call applies list elements as arguments. Try this:
lapply(res, function(l) do.call('myfunc', l))
In tidyverse, walk + reduce can use used
library(purrr)
walk(res, reduce, myfunc)
#num 1
#num 2
#num 3
I have a problem with elipsis usecase. My function accepts list of objects, let's call them objects of class "X". Now, objects X are being processed inside of my function to class "Xs", so I have list of "Xs" objects. Function that I import from other package can compute multiple "Xs" objects at once but they have to be enumerated (elipsis mechanic), not passed as list. Is there a way how to solve it? I want something like this
examplefun <- function(charlist){
nums <- lapply(charlist, as.numeric)
sum(... = nums)
}
Of course example above throws an error but it shows what i want to achieve. I tried to unlist with recursive = FALSE ("X" and "Xs" are the list itself) but it does not work.
If there is no solution then:
Let's assume I decideed to accept ... insted of list of "X" objects. Can I modify elipsis elements (change them to "Xs") and then pass to function that accepts elipsis? So it will look like this:
examplefun2 <- function(...){
function that modify object in ... to "Xs" objects
sum(...)
}
In your first function, just call sum directly because sum works correctly on vectors of numbers instead of individual numbers.
examplefun <- function (charlist) {
nums <- vapply(charlist, as.numeric, numeric(1L))
sum(nums)
}
(Note the use of vapply instead of lapply: sum expects an atomic vector, we can’t pass a list.)
In your second function, you can capture ... and work with the captured variable:
examplefun2 <- function (...) {
nums <- as.numeric(c(...))
sums(nums)
}
For more complex arguments, Roland’s comment is a good alternative: Modify the function arguments as a list, and pass it to do.call.
I have made a function that takes as an argument another function, the argument function takes as its argument some object (in the example a vector) which is supplied by the original function. It has been challenging to make the function call in the right way. Below are three approaches I have used after having read Programming with dplyr.
Only Option three works,
I would like to know if this is in fact the best way to evaluate a function within a function.
library(dplyr);library(rlang)
#Function that will be passed as an argument
EvaluateThis1 <- quo(mean(vector))
EvaluateThis2 <- ~mean(vector)
EvaluateThis3 <- quo(mean)
#First function that will recieve a function as an argument
MyFunc <- function(vector, TheFunction){
print(TheFunction)
eval_tidy(TheFunction)
}
#Second function that will recieve a function as an argument
MyFunc2 <- function(vector, TheFunction){
print(TheFunction)
quo(UQ(TheFunction)(vector)) %>%
eval_tidy
}
#Option 1
#This is evaluating vector in the global environment where
#EvaluateThis1 was captured
MyFunc(1:4, EvaluateThis1)
#Option 2
#I don't know what is going on here
MyFunc(1:4, EvaluateThis2)
MyFunc2(1:4, EvaluateThis2)
#Option 3
#I think this Unquotes the function splices in the argument then
#requotes before evaluating.
MyFunc2(1:4, EvaluateThis3)
My question is:
Is option 3 the best/most simple way to perform this evaluation
An explanation of what is happening
Edit
After reading #Rui Barradas very clear and concise answer I realised that I am actually trying to do someting similar to below which I didn't manage to make work using Rui's method but solved using environment setting
OtherStuff <-c(10, NA)
EvaluateThis4 <-quo(mean(c(vector,OtherStuff), na.rm = TRUE))
MyFunc3 <- function(vector, TheFunction){
#uses the captire environment which doesn't contain the object vector
print(get_env(TheFunction))
#Reset the enivronment of TheFunction to the current environment where vector exists
TheFunction<- set_env(TheFunction, get_env())
print(get_env(TheFunction))
print(TheFunction)
TheFunction %>%
eval_tidy
}
MyFunc3(1:4, EvaluateThis4)
The function is evaluated within the current environment not the capture environment. Because there is no object "OtherStuff" within that environment, the parent environments are searched finding "OtherStuff" in the Global environment.
I will try to answer to question 1.
I believe that the best and simpler way to perform this kind of evaluation is to do without any sort of fancy evaluation techniques. To call the function directly usually works. Using your example, try the following.
EvaluateThis4 <- mean # simple
MyFunc4 <- function(vector, TheFunction){
print(TheFunction)
TheFunction(vector) # just call it with the appropriate argument(s)
}
MyFunc4(1:4, EvaluateThis4)
function (x, ...)
UseMethod("mean")
<bytecode: 0x000000000489efb0>
<environment: namespace:base>
[1] 2.5
There are examples of this in base R. For instance approxfun and ecdf both return functions that you can use directly in your code to perform subsequent calculations. That's why I've defined EvaluateThis4 like that.
As for functions that use functions as arguments, there are the optimization ones, and, of course, *apply, byand ave.
As for question 2, I must admit to my complete ignorance.
I would like to apply a function to a vector. My function takes multiple arguments, but I can keep the arguments the same throughout.
Here's what I've come up with, but it doesn't work. I've seen some things using mapply -- I'm not sure if that's what I need though.
add = function(x, y) x+y
sapply(1:5, add(y = 10))
In general you can give additional arguments of a function to sapply like this.
sapply(1:5, add, y=10)
Just write the function name and after that you can give any number of arguments directly within sapply itself
I am writing a function that receives two parameters: a data frame, and a function, and, after processing the data frame, summarizes it using the function parameter (e.g. mean, sd,...). My question is, how can I get the name of the function received as a parameter?
How about:
f <- function(x) deparse(substitute(x))
f(mean)
# [1] "mean"
f(sd)
# [1] "sd"
do.call may be what you want here. You can get a function name as character value, and then pass that and a list of arguments to do.call for evaluation. For example:
X<-"mean"
do.call(X,args=list(c(1:5)) )
[1] 3
Perhaps I'm misunderstanding the question, but it seems like you could simply have the function name as a parameter, and evaluate the function like normal within your function. This approach works fine for me. The ellipsis is for added parameters to your function of interest.
myFunc=function(data,func,...){return(func(data,...))}
myFunc(runif(100), sd)
And if you'd want to apply it to every column or row of a data.frame, you could simply use an apply statement in myFunc.
Here's my try, perhaps, you want to return both the result and the function name:
y <- 1:10
myFunction <- function(x, param) {
return(paste(param(x), substitute(param)))
}
myFunction(y, mean)
# [1] "5.5 mean"