Función sapply en R

Aprende a usar la función sapply en R

La función sapply en R es una función vectorizada de la familia de aplicaciones que permite iterar sobre una lista o vector sin la necesidad de usar el bucle for, que es conocido por ser lento en R. En este tutorial mostraremos cómo trabajar con la función sapply de R con varios ejemplos.

La función sapply()

La función sapply en R aplica una función a un vector o lista y devuelve un vector, una matriz o un array. La función tiene la siguiente sintaxis:

sapply(X,   # Vector o lista
       FUN, # Función a ser aplicada
       ..., # Argumentos adicionales para ser pasados a FUN
       simplify = TRUE,  # Si FALSE devuelve una lista. Si "array" devuelve un array si es posible
       USE.NAMES = TRUE) # Si TRUE y si X es un vector de caracteres, usa los nombres de X

En las siguientes secciones revisaremos cómo trabajar con la función sapply con varios ejemplos.

Los ejemplos de este tutorial solo tienen sentido ilustrativo para entender cómo usar la función sapply, ya que hay mejores formas de obtener los resultados calculados.

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

Para usar la función sapply en R, deberás especificar la lista o el vector que deseas iterar en el primer argumento y la función que quieres aplicar a cada elemento del vector en el segundo. Ten en cuenta que puedes usar una función de cualquier paquete o una función personalizada:

sapply(1:4, sqrt)
# 1.000000 1.414214 1.732051 2.000000

# Equivalente a:
sapply(1:4, function(i) sqrt(i)) 

# También equivalente a:
mi_fun <- function(i) {
    sqrt(i)
}

sapply(1:4, mi_fun) 

Iterar sobre un vector

Considera, por ejemplo, que quieres calcular el cuadrado de los elementos de un vector. Usando un bucle for necesitarás escribir el siguiente código:

out <- numeric(10)

for (i in 1:10) {
    out[i] <- i ^ 2
}
out
1  4  9 16  25  36  49  64  81 100

Sin embargo, con la función sapply puedes obtener el mismo resultado escribiendo una sola línea de código:

sapply(1:10, function(i) i ^ 2)
1  4  9 16  25  36  49  64  81 100

Iterar sobre una lista

Si tienes una lista en lugar de un vector, los pasos son análogos, pero ten en cuenta que la función se aplicará a los elementos de la lista. En el siguiente ejemplo calcularemos el número de componentes de cada elemento de la lista con la función length.

Lista <- list(A = 1:5, B = 6:20, C = 1)

sapply(Lista, length)
A  B  C 
5 15  1

sapply vs lapply

La diferencia entre las funciones lapply y sapply es que la función sapply es un wrapper de la función lapply y devuelve un vector, una matriz o un array en lugar de una lista.

Considera que quieres calcular la exponencial de tres números. En este caso, si usas la función sapply obtendrás un vector como salida:

sapply(c(3, 5, 7), exp)
20.08554 148.41316 1096.63316

Pero si usas la función lapply, obtendrás una lista cuyos elementos corresponden a los componentes del vector anterior.

lapply(c(3, 5, 7), exp)
[[1]]
[1] 20.08554

[[2]]
[1] 148.4132

[[3]]
[1] 1096.633

Sin embargo, por un lado, si estableces el argumento simplify de la función sapply como FALSE obtendrás la misma salida que si usases la función lapply. Nótese que esto es lo mismo que usar la función as.list:

sapply(c(3, 5, 7), exp, simplify = FALSE)
as.list(sapply(c(3, 5, 7), exp)) # Equivalente
[[1]]
[1] 20.08554

[[2]]
[1] 148.4132

[[3]]
[1] 1096.633

Por otro lado, puedes convertir la salida de la función lapply al mismo tipo de salida de la función sapply usando las funciones simplify2array o unlist:

simplify2array(lapply(c(3, 5, 7), exp))
unlist(lapply(c(3, 5, 7), exp)) # Equivalente

Resumiendo, las funciones sapply y lapply son casi lo mismo, pero se diferencian en la clase de la salida.

Es más eficiente usar la función correspondiente que transformar la clase de la salida.

Función sapply con argumentos adicionales

La función sapply en R permite pasar argumentos adicionales a la función que estás aplicando después de la función. Considera a modo ilustrativo la siguiente lista con un valor NA:

mi_lista <- list(A = c(1, 4, 6), B = c(8, NA, 9 , 5))

Si aplicas la función sum a cada elemento de la lista calculará la suma de los componentes de cada uno de sus elementos. En este caso, como el segundo elemento contiene un valor NA, su suma devuelve un NA.

sapply(mi_lista, sum)
 A  B 
11 NA

Ahora bien, dado que la función sum tiene un argumento adicional llamado na.rm, puedes establecerlo en TRUE como sigue para eliminar los valores NA:

