apply(), lapply() y sapply() en R: repetir sin bucles

La familia apply sirve para aplicar una función a muchos elementos de golpe, sin escribir un bucle for. Son tres funciones que hacen casi lo mismo → la diferencia está en sobre qué trabajan y qué te devuelven.

La diferencia, en una tabla

Función Trabaja sobre Devuelve
apply() filas o columnas de una matriz o data frame vector o matriz
lapply() elementos de una lista o vector siempre una lista
sapply() igual que lapply vector, si puede simplificarlo

Ejemplo simple

sapply() es la que más vas a usar, porque devuelve algo cómodo de leer:

notas <- list(mates = c(7, 8, 9), lengua = c(5, 6, 7))

sapply(notas, mean)

Devuelve un vector con nombres: mates 8, lengua 6. Directo.

La misma llamada con lapply() devolvería una lista, más incómoda de mirar pero más segura cuando los resultados no son todos del mismo tipo:

lapply(notas, mean)

apply(): por filas o por columnas

Esta lleva un argumento más, el MARGIN, que indica la dirección: 1 = filas, 2 = columnas.

tabla <- matrix(1:12, nrow = 3)

apply(tabla, 1, sum)   # suma cada fila
apply(tabla, 2, sum)   # suma cada columna

Una regla para recordarlo: 1 es lo primero que se dice al describir una matriz ("filas por columnas"), así que 1 = filas.

Ejemplo algo más completo: resumir un data frame

El caso real más frecuente: sacar un estadístico de todas las columnas numéricas, como la mean() o la sd().

datos <- data.frame(
  edad = c(25, 32, 47, 51),
  altura = c(170, 165, 180, 175),
  peso = c(65, 59, 82, 78)
)

sapply(datos, mean)
sapply(datos, sd)

Y si quieres varias medidas a la vez, le pasas una función propia:

sapply(datos, function(x) c(media = mean(x), maximo = max(x)))

Ahí function(x) define una función anónima, que se usa solo en ese momento y no hace falta guardar con nombre.

Ojo con una cosa: si el data frame tiene columnas de texto, sapply(datos, mean) te devolverá avisos y NA. Filtra antes las numéricas:

numericas <- datos[, sapply(datos, is.numeric)]
sapply(numericas, mean)

¿Y los bucles for? Funcionan perfectamente y no tienen nada de malo. Pero con sapply() escribes una línea en vez de cuatro, y no te tienes que preocupar de crear el vector de resultados antes ni de ir rellenándolo.