A Família apply em Uma Frase
Todo membro da família apply faz o mesmo trabalho: pegar uma função e rodá-la sobre cada elemento de alguma estrutura, coletando os resultados. O que varia é o formato da entrada e o formato da saída. O mapa:
apply(m, MARGIN, FUN)- linhas ou colunas de uma matriz.lapply(x, FUN)- cada elemento de uma lista ou vetor; sempre retorna uma lista.sapply(x, FUN)- igual alapply, e depois simplifica para um vetor ou matriz quando dá.vapply(x, FUN, FUN.VALUE)- osapplycom um tipo de retorno declarado e obrigatório.mapply(FUN, x, y, ...)- percorre várias entradas em paralelo, elemento a elemento.tapply(values, groups, FUN)- aplicaFUNdentro de cada grupo: um resultado por grupo.
Todas aceitam as funções que você mesmo escreve, inclusive anônimas, e é daí que a família tira seu poder.
apply(): Linhas e Colunas de uma Matriz
apply recebe uma matriz, uma MARGIN (1 para linhas, 2 para colunas) e uma função:
A matriz é preenchida coluna por coluna, então as linhas são 1 3 5 e 2 4 6: as somas por linha saem como 9 12 e as somas por coluna como 3 7 11. Para os dois casos mais comuns o R base traz auxiliares dedicados e mais rápidos - rowSums, colSums, rowMeans, colMeans - então reserve apply para funções que não têm um equivalente, como apply(m, 2, max).
Uma armadilha: apply sobre um data frame o converte silenciosamente para matriz antes, o que força todas as colunas a um tipo comum. Para data frames, trate as colunas como uma lista e use lapply/sapply.
lapply() Sempre Dá uma Lista, sapply() Simplifica
lapply mapeia uma função sobre cada elemento e retorna uma lista do mesmo comprimento, aconteça o que acontecer:
Mesmo cálculo, dois formatos: lapply devolve uma lista contendo 85 e 80; sapply percebe que todo resultado tem comprimento 1 e simplifica para um vetor numérico nomeado - muito mais agradável de ler e de alimentar cálculos seguintes.
A conveniência tem um fio afiado, porém: o tipo de retorno do sapply depende dos dados. Se apenas um elemento produzir um comprimento diferente, a simplificação falha e você silenciosamente recebe uma lista de novo:
No modo interativo isso é um dar de ombros; dentro de um script significa código que funcionou o ano todo quebrando no dia em que os dados mudam de formato. É exatamente por isso que o vapply existe.
vapply(): O sapply Seguro Quanto a Tipos
vapply acrescenta um terceiro argumento, FUN.VALUE: um modelo declarando como um resultado deve ser. integer(1) significa "cada chamada retorna exatamente um inteiro":
Você recebe um vetor de inteiros nomeado - 5 6 6 - e, mais importante, uma garantia: se nchar algum dia retornasse dois valores ou um caractere, vapply pararia com um erro no local da chamada em vez de deixar um resultado malformado seguir adiante. A declaração serve também de documentação:
vapply(words, nchar, character(1))
# Error in vapply(words, nchar, character(1)) : values must be type 'character'
Regra prática: sapply no console, vapply em funções e scripts que precisam rodar sem supervisão.
mapply() e tapply(): Entradas Paralelas e Grupos
lapply percorre uma estrutura. Quando cada chamada precisa de um elemento de várias estruturas em posições correspondentes, use mapply - repare que a função vem primeiro:
Cada elemento da saída combina os primeiros valores, depois os segundos, e assim por diante: 10 200 3000 40000.
tapply é o membro voltado a grupos: ele divide values por groups e aplica a função dentro de cada grupo, retornando um resultado por grupo:
O grupo a tem média 30, o grupo b tem média 40. Essa é a resposta do R base para "média por categoria" - o mesmo formato de pergunta que group_by + summarize responde para data frames, então se você já está em território dplyr use aquilo; tapply brilha quando você tem dois vetores soltos e quer uma linha só.
Duas conveniências valem para a família inteira. Argumentos extras depois da função são encaminhados a ela em toda chamada:
E funções anônimas se encaixam sempre que nenhuma função pronta serve - sapply(x, \(v) max(v) - min(v)) calcula uma amplitude por elemento sem precisar nomear um auxiliar antes.
Família apply vs Laços for
Você vai ouvir que laços for são lentos no R e que a família apply é rápida. Isso é, em boa parte, mito. O que é genuinamente lento é fazer um resultado crescer dentro de um laço - result <- c(result, new_value) copia o vetor inteiro a cada passagem. Um laço que pré-aloca sua saída tem bom desempenho:
Então escolha por legibilidade, não por desempenho. A versão com apply diz o quê em uma linha - "eleve cada elemento ao quadrado" - e cuida da alocação por você, e é por isso que ela é o idioma para trabalho direto elemento a elemento. Um laço for se justifica quando as iterações dependem de resultados anteriores, quando você precisa de saídas antecipadas com break, ou quando o corpo é longo o bastante para que um lambda atrapalhe mais do que ajude. Os dois são R legítimo.
O Que Você Leva Daqui
- A família inteira é uma ideia só: rodar uma função sobre cada elemento e coletar os resultados.
applyé para linhas (MARGIN = 1) e colunas (MARGIN = 2) de matrizes; prefirarowSums/colMeansquando existirem.lapplysempre retorna uma lista;sapplysimplifica quando pode - o que significa que seu tipo de retorno pode mudar com os dados.vapplytrava o tipo de retorno; use-o em código que não pode te surpreender.mapplycombina várias entradas em paralelo;tapplyagrega valores dentro de grupos.- Laços for não são lentos por natureza - fazer vetores crescerem é. Escolha a grafia que se lê melhor.
A seguir: pipes - o operador que encadeia essas chamadas em pipelines legíveis, passo a passo.
Perguntas frequentes
Qual é a diferença entre lapply e sapply no R?
lapply sempre retorna uma lista, sem exceções. sapply faz o mesmo cálculo e depois tenta simplificar o resultado - para um vetor se todo elemento tiver comprimento 1, para uma matriz se compartilharem um comprimento, e de volta para uma lista se não conseguir. sapply é mais agradável no modo interativo; lapply (ou vapply) é mais seguro em scripts porque seu tipo de retorno nunca muda.
O que a função apply() faz no R?
apply(m, MARGIN, FUN) roda FUN sobre uma matriz: MARGIN = 1 a aplica a cada linha, MARGIN = 2 a cada coluna. apply(m, 1, sum) dá as somas por linha, apply(m, 2, mean) dá as médias por coluna. Ela é para matrizes e arrays - para listas e vetores use lapply/sapply.
A família apply é mais rápida que um laço for no R?
Normalmente não por muito - isso é um mito. Um laço for bem escrito, com o vetor de resultado pré-alocado, tem desempenho comparável. Os laços que deram má fama ao for fazem o resultado crescer um elemento por vez, copiando tudo a cada passagem. Escolha funções da família apply por concisão e clareza de intenção, não por velocidade.
Para que serve o vapply no R?
vapply é o sapply com um contrato: você declara o tipo e o comprimento de cada resultado, por exemplo vapply(x, nchar, integer(1)). Se a função retornar qualquer outra coisa, o R levanta um erro imediatamente em vez de silenciosamente te entregar uma estrutura inesperada. Prefira-o em código que precisa continuar funcionando sem supervisão.