by
медленнее, чем tapply
, потому что он оборачивает by
. Давайте посмотрим на некоторые тесты: tapply
в этой ситуации более чем в 3 раза быстрее, чем при использовании by
ОБНОВЛЕНО, чтобы включить замечательную рекомендацию @Roland:
library(rbenchmark)
library(data.table)
dt <- data.table(x,key="f")
using.tapply <- quote(tapply(x[, 1], x[, "f"], mean))
using.by <- quote(by(x[, 1], x[, "f"], mean))
using.dtable <- quote(dt[,mean(col1),by=key(dt)])
times <- benchmark(using.tapply, using.dtable, using.by, replications=10, order="relative")
times[,c("test", "elapsed", "relative")]
#------------------------#
# RESULTS #
#------------------------#
# COMPARING tapply VS by #
#-----------------------------------
# test elapsed relative
# 1 using.tapply 2.453 1.000
# 2 using.by 8.889 3.624
# COMPARING data.table VS tapply VS by #
#------------------------------------------#
# test elapsed relative
# 2 using.dtable 0.168 1.000
# 1 using.tapply 2.396 14.262
# 3 using.by 8.566 50.988
Если x$f является фактором, потеря эффективности между tapply и by еще больше!
Хотя обратите внимание, что они оба улучшаются по сравнению с нефакторными входными данными, в то время как data.table остается примерно таким же или хуже
x[, "f"] <- as.factor(x[, "f"])
dt <- data.table(x,key="f")
times <- benchmark(using.tapply, using.dtable, using.by, replications=10, order="relative")
times[,c("test", "elapsed", "relative")]
# test elapsed relative
# 2 using.dtable 0.175 1.000
# 1 using.tapply 1.803 10.303
# 3 using.by 7.854 44.880
Что касается почему, краткий ответ находится в самой документации.
?by
:
Описание
Функция by — это объектно-ориентированная оболочка для tapply, применяемая к фреймам данных.
давайте посмотрим на источник для by
(или, точнее, by.data.frame
):
by.data.frame
function (data, INDICES, FUN, ..., simplify = TRUE)
{
if (!is.list(INDICES)) {
IND <- vector("list", 1L)
IND[[1L]] <- INDICES
names(IND) <- deparse(substitute(INDICES))[1L]
}
else IND <- INDICES
FUNx <- function(x) FUN(data[x, , drop = FALSE], ...)
nd <- nrow(data)
ans <- eval(substitute(tapply(seq_len(nd), IND, FUNx, simplify = simplify)),
data)
attr(ans, "call") <- match.call()
class(ans) <- "by"
ans
}
Мы сразу видим, что по-прежнему есть вызов tapply
плюс множество дополнений (включая вызовы deparse(substitute(.))
и eval(substitute(.))
, оба из которых относительно медленные). Поэтому имеет смысл, что ваш tapply
будет относительно быстрее, чем аналогичный вызов by
.
person
Ricardo Saporta
schedule
04.12.2012
*apply
(некоторые допускают несколько аргументов) или вложенная конструкцияtapply(tapply(...))
. - person Carl Witthoft   schedule 04.12.2012