How to convert from ellipsis to list and back to a raw call in R without a deparse

StackOverflow https://stackoverflow.com/questions/20236527

  •  05-08-2022
  •  | 
  •  

Вопрос

There are number of good (1 2 3) StackOverflow questions on the use of an ellipsis ("dots") in R. However, I haven't met one that addresses my particular use case yet. I am struggling to write a compatibility wrapper for a function that changes the name of an argument from the original function call to the underlying function call. For example, one might want to call newMean(c(1:10,1000,NA),.2,rmNA=TRUE) rather than mean(c(1:10,1000,NA),.2,na.rm=TRUE). We might even want to write our wrapper in such a way that we don't kill the S3 calls for mean by assuming all calls to newMean are meant to be dispatched for mean.default. The apparent path forward is something like (in pseudocode):

newMean <- function(x,...) {
  dots <- list(...)
  names(dots)[names(dots)=="rmNA"] <- "na.rm"
  x.list <- list(x=x)
  dots <- c(x.list,dots)
  do.call(base::mean,dots)
}

This leaves us with a value in dots like:

dots <- structure(list(x = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1000, NA), 
    0.2, na.rm = TRUE), .Names = c("x", "", "na.rm"))

So far so good. But something isn't quite the way I might otherwise expect it. Consider the command newMean(c(1:10,1000,NA),.2,TRUE) versus base::mean(c(1:10,1000,NA),.2,TRUE). When we call base::mean R then calls mean.default because getS3method("base::mean",class(x)) for the x in the call is numeric and has no match. match.call(mean.default) called while debugging newMean shows that the (unnamed) second and third argument are, of course, assigned in order to the three arguments before the ellipsis in mean.default. In contrast, do.call seems to be ignoring the unnamed arguments (maybe treating them as being part of the ellipsis in the call of mean.default?).

I've tried identifying the S3 method to be used using getS3method then using formals to dig into and identify the number of formal arguments to the particular S3 method being dispatched, but I haven't been able to generate a way to make a call to mean::base that flexibly respects the number of formals present in the method to be called and matches them appropriately.

Without deparsing a matched call from newMean to base:mean (e.g. deparse(match.call(base::mean))) and manually subbing argument names, is there a way to fix the behavior of newMean such that trim and na.rm are appropriately matched? The reason I don't want to string manipulate the substitution of the argument names is I that I can imagine cases (outside of this toy example) where values in the call might be partially matched to argument names.

Это было полезно?

Решение

One approach is to modify the call to newMean() and change it to a call to base::mean():

newMean <- function(...) {
  call <- match.call()
  call[[1]] <- quote(base::mean)

  rmNA <- names(call) == "rmNA"
  names(call)[rmNA] <- "na.rm"

  eval(call, parent.frame())
}
newMean(c(1:10, NA), rmNA = T)

But this is uniformly worse than the simple:

newMean <- function(x, trim = 0, rmNA = FALSE, ...) {
  mean(x, trim = trim, na.rm = rmNA, ...)
}

Другие советы

As I indicated I'm not exactly sure what your goal is, but if all you wanted was to convert an unnamed argument list to one that you could pass to mean.default, then it would be as simple as:

newMean <- function(x,...) {
  dots <- list(...)
  mean(x, trim = dots[[1]], na.rm = dots[[2]] )
}

The utter simplicity of this makes me think I'm not understanding the problem or error you are encountering, but this demonstrates matching on names when they do exist:

newMean.partial <- function(x,...) {
   dots <- list(...)
   names(dots)[names(dots)=="rmNA"] <- "na.rm"
   x.list <- list(x=x)
   dots <- c(x.list,dots)
  print(dput(dots))
 }
 newMean.partial(c(1:10,1000,NA),.2,rmNA=TRUE)
# structure(list(x = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1000, NA), 
#     0.2, na.rm = TRUE), .Names = c("x", "", "na.rm"))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top