Menu

apply, lapply, sapply no R: A Família apply Explicada

A família apply - apply, lapply, sapply, vapply, mapply e tapply - roda uma função sobre cada elemento dos seus dados. Veja o que cada uma faz e quando recorrer a elas.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

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 a lapply, e depois simplifica para um vetor ou matriz quando dá.
  • vapply(x, FUN, FUN.VALUE) - o sapply com 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) - aplica FUN dentro 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; prefira rowSums/colMeans quando existirem.
  • lapply sempre retorna uma lista; sapply simplifica quando pode - o que significa que seu tipo de retorno pode mudar com os dados.
  • vapply trava o tipo de retorno; use-o em código que não pode te surpreender.
  • mapply combina várias entradas em paralelo; tapply agrega 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.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR