[R] setMethod for "["

Martin Morgan mtmorgan at fhcrc.org
Fri Mar 21 18:05:07 CET 2008


Hi Musa --

Musa Parmaksiz wrote:
> Hi Martin,
> 
> Thanks for the suggestions.
> 
> If possible I would avoid defining several methods with different 
> signatures. For the first solution,namely
> 
> setMethod("[",
>           signature=signature(x="myClass"),
>           function(x, i, j, ..., drop=FALSE)
>       {
>           if (missing(i) && missing(j))
>               callNextMethod(x=x at x,,, ..., drop=drop)
>           else if (missing(j))
>               callNextMethod(x=x at x, i=i, , ..., drop=drop)
>           else if (missing(i))
>               callNextMethod(x=x at x, , j=j, ..., drop=drop)
>           else
>                callNextMethod(x=x at x, i=i, j=j, ..., drop=drop)
>       })
> 
> one cannot distinguish between test[1] and test[1,] ! I suspect we could 
> use nargs()...

I thought I had seen a cleaner solution to this; perhaps someone else
will contribute. Here's what I think:

One could do something like nargs() - length(list(...)) -
!missing(drop), but as a class designer a better decision might be to
define [1] to be the same as [1,], and provide other ways for the
user to convert a matrix-like object to a vector-like object.

To make this point a bit stronger, an interpretation of '[' is that it
returns a subset of the original object (i.e., an object of the same
class as the original). This is in contrast to '[[', which might be
interpreted as returning the contents of (a subset of) the
object. '[' returning a vector-like object when invoked on a
matrix-like object violates this interpretation, and might therefore
be avoided.

Also, it might seem good to 'mimic' the behavior of [ on matrices.
But remember that the method defined above matches arguments by name
rather than position, so already one is departing from the way in
which subsets work with the primitive [. I would say that this
departure is an improvement over the situation with primitive [, and
it would be a mistake to implement [ for myClass in such a way as to
restore position-matching.

These are my opinions, probably others arrive at different conclusions.

Martin

> On Fri, Mar 21, 2008 at 12:43 AM, Martin Morgan <mtmorgan at fhcrc.org 
> <mailto:mtmorgan at fhcrc.org>> wrote:
> 
>     Hi Musa --
> 
>     Musa Parmaksiz wrote:
>      > Hi R-Help,
>      >
>      > Please consider the following simple case: I have a class like
>      >
>      > setClass("myClass",
>      >                representation(x="matrix", y="character"))
>      >
>      > and I would like to use the method *"["* for a *myClass* objects (but
>      > changing the default *drop* argument from TRUE to FALSE):
>      >
>      > setMethod("[","myClass",
>      > function(x,i,j,...,drop=FALSE)
>      > {
>      >     x <- x at x
>      >     callNextMethod()
> 
>     I think you are hoping that x at x will be subsetted, and it appears that
>     it is. But I think this is a bug. 'callNextMethod' without any arguments
>     should be using the 'x' in the signature. This should result in an error
>     like the one seen here
> 
>     setMethod("[",
>               signature=signature(x="myClass"),
>               function(x, i, j, ..., drop=FALSE)
>           {
>               callNextMethod()
>           })
> 
>      > test[1,]
>     Error in x[i = i, j = , ...] : object is not subsettable
> 
>     Normally, to change the value of the variable 'seen' by the next method,
>     one would expect to have to write something like
> 
> 
>     setMethod("[",
>               signature=signature(x="myClass"),
>               function(x, i, j, ..., drop=FALSE)
>           {
>               callNextMethod(x=x at x, i=i, j=j, ..., drop=drop)
>           })
> 
>     You can now see how this is a little more complicated -- if j is missing
>     in the original function call, then it can't be used in an assignment in
>     callNextMethod. You'd have to write something like
> 
>     setMethod("[",
>               signature=signature(x="myClass"),
>               function(x, i, j, ..., drop=FALSE)
>           {
>               if (missing(i) && missing(j))
>                   callNextMethod(x=x at x,,, ..., drop=drop)
>               else if (missing(j))
>                   callNextMethod(x=x at x, i=i, , ..., drop=drop)
>               else if (missing(i))
>                   callNextMethod(x=x at x, , j=j, ..., drop=drop)
>               else
>                    callNextMethod(x=x at x, i=i, j=j, ..., drop=drop)
>           })
> 
>     or, since this is really an implementation that dispatches on the
>     'misssing'-ness of i, j, a series of methods like
> 
>     setMethod("[",
>               signature=signature(
>                   x="myClass",
>                   i="ANY',
>                   j="missing")
>                function(x, i, j, ..., drop=FALSE)
>           {
>                callNextMethod(x at x, i=i, , ..., drop=drop)
>           })
> 
>     Notice too how it is necessary to specify the argument list in quite an
>     odd way, with a ',' for the 'missing' variable(s). This is because "["
>     is a so-called 'Primitive' function, and primitive functions match by
>     position rather than the usual matching by name:
> 
>      > matrix(1:20,5)[4,2]
>     [1] 9
>      > matrix(1:20,5)[j=4,i=2]
>     [1] 9
> 
>     !
> 
>     I read somewhere that when faced with learning complicated systems,
>     otherwise intelligent people will develop wildly inaccurate stories to
>     explain why the system 'works'. The above is my story.
> 
>     Martin
> 
> 
>      >     x<-as.myClass(x)
>      > }
>      > )
>      >
>      > suppose that *as.myClass* method has been already defined.
>     Actually, all I
>      > want is to pass all the arguments to *"["* method for *matrix*,
>     except
>      > changing the default behaviour for *drop*.
>      >
>      > When I execute:
>      >> test<-new("myClass",x=cbind(1:3,4:6),y="a")
>      >> test[1,] # works as expected
>      > [1] 1 4
>      >
>      >> test[1,drop=TRUE] # does not work
>      > Error: argument "j" is missing, with no default
>      > Error in callNextMethod() : error in evaluating a 'primitive'
>     next method
>      >
>      > but with a matrix the two cases work:
>      >> m<-cbind(1:3,4:6)
>      >> m[1,]
>      > [1] 1 4
>      >> m[1,drop=TRUE]
>      > [1] 1
>      >
>      > Can you please advise me a solution for this problem?
>      > Thank you in advance for the help.
>      >
>      > Musa
>      >
>      >       [[alternative HTML version deleted]]
>      >
>      > ______________________________________________
>      > R-help at r-project.org <mailto:R-help at r-project.org> mailing list
>      > https://stat.ethz.ch/mailman/listinfo/r-help
>      > PLEASE do read the posting guide
>     http://www.R-project.org/posting-guide.html
>      > and provide commented, minimal, self-contained, reproducible code.
> 
> 
>     --
>     Martin Morgan
>     Computational Biology / Fred Hutchinson Cancer Research Center
>     1100 Fairview Ave. N.
>     PO Box 19024 Seattle, WA 98109
> 
>     Location: Arnold Building M2 B169
>     Phone: (206) 667-2793
> 
> 


-- 
Martin Morgan
Computational Biology / Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N.
PO Box 19024 Seattle, WA 98109

Location: Arnold Building M2 B169
Phone: (206) 667-2793



More information about the R-help mailing list