[R] Dots argument in apply method

Christophe Pouzat christophe.pouzat at univ-paris5.fr
Wed Dec 7 13:37:34 CET 2005


Hello everyone,

I'm working on a package using S4 classes and methods and I ran into the 
following "problem" when I tried to create an "apply" method for objects 
of one of my new classes. I've found a way around the problem but I 
wonder if I did not paint myself into the corner. I'd like your opinion 
about that.

So I have an object "myObj" of class "myClass". I define a new function 
".apply.myClass" which is a "myClass" specific version of "apply". The 
trick is that I would like to have an additional formal argument in 
.apply.myClass compared to apply. More precisely we have:

apply(X, MARGIN, FUN, ...)

and I want:

.apply.myClass(x, margin, fun, groups = NULL, ...)

As long as I stay at the function level there is no problem. Life 
becomes harder when I want to define an "apply" method for myClass 
objects, method which should call .apply.myClass.
The formal argument "groups" in the myClass specific apply method will 
have to be passed in the dots argument, together with the "FUN" specific 
arguments. Then if the "groups" argument is provided it will have to be 
extracted and the remaining dots argument(s), if any, will have to be 
passed as such to .apply.myClass. Here is the way I did it:

## Start by setting a generic apply method
if (!isGeneric("apply"))
  setGeneric("apply", function(X, MARGIN, FUN, ...)       
standardGeneric("apply"))

## set apply method for myClass objects
setMethod("apply",
          signature(X = "myClass",
                    MARGIN = "numeric",
                    FUN = "function"),
          function(X, MARGIN, FUN, ...) {
            .call <- match.call(.apply.myClass)

            if (is.null(.call$groups)) myGroups <- NULL
            else myGroups <- .call$groups

            argList <- list(obj = .call$obj,
                            margin = .call$margin,
                            fun = .call$fun,
                            groups = myGroups
                            )
            if(!all(names(.call)[-1] %in% names(formals(.apply.myClass)))) {
              ## Some dots arguments have been provided
              otherNames <- (names(.call)[-1])[!(names(.call)[-1] %in% 
names(formals(.apply.myClass)))]
              remainingDots <- lapply(otherNames, function(i) .call[[i]])
              names(remainingDots) <- otherNames
              argList <- c(argList,remainingDots)
            }
            do.call(.apply.myClass, args = argList)
          }
          )

Does anyone have a quicker solution?

Thanks in advance,

Christophe.


PS: If you want a full example with actual class and .apply.myClass 
definitions, here is one:

## define class myClass
setClass("myClass", representation(Data = "data.frame", timeRange = 
"numeric"))

## create myObj an instantiation of myClass
myObj <- new("myClass",
             Data = data.frame(Time = sort(runif(10)),
               observation = I(matrix(rnorm(20),nrow=10,ncol=2)),
               label = factor(rep(1:2,5),levels = 1:2, labels = c("cat. 
1", "cat. 2"))
               ),
             timeRange = c(0,1)
             )

## create function .apply.myClass for myClass objects
.apply.myClass <- function(obj, ## object of class myClass
                           margin, ## a numeric which should be 1 or 2
                           fun, ## a function
                           groups = NULL, ## should fun be applied in a 
group   
                                                        ## specific manner?
                           ... ## additional arguments passed to fun
                           ) {

  ## attach the data frame contained in obj
  attach(obj at Data)
  ## make sure to detach it at the end
  on.exit(detach(obj at Data))
  ## get the variable names
  variableNames <- names(obj at Data)
  ## check that one variable is named "observation"
  if (!("observation" %in% variableNames))
    stop(paste("The slot Data of",
               deparse(substitute(obj)),
               "does not contain an observation variable as it should."
               )
         )
 
  if (margin == 1) {
    ## in that case we don't care of the group
    myResult <- apply(observation, 1, fun, ...)
    return(myResult)
  } else if (margin == 2) {
    if (is.null(groups)) {
      ## no groups defined
      myResult <- apply(observation, 2, fun, ...)
      return(myResult)
    } else {
      ## groups defined
      groups <- eval(groups)
      X <- levels(groups)
      dim(X) <- c(1,length(X))
      myResult <- apply(X,
                        2,
                        function(i) apply(observation[groups == i,],
                                          2,
                                          fun, ...)
                        )
      return(myResult)
    }
  } else {
    stop("margin should be set to 1 or 2.")
  }

}

-- 
A Master Carpenter has many tools and is expert with most of them.If you
only know how to use a hammer, every problem starts to look like a nail.
Stay away from that trap.
Richard B Johnson.
--

Christophe Pouzat
Laboratoire de Physiologie Cerebrale
CNRS UMR 8118
UFR biomedicale de l'Universite Paris V
45, rue des Saints Peres
75006 PARIS
France

tel: +33 (0)1 42 86 38 28
fax: +33 (0)1 42 86 38 30
web: www.biomedicale.univ-paris5.fr/physcerv/C_Pouzat.html




More information about the R-help mailing list