... em relação ao tempo de execução e / ou memória.
Se isso não for verdade, prove com um trecho de código. Observe que a aceleração por vetorização não conta. A aceleração deve vir de apply
( tapply
, sapply
...) em si.
As apply
funções em R não oferecem desempenho aprimorado em relação a outras funções de loop (por exemplo for
). Uma exceção a isso é lapply
que pode ser um pouco mais rápido, porque funciona mais no código C do que no R (consulte esta pergunta para um exemplo disso ).
Mas, em geral, a regra é que você deve usar uma função de aplicação para maior clareza, não para desempenho .
Eu acrescentaria que as funções de aplicação não têm efeitos colaterais , o que é uma distinção importante quando se trata de programação funcional com R. Isso pode ser substituído usando assign
ou <<-
, mas isso pode ser muito perigoso. Os efeitos colaterais também dificultam a compreensão de um programa, pois o estado de uma variável depende do histórico.
Editar:
Apenas para enfatizar isso com um exemplo trivial que calcula recursivamente a sequência de Fibonacci; isso pode ser executado várias vezes para obter uma medida precisa, mas o ponto é que nenhum dos métodos tem desempenho significativamente diferente:
> 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
Edição 2:
Em relação ao uso de pacotes paralelos para R (por exemplo, rpvm, rmpi, snow), eles geralmente fornecem apply
funções familiares (mesmo o foreach
pacote é essencialmente equivalente, apesar do nome). Aqui está um exemplo simples da sapply
função em snow
:
library(snow)
cl <- makeSOCKcluster(c("localhost","localhost"))
parSapply(cl, 1:20, get("+"), 3)
Este exemplo usa um cluster de soquete, para o qual nenhum software adicional precisa ser instalado; caso contrário, você precisará de algo como PVM ou MPI (consulte a página de cluster do Tierney ). snow
tem as seguintes funções de aplicação:
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, ...)
Faz sentido que apply
funções devam ser usadas para execução paralela, pois não têm efeitos colaterais . Quando você altera um valor variável dentro de um for
loop, ele é definido globalmente. Por outro lado, todas as apply
funções podem ser usadas com segurança em paralelo, porque as alterações são locais na chamada de função (a menos que você tente usar assign
ou <<-
, nesse caso, você pode introduzir efeitos colaterais). Escusado será dizer que é fundamental ter cuidado com as variáveis locais vs. globais, especialmente ao lidar com a execução paralela.
Editar:
Aqui está um exemplo trivial para demonstrar a diferença entre for
e no *apply
que diz respeito aos efeitos colaterais:
> 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
Observe como o df
ambiente pai é alterado, for
mas não é *apply
.
apply
família de funções. Portanto, a estruturação dos programas para que eles utilizem o aplicativo permite que eles sejam paralelizados a um custo marginal muito pequeno.snowfall
pacote e tentar os exemplos em sua vinheta.snowfall
constrói sobre osnow
pacote e abstrai ainda mais os detalhes da paralelização, simplificando ainda mais a execução deapply
funções paralelas .foreach
, desde então, tornou-se disponível e parece ser muito questionado sobre o SO.lapply
é "um pouco mais rápido" que umfor
loop. No entanto, não vejo nada sugerindo isso. Você mencionou apenas quelapply
é mais rápido quesapply
, o que é um fato bem conhecido por outros motivos (sapply
tenta simplificar a saída e, portanto, precisa fazer muita verificação de tamanho de dados e possíveis conversões). Nada relacionado afor
. Estou esquecendo de algo?Às vezes, a aceleração pode ser substancial, como quando você precisa aninhar loops para obter a média com base em um agrupamento de mais de um fator. Aqui você tem duas abordagens que fornecem exatamente o mesmo resultado:
Ambos fornecem exatamente o mesmo resultado, sendo uma matriz 5 x 10 com as médias e linhas e colunas nomeadas. Mas :
Ai está. O que eu ganhei? ;-)
fonte
*apply
é mais rápido. Mas acho que o ponto mais importante são os efeitos colaterais (atualizei minha resposta com um exemplo).data.table
é ainda mais rápido e acho "mais fácil".library(data.table)
dt<-data.table(X,Y,Z,key=c("Y,Z"))
system.time(dt[,list(X_mean=mean(X)),by=c("Y,Z")])
tapply
é uma função especializada para uma tarefa específica, é por isso que é mais rápida que um loop for. Ele não pode fazer o que um loop for pode fazer (enquanto o normalapply
pode). Você está comparando maçãs com laranjas.... e como acabei de escrever em outro lugar, vapply é seu amigo! ... é como sapply, mas você também especifica o tipo de valor de retorno que o torna muito mais rápido.
Atualização de 1 de janeiro de 2020:
fonte
for
os loops são mais rápidos no meu computador com Windows 10 e 2 núcleos. Eu fiz isso com5e6
elementos - um loop foi de 2,9 segundos vs. 3,1 segundos paravapply
.Escrevi em outro lugar que um exemplo como o de Shane não enfatiza realmente a diferença de desempenho entre os vários tipos de sintaxe de loop, porque todo o tempo é gasto dentro da função em vez de realmente estressar o loop. Além disso, o código compara injustamente um loop for sem memória com funções da família apply que retornam um valor. Aqui está um exemplo um pouco diferente que enfatiza o ponto.
Se você planeja salvar o resultado, aplicar as funções da família pode ser muito mais que o açúcar sintático.
(a simples deslistagem de z é de apenas 0,2s, portanto, o lapply é muito mais rápido. A inicialização do z no loop for é bastante rápida, porque eu estou dando a média das últimas 5 de 6 execuções para mover fora do sistema. dificilmente afeta as coisas)
Mais uma coisa a ser observada, porém, é que há outro motivo para usar as funções da família de aplicação, independentemente de seu desempenho, clareza ou falta de efeitos colaterais. Um
for
loop normalmente promove a colocação o máximo possível dentro do loop. Isso ocorre porque cada loop requer a configuração de variáveis para armazenar informações (entre outras operações possíveis). As instruções de aplicação tendem a ser tendenciosas de outra maneira. Muitas vezes, você deseja executar várias operações em seus dados, várias das quais podem ser vetorizadas, mas outras podem não ser. Em R, diferentemente de outros idiomas, é melhor separar essas operações e executar as que não são vetorizadas em uma instrução apply (ou versão vetorizada da função) e as que são vetorizadas como operações vetoriais verdadeiras. Isso geralmente acelera o desempenho tremendamente.Tomando o exemplo de Joris Meys, onde ele substitui um loop for tradicional por uma função R útil, podemos usá-lo para mostrar a eficiência de escrever código de uma maneira mais amigável para R para uma aceleração semelhante sem a função especializada.
Isso acaba sendo muito mais rápido que o
for
loop e um pouco mais lento que atapply
função otimizada incorporada. Não é porquevapply
é muito mais rápido que,for
mas porque está executando apenas uma operação em cada iteração do loop. Nesse código, todo o resto é vetorizado. Nofor
loop tradicional de Joris Meys, muitas operações (7?) Estão ocorrendo em cada iteração e há bastante configuração apenas para sua execução. Observe também como isso é mais compacto que afor
versão.fonte
2.798 0.003 2.803; 4.908 0.020 4.934; 1.498 0.025 1.528
, e vapply é ainda melhor:1.19 0.00 1.19
sapply
50% mais lento quefor
elapply
duas vezes mais rápido.y
como1:1e6
, nãonumeric(1e6)
(um vetor de zeros). Tentando alocarfoo(0)
az[0]
uma e outra não ilustra bem uma típicafor
uso loop. Caso contrário, a mensagem está no local.Ao aplicar funções sobre subconjuntos de um vetor,
tapply
pode ser bem mais rápido que um loop for. Exemplo:apply
, no entanto, na maioria das situações, não aumenta a velocidade e, em alguns casos, pode ser ainda mais lento:Mas para essas situações, temos
colSums
erowSums
:fonte
microbenchmark
é muito mais preciso quesystem.time
. Se você tentar compararsystem.time(f3(mat))
esystem.time(f4(mat))
obterá resultados diferentes quase sempre. Às vezes, apenas um teste de benchmark adequado é capaz de mostrar a função mais rápida.