Написание метода S4 на основе конкатенационной функции base R c (). Использование многоточия, точек, - PullRequest
1 голос
/ 08 января 2020

Я пытаюсь написать метод для конкатенации двух классов S4, которые я определил:

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

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

members каждого экземпляра класса My_group являются членами My_item class, но я не включил здесь код проверки, который обеспечивает выполнение этого требования.

I wi sh, чтобы написать метод конкатенации для класса My_group на основе c() из базы R. Его входные данные будут быть любым числом (включая ноль) элементов, которые могут быть смесью членов класса My_item или класса My_group. Метод должен возвращать один член класса My_group, состоящий из всех My_item членов на входах, так же, как c(c(1, 2), 3) возвращает c(1, 2, 3).

Я понимаю, что определение моего метода должно точно следуйте определению c() и поэтому должны принять следующую форму:

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

Мой вопрос касается функции, которая выполняет работу.

Я могу написать прямую функцию R, которая делает то, что я хочу:

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 - это простой метод, который распаковывает члена класса My_group в список его члены.)

Если я определю a1, a2, a3 как члены класса My_item, а g1 как My_group с членами a1 и 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))

затем myf(g1, a3) возвращает My_group с 3 членами, как требуется.

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

Но если я определю свой метод, используя тот же код, что и в функции myf, следующим образом:

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)
  }
)

Я получил неправильный ответ:


 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

Метод, похоже, проигнорировал g1.

Я подозреваю, что неправильно понял роль я, таинственный x, который появляется в определении c(), но я не могу получить больше, чем это в моем диагнозе.

Редактировать: следуя полезному и аргументированному предложению JDL, что я использую setClassUnion Я написал следующее с помощью простого метода, который должен просто возвращать аргументы, переданные c().

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


Но я нахожу

    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

Я, очевидно, все еще ошибаюсь.

Второе редактирование: предложение Алана О'Каллагана решило проблему. Для записи, мой метод теперь:

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)
  }
)

Это дает:

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


, что именно то, что я хотел.

Ответы [ 2 ]

0 голосов
/ 10 января 2020

Как отметил JDL, x используется для отправки метода. Ваша функция игнорировала его и использовала только .... Это должно работать (не проверено)

setMethod(
  f = "c",
  signature = "My_group",
  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)
  }
)
0 голосов
/ 09 января 2020

В настоящее время невозможно использовать ... для выбора методов, если аргументы, составляющие ..., не имеют одинаковый класс. Таким образом, ваш подход работает, если вы кормите c кучу my_item объектов или my_group объектов, но не, если вы кормите его смесью.

На странице справки для dotsMethods:

"Методы, определенные для таких функций, будут выбраны и вызваны, когда все аргументы, соответствующие« ... », принадлежат указанному классу или некоторому подклассу этого класса.

[... ]

Когда у вас есть вычисления, которые подходят для более чем одного существующего класса, удобным подходом может быть определение объединения этих классов с помощью вызова setClassUnion. "

Поэтому рекомендуется подход будет заключаться в создании объединения классов:

setClassUnion("mySortOfThing",c("my_item","my_group"))

, а затем написать метод для c, который использует mySortOfThing в качестве сигнатуры для ...,

Редактировать:

У данного кода есть отдельная проблема: c соответствует его аргументам positionally. В определении по умолчанию c есть только аргумент .... Вопрос определяет метод с аргументами x,...,recursive, который может означать, что первый аргумент не go, где вы ожидаете, и может быть проигнорирован. Если вам нужен отдельный аргумент x, вам нужно будет поставить его после ....

...