Función lapply en R

Aprende a usar la función lapply en R

La función lapply forma parte de la familia de funciones apply y permite aplicar una función sobre un vector o una lista, devolviendo una lista. En este tutorial explicaremos cómo usar la función lapply en R con varios ejemplos.

Sintaxis de la función lapply()

La función lapply aplica una función a una lista o a un vector y devuelve una lista de la misma longitud que el objeto de entrada. La sintaxis de la función es como sigue:

lapply(X,   # Lista o vector
       FUN, # Función a ser aplicada
       ...) # Argumentos adicionales para ser pasados a FUN

¿Cómo usar la función lapply en R?

Usar la función lapply es muy sencillo, tan solo debes pasar la lista o el vector y especificar la función que quieres que se aplique a cada uno de sus elementos.

Iterar sobre una lista

Considera, por ejemplo, la siguiente lista con dos elementos llamados A y B.

a <- list(A = c(8, 9, 7, 5),
          B = data.frame(x = 1:5, y = c(5, 1, 0, 2, 3)))
a
$A
[1] 8 9 7 5

$B
  x y
1 1 5
2 2 1
3 3 0
4 4 2
5 5 3

Si aplicas la función sum a la lista anterior obtendrás la suma de cada uno de sus elementos (la suma de los elementos del vector y la suma de los elementos del data frame).

lapply(a, sum)
$A
[1] 29

$B
[1] 26

Iterar sobre un vector

Si tienes un vector, la función lapply aplicará la función que especifiques a cada uno de los elementos del vector. Como ejemplo, considera el vector b y calcula la raíz cuadrada de cada elemento como se indica en el siguiente bloque de código:

b <- c(12, 18, 6)

lapply(b, sqrt)
[[1]]
[1] 3.464102

[[2]]
[1] 4.242641

[[3]]
[1] 2.44949

Si pasas una lista a lapply, la función que corresponda se aplicará a todos los elementos de la lista. Si pasas un vector, la función se aplicará a cada elemento del vector.

lapply con argumentos adicionales

Vale la pena mencionar que si la función que estás pasando al argumento FUN tiene argumentos adicionales puedes establecerlos después de la función, usando una coma, tal y como se muestra en el siguiente ejemplo donde especificamos el argumento probs de la función quantile.

c <- list(A = c(56, 12, 57, 24), B = c(89, 12, 64, 18, 65, 76))

lapply(c,                           # Lista
       quantile,                    # Función aplicada
       probs = c(0.25, 0.5, 0.75))  # Argumento adicional de la función quantile
$A
  25%   50%   75% 
21.00 40.00 56.25 

$B
  25%   50%   75% 
29.50 64.50 73.25

lapply con una función propia

También es posible aplicar una función propia con la función lapply. Para ese propósito puedes crear una función y pasar su nombre al argumento FUN o escribir directamente la función dentro de la función lapply, tal y como se muestra en el siguiente bloque de código:

d <- 1:3

# Función para elevar al cuadrado
fun <- function(x) {
    x ^ 2
}

# Aplicamos la función 'fun'
lapply(d, fun)
lapply(d, FUN = function(x) x ^ 2) # Equivalente
lapply(d, function(x) x ^ 2) # Equivalente
[[1]]
[1] 1

[[2]]
[1] 4

[[3]]
[1] 9

lapply vs bucle for

La función lapply se puede usar para evitar bucles for, que son conocidos por ser lentos en R si no se aplican de forma correcta. Considera que quieres devolver una lista que contenga los números pares de un vector elevados al cubo y los impares elevados a la cuarta potencia. En tal caso podrías escribir:

# Lista vacía de 5 elementos
x <- vector("list", 5)

# Vector
vec <- 1:5

for(i in vec) {
    if(i %% 2 == 0) { # Comprobamos si el elemento 'i' es par
        x[[i]] <- i ^ 3
    } else {
        x[[i]] <- i ^ 4
    }
}
x

Una alternativa podría ser utilizar la función lapply de la siguiente manera:

fun <- function(i) {
   if(i %% 2 == 0) {
        i ^ 3
   } else {
        i ^ 4
    }
}

