I have a function in R with a lot of default arguments, that I want to use multiple times. I want to be able to change those without having to write it all the time.
This is easy in Python like this:
def f(x, y, optional1=1, optional2=2, optional3=3):
return (x+y+optional1+optional2+optional3)
args = (10, 20)
print(f(1,2, *args)) #this prints 36 = 1+2+10+20+3
now I have an R function f = function(x,y, optional1=1, optional2=2, optional3=3) but I have not found a way of doing something similar to the example above.
I was hoping something could be done with the ellipsis (...) but seems like that's only for a variable amount of arguments in the function definition.
Doing something like
g = function(x,y){
return(f(x,y, 10, 20, 30)
}
would work but I was wondering if there was a cleaner and more readable way of doing this.
Thanks.
There are a few ways, but you can use do.call().
f <- function(x, y, optional1 = 1, optional2 = 2, optional3 = 3){
return(x + y + optional1 + optional2 + optional3)
}
f(1, 2, 10, 20)
# [1] 36
Using do.call() this will look like the following.
args <- list(optional1 = 10, optional2 = 20)
do.call(f, c(x = 1, y = 2, args))
# [1] 36
Another way using rlang saves a bit of typing, but is generally the same thing.
library(rlang)
exec(f, x = 1, y = 2, !!!args)
#[1] 36
If you are doing this a lot, you can take the approach you started and wrap a function.
g <- function(x, y, arg_list) {
do.call(f, c(x = x, y = y, arg_list))
}
g(1, 2, args)
# [1] 36
Related
I have been struggling to implement do.call(). I have a function that I have to run many times to generate a series of different plots. Instead of inputting each argument, I want each pair of arguments to be accessed from a table. This seems like it should be possible with do.call() but I can't seem to figure it out.
Here is a basic example I made to try to troubleshoot how to accomplish this.
fun <- function(x, y) {
z = x + y
assign(gsub(" ","_",paste("sum of", x, "and", y)), z, env=.GlobalEnv)
}
list_x = as.list(c(1, 2, 3))
list_y = as.list(c(4, 3, 2))
do.call(fun, c(list_x, list_y))
sum_of_1_and_4
sum_of_2_and_3
sum_of_3_and_2
However, I get the following error:
Error in (function (x, y) : unused arguments (3, 4, 3, 2)
I think mapply() might be a better fit in this situation:
fun <- function(x, y) {
z = x + y
assign(gsub(" ","_",paste("sum of", x, "and", y)), z, env=.GlobalEnv)
}
list_x = list(1, 2, 3)
list_y = list(4, 3, 2)
mapply(fun, list_x, list_y)
Yielding the following output:
sum_of_1_and_4
[1] 5
sum_of_2_and_3
[1] 5
sum_of_3_and_2
[1] 5
I have an R function which takes a large number of arguments (18) which I would like to pass in as a list. When I am running this function by hand, so to speak, I generally want to use all the defaults but one or two, but I also want to run this same function many times with various combinations of default and non-default items, which I would like to assemble programmatically as lists.
I know that I could just have my 18+ arguments as individual formals and then assemble them into a list inside the function, but I wish I could have a list as a default for a formal, and then have the elements have defaults as well. Like this:
> f <<- function(x, y = list(a=0, b=3)) {with(y, (x + a + b))}
> f(1)
[1] 4
> f(x=1, y$a = 1)
Error: unexpected '=' in "f(x=1, y$a ="
(or alternatively)
In y$a <- 1 :
Error in eval(substitute(expr), data, enclos = parent.frame()) :
object 'a' not found
except with the output of 5 rather than an error. I suspect there is no way to do this, because R does not recognise the assignments in the list as creating defaults, but only as creating named elements. But maybe with the assignment form of formals? or through some clever use of do.call?
Here are some alternatives:
1) modifyList Use modifyList to process the defaults.
f1 <- function(x, y = list()) {
defaults <- list(a = 0, b = 3)
with(modifyList(defaults, y), {
x + a + b
})
}
f1(x = 1)
## [1] 4
f1(x = 1, y = list(a = 1))
## [1] 5
2) do.call Another possibility is to have two functions. The first does not use a list and the second (which is the one the user calls) does using do.call to invoke the first.
f2impl <- function(x, a = 0, b = 3) x + a + b
f2 <- function(x, y = list()) do.call("f2impl", c(x, y))
f2(x = 1)
## [1] 4
f2(x = 1, y = list(a = 1))
## [1] 5
Assume I have a value x which is of some (unknown) type (especially: scalar, vector or list). I would like to get the R expression representing this value. If x == 1 then this function should simply return expression(1). For x == c(1,2)) this function should return expression(c(1,2)). The enquote function is quite near to that what I want, but not exactly.
By some playing around I found the following "solution" to my problem:
get_expr <- function(val) {
tmp_expr <- enquote(val)
tmp_expr[1] <- quote(expression())
return(eval(tmp_expr))
}
get_expr(1) # returns expression(1)
get_expr(c(1, 2)) # returns expression(c(1, 2))
get_expr(list(x = 1)) # returns expression(list(x = 1))
But I think my get_expr function is some kind of hack. Logically, the evaluation should not be necessary.
Is there some more elegant way to do this? As far as I see, substitute does not really work for me, because the parameter of my get_expr function may be the result of an evaluation (and substitute(eval(expr)) does not do the evaluation).
I found another way via parse(text = deparse(val)), but this is even more a bad hack...
as.expression(list(...)) seems to do it:
> get_expr <- function(val) as.expression(list(val))
> str(get_expr(1))
expression(1)
> str(get_expr(c(1, 2)))
expression(c(1, 2))
> str(get_expr(list(x=1)))
expression(list(x = 1))
> val <- list(x=1, y=2)
> str(get_expr(val))
expression(list(x = 1, y = 2))
You can use substitute(), and just need to call it a bit differently:
express <- function(e) substitute(expression(x), env = list(x=e))
v1 <- c(1, 2)
express(v1)
# expression(c(1, 2))
v2 <- list(a = 1, b = 2)
express(v2)
# expression(list(a = 1, b = 2))
I have a function and a list of arguments.
F <- function(a,b,...) {a^b+b/a}
L <- list("a" = 5, "b" = 2, "c" = 0)
I want to replace one of the arguments ("a", "b" or "c") with an unknown x (or "x") and plot with ggplot's stat_function.
These computations are part of a shiny app, where the user will 1) select a parameter from a drop-down list, say "a", to be the unknown, and 2) use sliders to select values of the other parameters. The numbers 5, 2, 0 in L are the default parameter values, to be used before user interaction. There are several such functions. Here the list of parameters L has an element not used in F.
I've been stuck on this for so long that I can't think straight anymore. Of the many things I've tried, here's one:
# select a parameter to vary:
Y <- "a"
f1 <- function(f = F, l = L, y = Y, x, ...){
l[y] <- x # replace "a" with x
do.call(f, l, ...)
}
# make a stat_function ggplot:
library("ggplot2")
df <- data.frame(x = c(0,10))
p <- ggplot(df, aes(x))
p <- p + stat_function(fun = f1)
print(p)
This returns the following error:
Error in (function (f = F, l = L, y = Y, x, ...) :
argument "x" is missing, with no default
Error in as.environment(where) : 'where' is missing
I have tried several variants, including: setting l[y] <- "x" and using aes_string instead of aes. I have also tried backquotes around x. I have read through the documentation about environments, so I've tried defining an environment, wrapping x and just about everything around eval or quote. I've even tried voodoo. I've lost count of how many hours I've spent on this. A suggestion to read the manual or a hint without an explanation will kill me. 8-) If my question is unclear, please let me know and I will clarify. Thanks!
If I understand, Having a multi parameters functions , you want to induce a partial function where you vary one parameter and fix others. Try this for example:
F <- function(a,b,...) {a^b+b/a}
L <- list("a" = 5, "b" = 2, "c" = 0)
f.partial <- function( var = "a",params=L){
params[[var]]=as.name("x")
function(x)do.call(F,params)
}
We can test this for example:
## vary a
f.partial("a")(1)
[1] 3
> F(1,b=L$b)
[1] 3
## vary b
> f.partial("b")(1)
[1] 5.2
> F(1,a=L$a)
[1] 5.2
Testing with ggplot2:
library("ggplot2")
df <- data.frame(x = seq(0,1,0.1))
ggplot(df, aes(x)) +
stat_function(fun = f.partial("a"),col='blue') +
stat_function(fun = f.partial("b"),col='red')
I wrote a function that operates on a single vector. Sometimes I want to use that function on whole data.frame. I can accomplish that by using sapply across the relevant variables, but now I want contain and direct the function using S3 methods.
First, the setup:
df_test <- data.frame(x = c(1, 2, 3, 4, 5),
y = c(6, 7, 8, 9, 10),
z = c(11, 12, 13, 14, 15))
adder <- function(x, from, to) {
if(is.null(ncol(x))) {
x <- structure(list(x = x, from = from, to = to), class = "single")
} else {
x <- structure(list(x = x, from = from, to = to), class = "multiple")
}
UseMethod("adder", x)
}
adder.single <- function(x, from, to) {
x <- x + (from - to) # just some random operation here - the real function does more
x
}
adder.multiple <- function(x, from, to) {
x <- sapply(x, function(y) {
y <- structure(list(x = y, from = from, to = to), class = "single");
UseMethod("adder", y) })
x
}
So on a single vector, the function works:
> adder(df_test[,1], 2, 4)
[1] -1 0 1 2 3
But passing the entire data.frame does not:
> adder(df_test, 2, 4)
Error in from - to : 'from' is missing
It's clear to me what the problem is - adder detects that we're looking at a whole data.frame, uses the "multiple" method, which then calls the "single" method and arguments from and to are not being passed along.
I read Hadley Wickham's OOP Field Guide and Advanced Programming by Nicolas Christian, but it ain't clickin' for me. I welcome a completely different approach, as long as it uses S3 methods because part of this exercise is for me to learn how to use them.
Simply call adder and S3 dispatch will do the right and call the right function.
adder.multiple <- function(x, from, to) {
sapply(x, adder,from,to)
}
You should not define the generic adder twice ( the call to UseMethod).