2013-03-18 4 views
3

У меня есть вектор успехов и хочу, чтобы бинон.test проводил по каждому из значений. Есть ли более быстрый способ, чем этот цикл (у меня есть довольно много):Ускоренный цикл binom.test?

successes <-rbinom(100, 625, 1/5) 
x <-NULL 
for (i in 1:100) { 
x <-append(x, binom.test(successes[i], 625, 1/5)$p.value) 
} 

ответ

8

Вместо цикл можно использовать для расчета sapply()p.values для каждого значения успехов.

pp <- sapply(successes, function(x) binom.test(x, 625, 1/5)$p.value) 

Если вам нужно реальное ускорение процесса вы можете использовать преимущество пакета data.table. Сначала конвертируйте successes в data.table объект. Затем вычислите для каждой строки p.value.

library(data.table) 
dt<-data.table(successes) 
dt[,pp:=binom.test(successes, 625, 1/5)$p.value,by=successes] 
+0

Будет ли это значительно быстрее, чем цикл? Спасибо – biggob1

+0

@ Решение biggob1 sapply не будет быстрее, но вы можете использовать решение data.table, которое будет намного быстрее - см. Обновленный ответ. –

+0

Отлично. Это работало блестяще. Благодарю. – biggob1

4

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 
+0

Привет. Я не совсем понял, почему вы сказали ничего себе рядом с вершиной; не время для 'dtbinom' и' fastbinom' примерно такое же около 4,1 секунды? Это 4,1 секунды для 'replications = 100', поэтому каждая задача заняла 0,04 секунды. 'data.table' на самом деле не для небольших задач, поэтому я немного запутался здесь. Есть служебные данные '.data.table' и' data.table() 'call in' dtbinom' обычно будет вещами, которые ухудшали бы производительность при повторении в цикле небольших задач. –

+0

@MatthewDowle Я был впечатлен - data.table, как и fastbinom, на 1100x быстрее, чем наивный f2 для этого размера выборки, и оба они отлично масштабируются с размером (не показаны). Мне потребовалось некоторое умственное мышление, чтобы выяснить, как сделать fastbinom работать быстрее, тогда как data.table быстро выходит из строя. Очевидно, вы делаете правильные вещи таким образом, который интуитивно понятен вашим пользователям ... –

+0

А, я вижу сейчас. Я пропустил, что они были быстрее, чем наивные. Благодарю. –

Смежные вопросы