How do I access function names in R? - r

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"

Related

Specify arguments when applying function with sapply

I created the following function which finds the columns correlation to the target. The function is applied on the diamonds dataset (assigned to dt here) for this purpose.
select_variables_gen <- function(variable, target = dt$price, threshold = 0.9){
if(all(class(variable) %in% c("numeric","integer"))){
corr <- abs(cor(variable, target));
if(corr > threshold){
return(T);
}else{F}
}else{F}
};
Now that I want to apply the function I can't figure out how to specify the arguments of the function. This is what I tried
alt_selected_gen <- names(dt)[sapply(dt,
select_variables(variable = dt, target = dt$carat, threshold = 0.1))]
alt_selected_gen;
Which returns an error saying thaht the 2nd and 3rd argument are unused. How can I use the function (with sapply or any other way) to be able to specify the arguments?
My desired output is the column names of the columns which have a correlation above the threshold. So using the default values with the above code that would be;
[1] "carat" "price"
You pass your function to sapply. What you are trying to pass is a call to your function.
When you use sapply on a data frame, the columns get sent one by one to your function as its first argument. If you want to pass further named arguments to your function you just add them directly as parameters to sapply after the function itself. This works because of the dots operator (...) in sapply's formal arguments, which pass any extra parameters into the call to your function.
It should therefore just be
names(dt)[sapply(dt, select_variables_gen, target = dt$carat, threshold = 0.1)]
#> [1] "carat" "table" "price" "x" "y" "z"
Notice also that the function is called select_variables_gen in your example, not select_variables.

Modify elipsis in R

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.

Evaluating a function that is an argument in another function using quo() in R

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.

Function that storages variables from a list automatically, in another function

For example I'd like to storage my variables only in f() function with the function setObj(argument), whose argument is a list with a,b,c,d fields, for example.
f<-function(x){
setObj(argument=x)
(a+b+c+d)/4
}
So I can call my variables with their names, without subsetting from the list.
Thank you
I believe you are looking for with:
f<-function(x){
with(x,
(a+b+c+d)/4
)
}
f(list(a=1,b=2,c=3,d=4))
[1] 2.5

passing several arguments to FUN of lapply (and others *apply)

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

Resources