I have run into a situation where I need to take all the extra arguments passed to an R function and roll them into an object for later use. I thought the previous question about ellipses in functions would help me, but I still can't quite grasp how to do this. Here is a very simple example of what I would like to do:
newmean <- function(X, ...){
args <- as.list(substitute(list(...)))[-1L]
return(mean(X, args))
}
I've tried a number of different formulations of args in the above example and tried unlisting args in the return call. But I can't make this work. Any tips?
I realize that I could do this:
newmean <- function(X, ...){
return(mean(X, ...))
}
But I need to have the ... arguments in an object which I can serialize and read back into another machine.
How about
newmean <- function(X, ...){
args <- as.list(substitute(list(...)))[-1L]
z<-list(X)
z<-c(z,args)
do.call(mean,z)
}
Related
I am trying to write a function myfun in which one of the variables fun, say, is itself a function. A simple, but ludicrous, example is:
myfun <- function(fun){
return(fun(1))
}
In line with good practice, I want to insert into myfun a test of fun to ensure that fun is indeed a function.
But I can't find code for any such test. What should I do?
You can use is.function to check if some object is a function, for example:
is.function(mean) or is.function(`[[`) would return TRUE, also on a side note there is another function is.primitive which tests for builtins and specials, but in your case you probably would want is.function
In your case :
myfun <- function(fun){
if(is.function(fun)){
return(fun(1))
} else {
warning("You are not passing fun as function")
}
}
Testing using:
fun <- function(x)x+2
would yield 3, but fun <- 1 it would give you warning for calls on myfun(fun)
I am trying to take an argument from a simple function "adder" and then use a loop to look at the effect of incrementing that argument.
I know there must be better approaches, such as building a single function that makes a longer data frame or maybe a nested loop without the second function... so I welcome those!
But what I'm more specifically interested is how to quote(?) and then parse(?) the argument, here called either "a" or "b" (but the function would declare them "arg_to_change") inside the new function, here called "change_of_adder_arguments".
adder <- function(a=1,b=2){
data.frame(t=1:100) %>% mutate(x=a*t, y=b*2)
}
change_of_adder_arguments <- function(arg_to_change) {
output <- list()
arg_to_change_enquo <- enquo(arg_to_change)
for (i in 1:5) {
output[[i]] <- ggplot(adder(!!arg_to_change_enquo := i), aes(x, y)) + geom_point()
}
return(output)
}
change_of_adder_arguments(a)
change_of_adder_arguments(b)
Error: Problem with mutate() input x.
x could not find function ":="
i Input x is a * t.
The nail in the coffin seems to be using the arg_to_change_enquo on the LHS of the assignment operator. I know there are many articles here about non-standard evaluation, but I have tried quote, enquo, bquote, parse/eval, sym, substitute, !!, {{}}, =, :=, assign and combinations of all these with no luck. My instinct is that the answer is in specifying which environment? If anybody knows of any good references that "ELI5" about enviroments, I would greatly appreciate it. Thanks!
You can use do.call and pass the arguments to change as a list.
library(ggplot2)
change_of_adder_arguments <- function(arg_to_change) {
output <- vector('list', 5)
arg_to_change_string <- deparse(substitute(arg_to_change))
for (i in 1:5) {
output[[i]] <- ggplot(do.call(adder, setNames(as.list(i),
arg_to_change_string)), aes(x, y)) + geom_point()
}
return(output)
}
plot <- change_of_adder_arguments(b)
I'm having troubles understanding how the ...parameter works in R, I can't find a complete documentation, is it some kind of editable object?
I'm writing an R package and I would like to write a function that uses the dots, to pass them to another function (plot), but I want to pass some parameters to the inner function only if they are not in .... I know that using list(...) I can check if they have been specified, but I don't know if I can add them to ... otherwise. What's the best way to make this work?
I tried to pass an edited pars= list(...) to the inner function plot(...= pars), but it didn't work. Actually in the end I found a solution, which is updating the list pars trough modifyList and then using do.call to pass them to the inner function, but this feels a bit intricate to me, there is any simpler solution?
You can just pass ... along as so:
# weird way to construct a linear function
f0 <- function (x, b=1) x+b
f1 <- function (x, a=2, ...){
# INITIAL WRONG ANSWER stopifnot(exists("b")) # see comments
if("b" %in% names(list(...))){
f0(a*x, ...)
}else{
f0(a*x, b=4, ...) # Only makes sense if b=4 is a default that has meaning
# in f1 but not outside of f1 ; or if you cannot change
# the definition of f0 (imported functions). Otherwise,
# you'd better change the default in f0.
}
}
f1(10)
f1(10, b=3)
Arthur and Janhoo made some good suggestions, but in the end I see that the straightforward way I hoped to find doesn't exist. So in the end the best solution to me is the one I sketched in the question:
pars_user= list(...)
pars_default= list(a= 1, b= 2)
pars_fixed= list(c= 3, d= 4)
pars= modifyList(pars_default, pars_user)
pars= modifyList(pars, pars_fixed)
do.call(function, pars)
One fear I have when using mapply in R is that I may mess up the order of arguments & hence unconsciously generate garbage results.
mydata<-data.frame(Temperature=foobar,Pressure=foobar2)
myfunction<-function(P,T)
{
....
}
mapply(FUN = myfunction,mydata$Temperature,mydata$Pressure)
Is there a way to utilize named arguments to avoid this sort of error via mapply?
If we need to match the function arguments, name the arguments for Map/mapply with the arguments of the function
mapply(FUN = myfunction,T=mydata$Temperature,P=mydata$Pressure)
We can apply the function directly instead of mapply though (based on the example provided below in my post)
do.call(myfunction, unname(mydata[2:1]))
data
mydata <- data.frame(Temperature = 1:5, Pressure = 16:20)
myfunction <- function(P, T) {P*5 + T*10}
I'm trying to write a wrapper for a function in order to use lists as input. I cannot change the function itself, therefore I need a workaround outside of it. I use as.call() and it works without optional arguments, but I fail to make it work when I have vectors as optional arguments.
Example:
# function I cannot change
func <- function(..., opt=c(1,2)) {
cl <- match.call(expand.dots = FALSE)
names <- lapply(cl[[2]],as.character)
ev <- parent.frame()
classes <- unlist(lapply(names,function(name){class(get(name,envir=ev))}))
print(c(opt,names, classes))
}
a <- structure(1:3, class="My_Class")
b <- structure(letters[1:3], class="My_Class")
lst <- list(a, b)
names(lst) <- c("a","b")
# Normal result
func(a,b,opt=c(3,4))
# This should give the same but it doesn't
call <- as.call(append(list(func), list(names(lst), opt=c(3,4))))
g <- eval(call, lst)
Instead of a list as optional argument, I also tried c(), but this doesn't work either. Does anybody have a suggestion or a help page? ?call wasn't to clear about my problem.
(I already asked a previous question to the topic here: R: How to use list elements like arguments in ellipsis? , but left out the detail about the optional parameter and cannot figure it out now.)
This produces the same result for me under both versions
call <- as.call(c(list(quote(func)), lapply(names(lst), as.name), list(opt=c(3,4))))
g <- eval(call, lst)
EDIT: as per Hadley's suggestions in comments.