Wow data.table
действительно быстрый и, кажется, просто работает! Многие из значений в successes
повторяются, поэтому можно сэкономить время, выполнив дорогостоящие вычисления binom.test
только по уникальным значениям.
fasterbinom <- function(x, ...) {
u <- unique(x)
idx <- match(x, u)
sapply(u, function(elt, ...) binom.test(elt, ...)$p.value, ...)[idx]
}
Для некоторых тайминги, мы имеем
dtbinom <- function(x, ...) {
dt <- data.table(x)
dt[, pp:=binom.test(x, ...)$p.value, by=x]$pp
}
с
> successes <-rbinom(100000, 625, 1/5)
> identical(fasterbinom(successes, 625, .2), dtbinom(successes, 625, .2))
[1] TRUE
> library(rbenchmark)
> benchmark(fasterbinom(successes, 625, .2), dtbinom(successes, 625, .2))
test replications elapsed relative user.self
2 dtbinom(successes, 625, 0.2) 100 4.265 1.019 4.252
1 fasterbinom(successes, 625, 0.2) 100 4.184 1.000 4.124
sys.self user.child sys.child
2 0.008 0 0
1 0.052 0 0
Интересно в этом случае сравнить зацикливание подходы
f0 <- function(s, ...) {
x0 <-NULL
for (i in seq_along(s))
x0 <-append(x0, binom.test(s[i], ...)$p.value)
x0
}
f1 <- function(s, ...) {
x1 <- numeric(length(s))
for (i in seq_along(s))
x1[i] <- binom.test(s[i], ...)$p.value
x1
}
f2 <- function(s, ...)
sapply(s, function(x, ...) binom.test(x, ...)$p.value, ...)
f3 <- function(s, ...)
vapply(s, function(x, ...) binom.test(x, ...)$p.value, numeric(1), ...)
где f1
я как правило, лучше использовать стратегию «предварительно распределить и заполнить» при использовании for
, f2
- это sapply
, что устраняет возможность плохо сформированного цикла for
из пользовательского захвата, а f3
- более безопасная и потенциально более быстрая версия sapply
, которая гарантирует, что каждый результат числовое значение длины-1.
Каждая функция возвращает тот же результат
> n <- 1000
> xx <-rbinom(n, 625, 1/5)
> res0 <- f0(xx, 625, .2)
> identical(res0, f1(xx, 625, .2))
[1] TRUE
> identical(res0, f2(xx, 625, .2))
[1] TRUE
> identical(res0, f3(xx, 625, .2))
[1] TRUE
apply
и в то время -как методы около 10% быстрее, чем для петель (в данном случае, разница между F0 и F1 может быть гораздо более существенным, когда отдельные элементы являются большими)
> benchmark(f0(xx, 625, .2), f1(xx, 625, .2), f2(xx, 625, .2),
+ f3(xx, 625, .2), replications=5)
test replications elapsed relative user.self sys.self user.child
1 f0(xx, 625, 0.2) 5 2.303 1.100 2.300 0 0
2 f1(xx, 625, 0.2) 5 2.361 1.128 2.356 0 0
3 f2(xx, 625, 0.2) 5 2.093 1.000 2.088 0 0
4 f3(xx, 625, 0.2) 5 2.212 1.057 2.208 0 0
sys.child
1 0
2 0
3 0
4 0
реальная скорость от голубевода алгоритма fasterbinom
/dtbinom
.
> identical(res0, fasterbinom(xx, 625, .2))
[1] TRUE
> benchmark(f2(xx, 625, .2), fasterbinom(xx, 625, .2), replications=5)
test replications elapsed relative user.self sys.self
1 f2(xx, 625, 0.2) 5 2.146 16.258 2.145 0
2 fasterbinom(xx, 625, 0.2) 5 0.132 1.000 0.132 0
user.child sys.child
1 0 0
2 0 0
Будет ли это значительно быстрее, чем цикл? Спасибо – biggob1
@ Решение biggob1 sapply не будет быстрее, но вы можете использовать решение data.table, которое будет намного быстрее - см. Обновленный ответ. –
Отлично. Это работало блестяще. Благодарю. – biggob1