Quantcast
Channel: Active questions tagged r - Stack Overflow
Viewing all articles
Browse latest Browse all 201919

Writing S4 method based on base R concatenation function c(). Use of ellipsis, dots ,

$
0
0

I am trying to write a method for concatenation of two S4 classes that I have defined:

setClass("My_item",
         representation(contents = "vector"))

setClass("My_group",
         representation(members = "list"))

The members of every instance of the My_group class are all members of the My_item class but I have not included here the validation code that enforces that requirement.

I wish to write a concatenation method for the My_group class based on c() from base R. Its inputs will be any number (including zero) of elements which might be a mixture of members of either My_item class or the My_group class. The method should return a single member of the My_group class consisting of all the My_item members in the inputs, just as c(c(1, 2), 3) returns c(1, 2, 3).

I understand that the definition of my method must follow exactly the definition of c() and so must take the following form:

setMethod(
  f = "c",
  signature = "My_group",
  definition = function(x, ..., recursive = FALSE) {
    [code to be written]
}
)

My question is about the function that does the work.

I can write a straight R function that does what I want:

myf<- function(...){
  elements <- list(...) 
  if (length(elements) != 0) { 
    items <- unlist(lapply(
      elements,
      FUN = function(object) {
        if (is(object, "My_group")) {
          return(getMy_group(object))
        } else {
          return(object)
        }
      }
    ))
    object <- new("My_group",
                  members = items )
  } else {
    object <-  new("My_group")
  }
}

(getMy_group is a simple method that unpacks a member of the My_group class into a list of its members.)

If I define a1, a2, a3 as members of the My_item class, and g1 as a My_group with members a1 and a2,

a1 <- new("My_item", contents = c(1, 2, 3))
a2 <- new("My_item", contents = c( "x", "y", "z"))
a3 <- new("My_item", contents = c(0.1, 0.2, 0.3))

g1 <- new("My_group", members = list(a1, a2))

then myf(g1, a3) returns a My_group with 3 members, as required.

R>str(myf(g1, a3))
Formal class 'My_group' [package ".GlobalEnv"] with 1 slot
  ..@ members:List of 3
  .. ..$ :Formal class 'My_item' [package ".GlobalEnv"] with 1 slot
  .. .. .. ..@ contents: num [1:3] 1 2 3
  .. ..$ :Formal class 'My_item' [package ".GlobalEnv"] with 1 slot
  .. .. .. ..@ contents: chr [1:3] "x""y""z"
  .. ..$ :Formal class 'My_item' [package ".GlobalEnv"] with 1 slot
  .. .. .. ..@ contents: num [1:3] 0.1 0.2 0.3

But if I define my method using the same code as in the function myf, as follows:

setMethod(
  f = "c",
  signature = "My_group",
  definition = function(x, ..., recursive = FALSE) {
    elements <- list(...) 
    if (length(elements) != 0) { 
      items <- unlist(lapply(
        elements,
        FUN = function(object) {
          if (is(object, "My_group")) {
            return(getMy_group(object))
          } else {
            return(object)
          }
        }
      )) 
      object <- new("My_group",
                    members = items)
    } else {
      object <- new("My_group")
    }

    return(object)
  }
)

I get the wrong answer:


 R>c(g1, a3)
An object of class "My_group"
Slot "members":
[[1]]
An object of class "My_item"
Slot "contents":
[1] 0.1 0.2 0.3

The method appears to have ignored g1.

I suspect I have misunderstood the role of the, to me, mysterious x that appears in the definition of c(), but I can't get any further than that in my diagnosis.

Edit: following JDL's helpful and reasoned suggestion that I use setClassUnion I wrote the following with a simple method that is supposed merely to return the arguments supplied to c().:

    setClassUnion("mySortOfThing",c("My_item","My_group"))
    setMethod(
     f = "c",
     signature = "mySortOfThing",
     definition = function(x, ..., recursive = FALSE) {
      elements <- list(...)
      return(elements)
     }
    )


But I find

    g3 <- c(g1, a3) 
    R>str(g3)
    List of 1
     $ :Formal class 'My_item' [package ".GlobalEnv"] with 1 slot
      .. ..@ contents: num [1:3] 0.1 0.2 0.3

I am obviously still getting something wrong.

Second edit: alan o'callaghan's suggestion solved the problem. For the record my method is now:

setMethod(
  f = "c",
  signature = "My_union",
  definition = function(x, ..., recursive = FALSE) {
    elements <- list(x, ...)
    if (length(elements) != 0) { 
      items <- unlist(lapply(
        elements,
        FUN = function(object) {
          if (is(object, "My_group")) {
            return(getMy_group(object))
          } else {
            return(object)
          }
        }
      ))

      object <- new("My_group",
                    members = items)
    } else {
      object <- new("My_group")
    }

    return(object)
  }
)

That yields:

R>c(g1, a3)
An object of class "My_group"
Slot "members":
[[1]]
An object of class "My_item"
Slot "contents":
[1] 1 2 3


[[2]]
An object of class "My_item"
Slot "contents":
[1] "x""y""z"


[[3]]
An object of class "My_item"
Slot "contents":
[1] 0.1 0.2 0.3


which is exactly what I wanted.


Viewing all articles
Browse latest Browse all 201919

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>