lapply(vec, fun)

La salida en ambos casos será la misma:

[[1]]
[1] 1   # <- Cuarta potencia de 1

[[2]]
[1] 8   # <- 2 al cubo

[[3]]
[1] 81  # <- Cuarta potencia de 3

[[4]]
[1] 64  # <- 4 al cubo

[[5]]
[1] 625 # <- Cuarta potencia de 5

Tan solo serás capaz de usar la función lapply en lugar de un bucle for si quieres devolver una lista de la misma longitud que el vector con el que quieres iterar.

lapply vs sapply en R

Las funciones lapply y sapply son muy similares, ya que la primera es un wrapper de la segunda. La principal diferencia entre ambas funciones es que lapply devuelve una lista en lugar de un array. Sin embargo, si estableces simplify = FALSE en la función sapply ambas devolverán una lista.

Para aclarar lo anterior, si aplicas la función sqrt a un vector con la función lapply obtendrás una lista de la misma longitud del vector, donde cada elemento de la lista es la raíz cuadrada de cada elemento del vector original:

lapply(c(4, 9, 16), FUN = sqrt)
[[1]]
[1] 2

[[2]]
[1] 3

[[3]]
[1] 4

Sin embargo, si usas la función sapply en su lugar, obtendrás la misma salida pero ésta será un vector.

sapply(c(4, 9, 16), FUN = sqrt)
2 3 4

Recuerda que también puedes devolver una lista con la función sapply, estableciendo el argumento simplify como FALSE o envolviéndola con la función as.list.

sapply(c(4, 9, 16), FUN = sqrt, simplify = FALSE)
as.list(sapply(c(4, 9, 16), sqrt)) # Equivalente
[[1]]
[1] 2

[[2]]
[1] 3

[[3]]
[1] 4

Análogamente a lo anterior también es posible devolver un vector con la función lapply usando las funciones unlist o simplify2array como sigue:

unlist(lapply(c(4, 9, 16), sqrt)) 
simplify2array(lapply(c(4, 9, 16), sqrt)) # Equivalente

Más ejemplos de la función lapply en R

lapply en las columnas de un data frame

Supón que tienes un data frame y que quieres multiplicar los elementos de la primera columna por uno, los elementos de la segunda por dos y así sucesivamente.

Por una parte, si quieres realizar el cálculo para todas las columnas puedes escribir:

df <- data.frame(x = c(6, 2), y = c(3, 6), z = c(2, 3))

# Función aplicada a todas las columnas
lapply(1:ncol(df), function(i) df[, i] * i)
[[1]]
[1] 6 2

[[2]]
[1]  6 12

[[3]]
[1] 6 9

Por otra parte, si quieres usar la función lapply en determinadas columnas del data frame puedes ejecutar:

# Función aplicada a la primera y tercera columna
lapply(c(1, 3), function(i) df[, i] * i)
[[1]]
[1] 6 2

[[2]]
[1] 6 9

Funciones lapply anidadas

En caso de ser necesario también es posible anidar múltiples funciones lapply. Considera que quieres iterar sobre las columnas y filas de un data frame y aplicar una función a cada celda. Para ese propósito, y suponiendo que quieres multiplicar cada celda por cuatro, puedes escribir algo como lo que sigue:

df <- data.frame(x = c(6, 2), y = c(3, 6))

# Lista vacía de dos elementos
res <- vector("list", 2)

for(i in 1:ncol(df)) {
    for (j in 1:nrow(df)) {
        res[[j]][i] <- df[j, i] * 4
    }
}

res
[[1]]        # <- Primera fila por cuatro
[1] 24 12

[[2]]        # <- Segunda fila por cuatro
[1]  8 24

Puedes obtener los mismos valores anidando dos funciones lapply, aplicando un lapply dentro del argumento FUN del primero:

lapply(1:ncol(df), function(i) {
       unlist(lapply(1:nrow(df), function(j) {
              df[j, i] * 4
       }))
})

Ten en cuenta que este ejemplo solo es para mostrar que se pueden anidar varios lapply y tiene solo fines educativos, ya que simplemente podrías escribir df * 4 para obtener los mismos valores que la salida.