Using multiple ellipses arguments in R
You could so something similar to your second choice if you use do.call
, which allows you to pass the arguments to a function as a list. E.g. pass axesarg
as a list and then in your function have: do.call(axes,axesarg)
etc
For example:
outer_fxn <- function(inner_args=list(), ...) {
do.call(inner_fxn, inner_args)
}
inner_fxn <- function(...) {
# do stuff
}
# function call
outer_fxn(inner_args=list(a=1, b=2), outer_arg1=3, etc)
In the above, any arguments that should be handled by the inner_fxn
...
should be passed in with in the inner_args
list. The outer_fxn
...
arguments are handled as usual.
Using multiple three dot ellipsis in R
OPTION 1
Function
test = function(x = rnorm(20), y = rnorm(20), plot_options = NA, ...){
if (is.na(plot_options) == FALSE){
eval(parse(text = paste0("plot(x, y, ", plot_options, ")")))
} else {
plot(x, y, type = "n")
}
lines(x, y, ...)
}
USAGE
test()
set.seed(42)
m = rnorm(20)
n = rnorm(20)
test(x = m, y = n,
plot_options = "type = 'p', col = 'red', pch = 19, xlab = 'Test Plot', ylab = 'Y-axis'")
OPTION 2 (@Gregor's Solution)
Function
test2 = function(x = rnorm(20), y = rnorm(20), ..., line_options){
plot(x, y, ...)
if (missing(line_options)) {
lines(x, y)
} else {
do.call(lines, c(list(x = x, y = y), line_options))
}
}
USAGE
par(mfrow = c(2, 2), mar = c(2, 2, 1, 1))
test2(main = 'default')
test2(line_options = list(lty = 2), main = 'line')
test2(col = 'red', main = 'plot')
test2(col = 'red', line_options = list(lty = 2, col = 'blue'), main = 'line and plot')
Passing along ellipsis arguments to two different functions?
You can pass a list to one of the subfunctions, ie
mainF <- function(f1, f2, ..., args.f2 = list()) {
do.call(f2, c(X=f1(...), args.f2))
}
mainF(function(x) x, function(X, y) X*y, x=5, args.f2 = list(y=3))
(untested, but you got the gist)
R functions: passing arguments with ellipsis
So the ellipses are used to save you specifying all the arguments of f2
in the arguments of f1
. Though when you declare f2
, you still have to treat it like a normal function, so specify the arguments b
and c
.
f1 <- function(a, ...) {
print(a)
f2(...)
}
# Treat f2 as a stand-alone function
f2 <- function(b, c) {
print(b == TRUE)
print(runif(c))
}
f1(2, b=FALSE, c=2)
[1] 2
[1] FALSE
[1] 0.351295 0.9384728
R: Understanding how ellipsis (...) works in nested functions, & how iit doesn't
Ellipsis has to be explicitly provided to pass to a nested function, so for example, in your f02, the list
call gets whatever was passed to f02 as its own arguments. In contrast, in f01, the arguments are simply ignored. You could pass the arguments to f02 within f01 thus:
f01 <- function(...){
f02(b = 2,...)
}
Result:
f01(a=1)
$b
[1] 2
$a
[1] 1
This works regardless of the number of arguments in the ellipsis:
f01(a=1,c=3)
$b
[1] 2
$a
[1] 1
$c
[1] 3
How to use R's ellipsis feature when writing your own function?
I read answers and comments and I see that few things weren't mentioned:
data.frame
useslist(...)
version. Fragment of the code:object <- as.list(substitute(list(...)))[-1L]
mrn <- is.null(row.names)
x <- list(...)object
is used to do some magic with column names, butx
is used to create finaldata.frame
.
For use of unevaluated...
argument look atwrite.csv
code wherematch.call
is used.As you write in comment result in Dirk answer is not a list of lists. Is a list of length 4, which elements are
language
type. First object is asymbol
-list
, second is expression1:10
and so on. That explain why[-1L]
is needed: it removes expectedsymbol
from provided arguments in...
(cause it is always a list).
As Dirk statessubstitute
returns "parse tree the unevaluated expression".
When you callmy_ellipsis_function(a=1:10,b=11:20,c=21:30)
then...
"creates" a list of arguments:list(a=1:10,b=11:20,c=21:30)
andsubstitute
make it a list of four elements:List of 4
$ : symbol list
$ a: language 1:10
$ b: language 11:20
$ c: language 21:30First element doesn't have a name and this is
[[1]]
in Dirk answer. I achieve this results using:my_ellipsis_function <- function(...) {
input_list <- as.list(substitute(list(...)))
str(input_list)
NULL
}
my_ellipsis_function(a=1:10,b=11:20,c=21:30)As above we can use
str
to check what objects are in a function.my_ellipsis_function <- function(...) {
input_list <- list(...)
output_list <- lapply(X=input_list, function(x) {str(x);summary(x)})
return(output_list)
}
my_ellipsis_function(a=1:10,b=11:20,c=21:30)
int [1:10] 1 2 3 4 5 6 7 8 9 10
int [1:10] 11 12 13 14 15 16 17 18 19 20
int [1:10] 21 22 23 24 25 26 27 28 29 30
$a
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.00 3.25 5.50 5.50 7.75 10.00
$b
Min. 1st Qu. Median Mean 3rd Qu. Max.
11.0 13.2 15.5 15.5 17.8 20.0
$c
Min. 1st Qu. Median Mean 3rd Qu. Max.
21.0 23.2 25.5 25.5 27.8 30.0It's ok. Lets see
substitute
version:my_ellipsis_function <- function(...) {
input_list <- as.list(substitute(list(...)))
output_list <- lapply(X=input_list, function(x) {str(x);summary(x)})
return(output_list)
}
my_ellipsis_function(a=1:10,b=11:20,c=21:30)
symbol list
language 1:10
language 11:20
language 21:30
[[1]]
Length Class Mode
1 name name
$a
Length Class Mode
3 call call
$b
Length Class Mode
3 call call
$c
Length Class Mode
3 call callIsn't what we needed. You will need additional tricks to deal with these kind of objects (as in
write.csv
).
If you want use ...
then you should use it as in Shane answer, by list(...)
.
R: using ellipsis argument (...)
AN ACTUAL ANSWER:
You can do this through a bit of trickery. First, define your function as before, but include a list with your default arguments inside the function. Then you can parse whatever arguments come in through ...
as a list, replace the defaults with anything in ...
and then pass the updated list of arguments through do.call
.
myplot <- function(x, ...) {
args1 <- list(cex=4, main="Default Title") # specify defaults here
inargs <- list(...)
args1[names(inargs)] <- inargs
do.call(plot, c(list(x=x), args1))
}
myplot(x=1:3) # call with default arguments
myplot(x=1:3, cex=2, main="Replacement", xlab="Test xlab") # call with optional arguments
EARLIER COMMENTARY:
The problem here can be seen through a few example functions:
myplot1 <- function(x, ... ) {
plot(x, cex= 1.5, ... )
}
myplot2 <- function(x, cex=3, ... ) {
plot(x, cex=cex, ... )
}
myplot3 <- function(x, ... ) {
plot(x, ... )
}
myplot1(1:3, cex=3) # spits your error
myplot2(1:3, cex=3) # works fine
myplot3(1:3, cex=3) # works fine
In myplot2
, you specify a default value of cex
but can change it. In myplot3
, cex
is simply passed through. If you run myplot2
with two cex
arguments, you'll see what's happening with your function (myplot1
):
myplot2(1:3, cex=3, cex=1.5) # same error as above
So, you're probably best to avoid setting any defaults in plot()
, so then you can pass anything through the ...
in myplot
.
Ignore argument missing error in ellipsis function
R is an odd beast because even though the syntax clearly allows trailing commas, it doesn’t just discard/ignore them, as other languages do. Instead, R pretends that a “missing” argument has been passed. As long as nothing touches the missing argument, that’s a-ok. We can even use this situation without dots:
f = function (a, b, c) a + b
Since c
is never read, we don’t need to pass it:
f(1, 2) # works
f(1, 2, ) # works, too
Not very useful, perhaps. But the following also works:
g = function (a, b, c) a + b + if (missing(c)) 0 else c
g(1, 2) # 3
g(1, 2, ) # 3
g(1, 2, 3) # 6
… unfortunately this doesn’t help us directly to capture ...
.
Short of using a read-made solution (e.g. rlang::list2
), the only real way to capture dots (allowing trailing commas) is to work on unevaluated arguments, and to manually evaluate them (we might instead be tempted to try missing(...elt(...length()))
; alas R doesn’t accept that).
There are different ways of getting at the unevaluated dot arguments. The simplest way is to use match.call(expand.dots = FALSE)$...
. Which leaves us with:
new_game = function (...) {
args = match.call(expand.dots = FALSE)$...
nargs = length(args)
if (identical(args[[nargs]], quote(expr = ))) nargs = nargs - 1L
lapply(args[seq_len(nargs)], eval, envir = parent.frame())
}
What does passing an ellipsis (...) as an argument mean in R?
The typical use of ...
argument is when a function say f
internally calls a function g
and uses ...
to pass arguments to g
without explicitly listing all those arguments as its own formal arguments. One may want to do this, for example, when g
has a lot of optional arguments that may or may not be needed by the user in the function f. Then instead of adding all those optional arguments to f
and increasing complexity, one may simply use ...
.
What it means, as you asked, is the function f
will simply ignore these and pass them on to g
. The interesting thing is that ...
may even have arguments that g
does not want and it will ignore them too, for say h
if it needed to use ...
too. But also see this so post for a detailed discussion.
For example consider:
f <- function (x, y, ...) {
# do something with x
# do something with y
g(...) # g will use what it needs
h(...) # h will use that it needs
# do more stuff and exit
}
Also, see here in the intro-R manual for an example using par
.
Also, this post shows how to unpack the ...
if one was writing a function that made use of it.
Related Topics
How to Upload a File to a Server via Ftp Using R
Highlighting Individual Axis Labels in Bold Using Ggplot2
Add One Column Below Another in a Data.Frame in R
How to Add an External Legend to Ggpairs()
R: Why Does Read.Table Stop Reading a File
Add Textbox to Facet Wrapped Layout in Ggplot2
R Dplyr Join by Range or Virtual Column
Dodging Points and Error Bars with Ggplot
R Shiny Error: Cannot Coerce Type 'Closure' to Vector of Type 'Double'
Convert Column in Data.Frame to Date
Coding Variable Values into Classes Using R
Plot Mean and Sd of Dataset Per X Value Using Ggplot2
Shiny: How to Adjust the Width of the Tabsetpanel
Dynamically Add Function to R6 Class Instance