Как вы расширяете rbind для данных.подкласс фреймов?
Мой вопрос в том, как вы расширяете rbind()
для работы с подклассом data.frame
? Я не могу должным образом расширить rbind()
для работы даже с очень простым подклассом. Следующий пример демонстрирует эту проблему:
Определение подкласса и метода:
new_df2 <- function(x, ...)
{
stopifnot(is.data.frame(x))
structure(x, class = c("df2", "data.frame"), author = "some user")
}
rbind.df2 <- function(..., deparse.level = 1)
{
NextMethod()
}
Я понимаю, что расширение rbind()
в этом случае не обязательно, но мой грандиозный план состоит в том, чтобы использовать rbind.data.frame()
на моем подклассе, а затем добавить несколько дополнительных проверок/атрибутов к его результату.
Если вы вызовете далее вы получаете ошибку: Error in NextMethod() : generic function not specified
.
Не работает:
t1 <- data.frame(a = 1:12, b = month.abb)
t2 <- new_df2(t1)
rbind(t2, t2)
Я также пытался использовать NextMethod(generic = "rbind")
, но в этом случае вы получите эту ошибку: Error in NextMethod(generic = "rbind") : wrong value for .Method
.
Также не работает:
rbind.df2 <- function(..., deparse.level = 1)
{
NextMethod(generic = "rbind")
}
rbind(t2, t2)
Я нахожусь в тупике и догадываюсь о границах моего понимания подклассов / методов тоже. Спасибо за любую помощь.
2 ответа:
Я рассмотрю конкретный случай
rbind()
ниже, но сначала отмечу, что мы могли бы генерировать дополнительные примеры, показывающие, что в общем случае нет проблем сNextMethod()
, Когда первый аргумент...
(относительно запроса на вознаграждение):f <- function(..., b = 3) UseMethod("f") f.a <- function(..., b = 3) { print("yes"); NextMethod() } f.integer <- function(..., b = 4) sapply(list(...), "*", b) x <- 1:10 class(x) <- c("a", class(x)) f(x) [1] "yes" [,1] [1,] 4 [2,] 8 [3,] 12 [4,] 16 [5,] 20 [6,] 24 [7,] 28 [8,] 32 [9,] 36 [10,] 40 f(x, b = 5) [1] "yes" [,1] [1,] 5 [2,] 10 [3,] 15 [4,] 20 [5,] 25 [6,] 30 [7,] 35 [8,] 40 [9,] 45 [10,] 50
Так почему же не rbind?работа df2?
Как оказалось,
rbind()
иcbind()
не являются нормальными дженериками. Во-первых, они внутренне универсальны; смотрите раздел "внутренние универсалии" здесь из старого S3 Хэдли Уикхема страница на Advanced R, или эта выдержка из текущей Advanced R :Некоторые генераторы S3, такие как [, sum () и cbind (), не называют UseMethod() поскольку они реализованы в C. Вместо этого они вызывают функции C. DispatchGroup() или DispatchOrEval ().
Этого недостаточно, чтобы причинить нам неприятности, как мы можем видеть, используя
sum()
в качестве примера:sum.a <- function(x, na.rm = FALSE) { print("yes"); NextMethod() } sum(x) [1] "yes" [1] 55
Однако для
rbind
иcbind
это еще более странно, как признается в комментариях в исходный код (начиная со строки 1025):/* cbind(deparse.level, ...) and rbind(deparse.level, ...) : */ /* This is a special .Internal */
... (Код опущен) ...
После этого дается некоторое объяснение правил отправки, но до сих пор я не смог использовать эту информацию, чтобы заставить/* Lazy evaluation and method dispatch based on argument types are * fundamentally incompatible notions. The results here are * ghastly.
NextMethod()
работать. В случае использования, приведенном выше, я бы последовал совету F. Privé из комментариев и сделал это:new_df2 <- function(x, ...) { stopifnot(is.data.frame(x)) structure(x, class = c("df2", "data.frame")) } rbind.df2 <- function(..., deparse.level = 1) { print("yes") # Or whatever else you want/need to do base::rbind.data.frame(..., deparse.level = deparse.level) } t1 <- data.frame(a = 1:12, b = month.abb) t2 <- new_df2(t1) rbind(t2, t2) [1] "yes" a b 1 1 Jan 2 2 Feb 3 3 Mar 4 4 Apr 5 5 May 6 6 Jun 7 7 Jul 8 8 Aug 9 9 Sep 10 10 Oct 11 11 Nov 12 12 Dec 13 1 Jan 14 2 Feb 15 3 Mar 16 4 Apr 17 5 May 18 6 Jun 19 7 Jul 20 8 Aug 21 9 Sep 22 10 Oct 23 11 Nov 24 12 Dec
Ответ состоит в том, чтобы расширить
rbind2
, а неrbind
. На странице справки изrbind2
:" это (S4) универсальные функции с методами по умолчанию.
...
Основное использование cbind2 (rbind2) заключается в рекурсивном вызове cbind () (rbind ()), когда выполняются оба этих требования:
Существует по крайней мере один аргумент, который является объектом S4, и
Сбой диспетчеризации S3 (см. раздел диспетчеризации в разделе cbind)."