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.