From renaud at mancala.cbio.uct.ac.za  Thu Dec  5 11:40:04 2013
From: renaud at mancala.cbio.uct.ac.za (Renaud Gaujoux)
Date: Thu, 5 Dec 2013 12:40:04 +0200
Subject: [Rd] S4 method for '[' with extra arguments: distinguishing between
 x[i] and x[i, ]
Message-ID: <CAHavPHH5jo_7q+=MT9tdM0v10MimCoNqq0rubZEJJKU4gRCj1Q@mail.gmail.com>

Hi,

I want to implement a '[' for an S4 class, that behaves differently
when called with a single index argument or multiple indexes (possibly
missing), like what happens when subsetting matrices x[i] vs. x[i, ].

I manage to do it using nargs() and checking if drop is missing (see
code below), but when I want to add an extra argument to the method
(before drop), then the parent call somehow changes and always
includes all indexes in the call (even missing ones) and nargs()
always returns the same value.

I thought there might be a generic for a single index (with no j in
the definition) but could not find its definition, and can't see how
setMethod will know for which '[' to define the method. Defining a
method for signature(x = 'A', j = 'missing') has the same issue.

Is there actually a way to do this?
Thank you.

Bests,
Renaud


####
# Code
####

# Class A
setClass('A', contains = 'character')

# No extra argument is fine
setMethod('[', 'A', function(x, i, j, ..., drop = TRUE){
            ca <- match.call()
            mdrop <- missing(drop)
            Nargs <- nargs() - !mdrop
            print(ca)
            print(nargs())
            print(mdrop)
            print(Nargs)
            if( !missing(i) && Nargs < 3 ) TRUE
            else FALSE
        })

testA <- function(){
    a <- new('A')
    tests <- c('a[1]', 'a[1,]', 'a[,1]')
    sapply(tests, function(s){
        message('\n#', s); message('single arg: ', eval(parse(text = s)))
        s <- sub(']', ', drop = FALSE]', s, fixed = TRUE)
        message('\n#', s); message('single arg: ', eval(parse(text = s)))
    })
    invisible()
}

testA()

# with extra argument => cannot distinguish the calls
setMethod('[', 'A', function(x, i, j, ..., extra = FALSE, drop = TRUE){
            ca <- match.call()
            mdrop <- missing(drop)
            Nargs <- nargs() - !mdrop
            print(ca)
            print(nargs())
            print(mdrop)
            print(Nargs)
            if( !missing(i) && Nargs < 3 ) TRUE
            else FALSE
        })

testA()

# System info
sessionInfo()
R.version


####
# RESULTS
####

> # Class A
> setClass('A', contains = 'character')
>
> # No extra argument is fine
> setMethod('[', 'A', function(x, i, j, ..., drop = TRUE){
+             ca <- match.call()
+             mdrop <- missing(drop)
+             Nargs <- nargs() - !mdrop
+             print(ca)
+             print(nargs())
+             print(mdrop)
+             print(Nargs)
+             if( !missing(i) && Nargs < 3 ) TRUE
+             else FALSE
+         })
[1] "["
>
> testA <- function(){
+     a <- new('A')
+     tests <- c('a[1]', 'a[1,]', 'a[,1]')
+     sapply(tests, function(s){
+         message('\n#', s); message('single arg: ', eval(parse(text = s)))
+         s <- sub(']', ', drop = FALSE]', s, fixed = TRUE)
+         message('\n#', s); message('single arg: ', eval(parse(text = s)))
+     })
+     invisible()
+ }
>
> testA()

#a[1]
a[i = 1]
[1] 2
[1] TRUE
[1] 2
single arg: TRUE

#a[1, drop = FALSE]
a[i = 1, drop = FALSE]
[1] 3
[1] FALSE
[1] 2
single arg: TRUE

#a[1,]
a[i = 1]
[1] 3
[1] TRUE
[1] 3
single arg: FALSE

#a[1,, drop = FALSE]
a[i = 1, drop = FALSE]
[1] 4
[1] FALSE
[1] 3
single arg: FALSE

#a[,1]
a[j = 1]
[1] 3
[1] TRUE
[1] 3
single arg: FALSE

#a[,1, drop = FALSE]
a[j = 1, drop = FALSE]
[1] 4
[1] FALSE
[1] 3
single arg: FALSE
>
> # with extra argument => cannot distinguish the calls
> setMethod('[', 'A', function(x, i, j, ..., extra = FALSE, drop = TRUE){
+             ca <- match.call()
+             mdrop <- missing(drop)
+             Nargs <- nargs() - !mdrop
+             print(ca)
+             print(nargs())
+             print(mdrop)
+             print(Nargs)
+             if( !missing(i) && Nargs < 3 ) TRUE
+             else FALSE
+         })
[1] "["
>
> testA()

#a[1]
.local(x = x, i = i, j = j, drop = drop)
[1] 4
[1] FALSE
[1] 3
single arg: FALSE

#a[1, drop = FALSE]
.local(x = x, i = i, j = j, drop = drop)
[1] 4
[1] FALSE
[1] 3
single arg: FALSE

#a[1,]
.local(x = x, i = i, j = j, drop = drop)
[1] 4
[1] FALSE
[1] 3
single arg: FALSE

#a[1,, drop = FALSE]
.local(x = x, i = i, j = j, drop = drop)
[1] 4
[1] FALSE
[1] 3
single arg: FALSE

#a[,1]
.local(x = x, i = i, j = j, drop = drop)
[1] 4
[1] FALSE
[1] 3
single arg: FALSE

#a[,1, drop = FALSE]
.local(x = x, i = i, j = j, drop = drop)
[1] 4
[1] FALSE
[1] 3
single arg: FALSE
>
> # System info
> sessionInfo()
R version 3.0.2 (2013-09-25)
Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C
 [9] LC_ADDRESS=C               LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base
> R.version
               _
platform       x86_64-pc-linux-gnu
arch           x86_64
os             linux-gnu
system         x86_64, linux-gnu
status
major          3
minor          0.2
year           2013
month          09
day            25
svn rev        63987
language       R
version.string R version 3.0.2 (2013-09-25)
nickname       Frisbee Sailing
>



[Rd] S4 method for '[' with extra arguments: distinguishing between x[i] and x[i, ]
Renaud Gaujoux