sapply(mi_lista, sum, na.rm = TRUE)

En consecuencia, el valor NA no se tiene en cuenta y la función devuelve la suma de los valores finitos.

 A  B 
11 22

Cabe señalar que si la función que estás aplicando tiene más argumentos adicionales, puedes especificarlos de la misma manera, uno tras otro.

Devolviendo una matriz o un array

La salida de la función sapply en R también puede ser una matriz o un array. Por un lado, si la función que estás aplicando devuelve vectores de la misma longitud, la función sapply devolverá una matriz, donde las columnas son cada uno de los vectores. Por otro lado, si la función devuelve una matriz, la función sapply tratará, por defecto, las matrices como vectores, creando una nueva matriz, donde cada columna corresponde a los elementos de cada matriz.

Considera, como ejemplo, que quieres crear matrices de tres filas y tres columnas, donde todas las celdas contienen el mismo número. Para crear una, puedes escribir lo siguiente:

matrix(1, ncol = 3, nrow = 3)
     [,1] [,2] [,3]
[1,]    1    1    1
[2,]    1    1    1
[3,]    1    1    1

Sin embargo, si intentas usar la función sapply para iterar sobre una lista y crear más matrices, el resultado no será el esperado, debido a que, como señalamos, la función trata a cada matriz por defecto como vectores.

sapply(1:3, function(i) matrix(i, ncol = 3, nrow = 3))
     [,1] [,2] [,3]
 [1,]    1    2    3
 [2,]    1    2    3
 [3,]    1    2    3
 [4,]    1    2    3
 [5,]    1    2    3
 [6,]    1    2    3
 [7,]    1    2    3
 [8,]    1    2    3
 [9,]    1    2    3

Para resolver este problema, puedes establecer el argumento simplify como "array" y en consecuencia cada elemento de la matriz contendrá la matriz deseada:

sapply(1:3, function(i) matrix(i, ncol = 3, nrow = 3), simplify = "array")
, , 1

     [,1] [,2] [,3]
[1,]    1    1    1
[2,]    1    1    1
[3,]    1    1    1

, , 2

     [,1] [,2] [,3]
[1,]    2    2    2
[2,]    2    2    2
[3,]    2    2    2

, , 3

     [,1] [,2] [,3]
[1,]    3    3    3
[2,]    3    3    3
[3,]    3    3    3

Cabe mencionar que si estableces simplify como FALSE, puedes generar una lista, donde cada elemento contendrá la matriz correspondiente. Ten en cuenta que este es el comportamiento predeterminado de la función lapply.

sapply(1:3, function(i) matrix(i, ncol = 3, nrow = 3), simplify = FALSE)
[[1]]
     [,1] [,2] [,3]
[1,]    1    1    1
[2,]    1    1    1
[3,]    1    1    1

[[2]]
     [,1] [,2] [,3]
[1,]    2    2    2
[2,]    2    2    2
[3,]    2    2    2

[[3]]
     [,1] [,2] [,3]
[1,]    3    3    3
[2,]    3    3    3
[3,]    3    3    3

Funciones sapply anidadas

Puedes anidar múltiples funciones sapply en R. Supón que quieres iterar sobre las columnas y filas de un data frame y multiplicar cada elemento por dos. Para ese propósito, usando un bucle for podrías escribir:

df <- trees

res <- data.frame()

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

No obstante, utilizando la función sapply puedes evitar bucles. Escribe lo siguiente para lograr el mismo resultado:

sapply(1:ncol(df), function(i) {
       sapply(1:nrow(df), function(j) {
              df[j, i] * 2
        })
})

Este ejemplo es solo para fines educativos, ya que podrías lograr el mismo resultado con df * 2.

Ejemplo de la función sapply en R: creando gráficos

A veces, el número de líneas o gráficos que quieres dibujar depende de algo (como el número de variables de un data frame, por ejemplo). En este caso, debes iterar sobre alguna lista para mostrar el resultado final. Para este propósito, puedes usar un bucle for:

plot(rnorm(10), ylim = c(-6, 6))

nlines <- 5

for (i in 1:nlines) {
    lines(-i:i, col = i, lwd = 3)
}

Sin embargo, si quieres evitar el uso de bucles puedes usar la función sapply. Ten en cuenta que ya que estamos aplicando una función de gráficos, la función sapply devuelve NULL pero la función invisible evita mostrar el texto de salida.

plot(rnorm(10), ylim = c(-6, 6))

nlines <- 5

invisible(sapply(1:nlines, function(i) lines(-i:i, col = i, lwd = 3)))

Esto es muy útil cuando se crean funciones con clases S3 en paquetes de R para dibujar gráficas.

Creado gráficos en R con la función sapply