Создайте класс S4, наследующий от фрейма данных

Я пишу пакет R. В этом пакете я хочу иметь специальный тип фрейма данных, который могут распознавать некоторые функции, например, с некоторыми дополнительными атрибутами, но в остальном действующий точно так же, как фрейм данных. Один из способов добиться того, чего я хочу, - просто установить некоторые атрибуты в обычном фрейме данных:

   makedf <- function() {
     df <- data.frame(ID=1:3)
     attr(df, "myDF") <- TRUE
     attr(df, "source") <- "my nose"
     return(df)
   }

   dosmth <- function(df) {
     if(!is.null(attr(df, "myDF"))) message(sprintf("Oh my! My DF! From %s!", attr(df, "source")))
     message(sprintf("num of rows: %d", nrow(df)))
   }

Когда dosmth() получает "myDF", у него есть дополнительная информация об источнике фрейма данных:

dosmth(data.frame(1:5))
#> num of rows: 5
dosmth(makedf())
#> Oh my! My DF! From my nose!
#> num of rows: 3

Точно так же с S3 это было бы довольно просто, и мы могли бы даже написать разные варианты dosmth, используя преимущества диспетчеризации методов. Как мне это сделать с S4?


person January    schedule 17.04.2020    source источник


Ответы (1)


Я не совсем уверен, что это то, что вы ищете, но звучит так, как будто вы хотите определить общую функцию, которая специализируется на ваших MyDF и data.frame, например этот репрекс

MyDF <- setClass(Class = "MyDF",
                 slots = c(source = "character"),
                 contains = c("data.frame"))

setGeneric("dosmth", function(x) message("Can only dosmth to a df or MyDF"))

setMethod("dosmth", signature("data.frame"), 
          function(x) message(sprintf("num of rows: %d", nrow(x))))

setMethod("dosmth", signature("MyDF"), 
          function(x)  message(sprintf("Oh my! My DF! From %s!", x@source)))

a_df   <- data.frame(a = 1, b = 2)
a_MyDF <- MyDF(a_df, source = "my nose")

dosmth("my nose")
#> Can only dosmth to a df or MyDF
dosmth(a_df)
#> num of rows: 1
dosmth(a_MyDF)
#> Oh my! My DF! From my nose!

Спасибо @JDL за комментарий, указывающий, что нам не нужен дополнительный слот «data.frame», чтобы поведение data.frame можно было правильно наследовать, как показано в следующем примере:

a_MyDF[1,]
#>   a b
#> 1 1 2

Создано 17 апреля 2020 г. в пакете reprex (v0.3.0)

person Allan Cameron    schedule 17.04.2020
comment
Наличие слота data="data.frame" в определении вашего класса сбивает с толку. Вам это не нужно — когда вы создаете класс, который contains другой, слот .Data автоматически добавляется к определению, содержащему унаследованный класс (так что в вашем примере теперь есть два слота фрейма данных, data, который содержит предоставленный фрейм данных, и .Data). который должен был содержать его, но не содержит). Попробуйте nrow на своем объекте my_DF — вы обнаружите, что он нарушает в противном случае, ведет себя точно так же, как требование фрейма данных. - person JDL; 17.04.2020
comment
@JDL Ты прав - мой плохой. Я отредактирую ответ. Спасибо. - person Allan Cameron; 17.04.2020
comment
Благодарю вас! И как я могу заставить a_MyDF наследовать метод show() data.frame? Я имею в виду — именно так, потому что прямо сейчас он также показывает информацию о слотах и ​​Object класса MyDF, другими словами — не ведет себя точно так же, как фрейм данных? - person January; 17.04.2020
comment
@January Я думаю, это потому, что нет метода show для класса data.frame, что вы можете подтвердить, выполнив showMethods("show"). Объекты, отличные от S4, для которых не определен конкретный метод show (например, фреймы данных), просто вызывают print (через showDefault) при вызове show. Итак, если вы хотите, чтобы MyDF был классом S4, то show — это одна из функций, для которой вам нужно установить метод: setMethod("show", signature("MyDF"), function(object) print(object)) - person Allan Cameron; 17.04.2020