What Does Passing an Ellipsis (...) as an Argument Mean in R

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.

R ellipsis - further arguments passed to or from other methods

The ellipsis argument allows one to pass arguments to a downstream function. We'll illustrate with a simple R function as follows.

testfunc <- function(aFunction,x,...) {
aFunction(x,...)
}
aVector <- c(1,3,5,NA,7,9,11,32)

# returns NA because aVector contains NA values
testfunc(mean,aVector)

# use ellipsis in testfunc to pass na.rm=TRUE to mean()
testfunc(mean,aVector,na.rm=TRUE)

...and the output:

> testfunc <- function(aFunction,x,...) {
+ aFunction(x,...)
+ }
> aVector <- c(1,3,5,NA,7,9,11,32)
>
> # returns NA because aVector contains NA values
> testfunc(mean,aVector)
[1] NA
> # use ellipsis in testfunc to pass na.rm=TRUE to mean()
> testfunc(mean,aVector,na.rm=TRUE)
[1] 9.714286

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

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

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:

  1. data.frame uses list(...) 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, but x is used to create final data.frame.

    For use of unevaluated ... argument look at write.csv code where match.call is used.

  2. 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 a symbol - list, second is expression 1:10 and so on. That explain why [-1L] is needed: it removes expected symbol from provided arguments in ... (cause it is always a list).

    As Dirk states substitute returns "parse tree the unevaluated expression".

    When you call my_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) and substitute make it a list of four elements:

    List of 4
    $ : symbol list
    $ a: language 1:10
    $ b: language 11:20
    $ c: language 21:30

    First 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)
  3. 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.0

    It'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 call

    Isn'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: why providing a list to ellipsis (...) when ellipsis is the last argument does not work?

The way arguments work in R is that when you don't name your first argument, R just assumes that goes into the first argument for the function. This is true for the second, third and so forth arguments in the argument list EXCEPT for arguments that come after the ... - because R doesn't know how many arguments you intend to fit into the ..., if you want to change the default of whatever comes after it, you have to name it.

So in your case, when you call function f(), the object list(a=2) goes into the .... But in g(), that same object goes into a. The only way you can get something into ... when that is placed at the end of the argument list without including arguements for a and b is to name it something that isn't a or b, e.g. g(c=list(a=1)).

Passing elipsis argument to be evaluated within function call within Reduce

I think just need a little change:

compare_multiple_vectors <- function(x, ...) {
# Check if all elements of x are atomic vectors
Vectorize(FUN = checkmate::assert_atomic_vector,
vectorize.args = "x")(x)

# Compare list elements
Reduce(
f = function(a, b) { # <===================== Remove *...*

if (isTRUE(all.equal(target = a, current = b, ...))) {
a
} else {
FALSE
}
},
x = x
) -> res_red

# Return results
if (isFALSE(res_red)) {
return(FALSE)
} else {
return(TRUE)
}
}

The arguments f of Reduce seems to have a signature like function(x, y). So Reduce will ignore ... in f. If remove the ellipsis of f, ... will reference from the outer space, and will have the right result you want.

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.

passing ellipsis arguments to map function purrr package, R

You just need to pass the ellipses through the map_df() call as well. Otherwise they can't get into the inner f1() call.

f2 <- function(df, ...){
res <- map_df(colnames(df), ~f1(df[,.], a=., ...), ...)
return(res)
}


Related Topics



Leave a reply



Submit