Функции apply
в R не обеспечивают улучшенную производительность по сравнению с другими функциями зацикливания (например, for
).Единственное исключение из этого - lapply
, которое может быть немного быстрее, поскольку в коде C он выполняет больше работы, чем в R (см. этот вопрос для примера этого ).
Но в целом правило: , вы должны использовать функцию применения для ясности, а не для производительности .
Я бы добавил к этому, что применяемые функции не имеют никаких побочных эффектов , что является важным отличием, когда речь идет о функциональном программировании с использованием RЭто может быть отменено с помощью assign
или <<-
, но это может быть очень опасно.Побочные эффекты также затрудняют понимание программы, поскольку состояние переменной зависит от истории.
Редактировать:
Просто для того, чтобы подчеркнуть это тривиальным примером, который рекурсивно вычисляетПоследовательность Фибоначчи;это может быть выполнено несколько раз для получения точного измерения, но дело в том, что ни один из методов не имеет существенно отличающихся характеристик:
> fibo <- function(n) {
+ if ( n < 2 ) n
+ else fibo(n-1) + fibo(n-2)
+ }
> system.time(for(i in 0:26) fibo(i))
user system elapsed
7.48 0.00 7.52
> system.time(sapply(0:26, fibo))
user system elapsed
7.50 0.00 7.54
> system.time(lapply(0:26, fibo))
user system elapsed
7.48 0.04 7.54
> library(plyr)
> system.time(ldply(0:26, fibo))
user system elapsed
7.52 0.00 7.58
Редактировать 2:
Что касается использования параллельных пакетов для R (например, rpvm, rmpi, snow), они обычно предоставляют семейные функции apply
(даже пакет foreach
по сути эквивалентен, несмотря на название).Вот простой пример функции sapply
в snow
:
library(snow)
cl <- makeSOCKcluster(c("localhost","localhost"))
parSapply(cl, 1:20, get("+"), 3)
В этом примере используется кластер сокетов, для которого не требуется устанавливать дополнительное программное обеспечение;в противном случае вам понадобится что-то вроде PVM или MPI (см. Страница кластеризации Tierney ).snow
имеет следующие применяемые функции:
parLapply(cl, x, fun, ...)
parSapply(cl, X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
parApply(cl, X, MARGIN, FUN, ...)
parRapply(cl, x, fun, ...)
parCapply(cl, x, fun, ...)
Имеет смысл, что apply
функции должны использоваться для параллельного выполнения, так как они не имеют побочных эффектов .Когда вы изменяете значение переменной в цикле for
, оно устанавливается глобально.С другой стороны, все функции apply
можно безопасно использовать параллельно, поскольку изменения являются локальными для вызова функции (если только вы не попытаетесь использовать assign
или <<-
, в этом случае вы можете ввести побочные эффекты).Само собой разумеется, важно быть осторожным с локальными и глобальными переменными, особенно когда имеешь дело с параллельным выполнением.
Редактировать:
Вот тривиальный пример, чтобы продемонстрироватьразница между for
и *apply
в отношении побочных эффектов:
> df <- 1:10
> # *apply example
> lapply(2:3, function(i) df <- df * i)
> df
[1] 1 2 3 4 5 6 7 8 9 10
> # for loop example
> for(i in 2:3) df <- df * i
> df
[1] 6 12 18 24 30 36 42 48 54 60
Обратите внимание, как df
в родительской среде изменяется на for
, но не *apply
.