Los bucles de la familia apply

apply

La función apply() pertenece al paquete base R y aplica una función a una matriz, lista o vector que se le pase como parámetro.

La documentación (ejecuta ?apply) de la base R dice que se llama de la siguiente manera: apply(X, MARGIN, FUN, …)

donde:

  • Argumento 1: matriz, lista o vector
  • Argumento 2: 1 para operar sobre las columnas y 2 sobre las filas, c(1, 2) indica filas y columnas
  • Argumento 3: la función a aplicar según indique el argumento 2
  • …: argumentos opcionales a FUN.

Por ejemplo, si queremos saber la media de cada fila de una matriz de 100 filas y 10 columnas:

myMat <- matrix(sample(1:100, size = 1000, replace = TRUE), ncol = 10)
apply(X = myMat, MARGIN = 1, FUN = mean)
##   [1] 40.8 43.9 58.4 46.0 46.1 60.6 56.7 55.4 46.9 73.2 43.3 35.3 40.3 44.9
##  [15] 54.0 55.8 30.7 57.7 64.2 61.0 52.0 35.0 65.1 54.4 41.2 30.0 38.6 33.8
##  [29] 50.9 49.0 65.5 59.9 40.2 59.4 57.0 52.2 52.9 45.8 45.0 61.6 52.1 57.3
##  [43] 57.2 63.3 58.0 63.2 64.5 44.1 60.0 44.9 51.7 53.0 56.0 55.6 48.3 42.1
##  [57] 47.3 37.8 56.4 50.5 50.3 57.9 33.8 61.7 45.0 49.9 44.7 34.8 49.2 66.0
##  [71] 65.4 62.3 39.7 64.6 41.3 44.8 54.1 49.5 37.0 62.6 70.4 56.0 49.1 45.4
##  [85] 65.6 50.6 40.8 46.6 47.5 57.5 52.7 47.9 53.8 48.0 64.4 53.2 30.9 32.8
##  [99] 38.7 38.9

Si queremos saber la media de cada columna, la expresión anterior se convierte en:

apply(X = myMat, MARGIN = 2, FUN = mean)
##  [1] 48.86 53.92 47.65 50.39 49.33 45.90 49.49 53.11 54.44 54.25

lapply

Es usada cuando se desea aplicar una función determinada a cada elemento de una lista y obtener una lista como resultado. Al ejecutar ?lapply, la sintaxis se parece a la función apply().

La diferencia es que:

  1. Puede ser usado para otros objetos como dataframes, listas o vectores; y
  2. Devuelve una lista de la misma longitud que X, y cada elemento resulta de la aplicación FUN al elemento X correspondiente.

Para ver cómo funciona esto, tenenemos el siguiente ejemplo:

set.seed(12345)
(myList <- list(
  a = sample(1:100, size = 10),
  b = sample(1:100, size = 10),
  c = sample(1:100, size = 10),
  d = sample(1:100, size = 10),
  e = sample(1:100, size = 10)
  ))
## $a
##  [1] 73 87 75 86 44 16 31 48 67 91
## 
## $b
##  [1]  4 16 73  1 38 44 37 96 17 87
## 
## $c
##  [1] 46 33 95 69 62 38 66 51 21 45
## 
## $d
##  [1] 80  1 19 67 36 35 82 85 57 13
## 
## $e
##  [1] 79 43 91 76 25 31  6  5 94 57

Para obtener el promedio de cada elemento de la list:

lapply(myList, FUN = mean)
## $a
## [1] 61.8
## 
## $b
## [1] 41.3
## 
## $c
## [1] 52.6
## 
## $d
## [1] 47.5
## 
## $e
## [1] 50.7

sapply

La función sapply() es una versión modificada de la función lapply() que realiza la misma operación pero intenta simplificar la salida a la estructura de datos más elemental siempre que sea posible.

Por ejemplo digamos que se desea repetir la operación de calcular la media del ejemplo anterior:

sapply(myList, FUN = mean)
##    a    b    c    d    e 
## 61.8 41.3 52.6 47.5 50.7

Extraer el elemento “n” (en este caso el primer elemento) de cada elemento de una list. La función que se llama para hacer esto es ‘[[’.

sapply(myList, FUN = '[[', 1)
##  a  b  c  d  e 
## 73  4 46 80 79

tapply

La función tapply() permite aplicar una función tomando como elemento para iterar una variable existente, es decir realiza una operación (parámetro 3) respecto a un vector (parámetro 1) agrupada por los factores que se indiquen como argumento (parámetro 2).

Para este ejemplo se usa la base de datos iris para obtener fácilmente un resumen de las mediciones de Sepal.Length para cada Species con la función tapply() y la función summary()

data("iris")
tapply(iris$Sepal.Length, iris$Species, summary)
## $setosa
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   4.300   4.800   5.000   5.006   5.200   5.800 
## 
## $versicolor
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   4.900   5.600   5.900   5.936   6.300   7.000 
## 
## $virginica
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   4.900   6.225   6.500   6.588   6.900   7.900

mapply

La función mapply() significa apply “multivariante”, que aplica una función a múltiples listas o múltiples argumentos vectoriales.

Por ejemplo, para crear una matriz 4x4 se puede crear normalmente con la función matrix() y la función rep() repetidamente:

matrix(c(rep(1, 4), rep(2, 4), rep(3, 4), rep(4, 4)),4,4)
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    1    2    3    4
## [3,]    1    2    3    4
## [4,]    1    2    3    4

Ahora usando la función mapply() y la función rep() repetidamente:

mapply(rep, 1:4, 4)
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    1    2    3    4
## [3,]    1    2    3    4
## [4,]    1    2    3    4

Si tenemos tres matrices x, y y z para obtener la suma entre cada uno de los elementos de las matrices se puede usar la función mapply():

x <- matrix(1:9,3,3)
y <- matrix(11:19,3,3)
z <- matrix(21:29,3,3)
mapply(sum, x, y, z)
## [1] 33 36 39 42 45 48 51 54 57

vapply

Devuelve un vector con la longitud que tiene cada una de las listas introducidas como parámetro.

(x <- list(A = 1, B = 1:3, C = 1:7))
## $A
## [1] 1
## 
## $B
## [1] 1 2 3
## 
## $C
## [1] 1 2 3 4 5 6 7
vapply(x, FUN = length, FUN.VALUE = 0L)
## A B C 
## 1 3 7

rapply

rapply() se utiliza cuando queremos aplicar una función a cada elemento de un objeto con estructura de lista anidada (una lista dentro de una lista), de manera recursiva (Máxima Formación n.d.).

Funciones relacionadas con apply()

En ocasiones se utilizan funciones estructuradas de forma similar junto con el elemento de la familia apply(), a continuación se presenta una visión general de algunas de ellas.

sweep

La función sweep() se utiliza cuando se desea duplicar diferentes acciones sobre los elementos MARGIN que se han elegido (limitándose aquí al caso de la matriz).

Supongamos que tiene un conjunto de datos. Primero se encuentran las medias y la dispersión o desviación estándar por columna con la ayuda de la función apply(). Luego se centra los todos los datos con respecto a su centro (cada dato original menos la media). Finamente los datos centrados se utilizará para normalizar con respecto a su desviación estándar (dato original menos la media dividido para la desviación estándar del dato original) (Fanara 2016):

data <- matrix(c(seq(4, 7), seq(8, 11), seq(12, 15)), 4, 3)
# Calcular la media por columna con `apply()`
data.media <- apply(data, 2, mean)
# Encuentrar la desviación estándar con `apply()`
data.sd <- apply(data, 2, sd)
# Centrar los datos
(data.centrado <- sweep(data, 2, data.media, "-"))
##      [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,]  0.5  0.5  0.5
## [4,]  1.5  1.5  1.5
# Normalizar los datos
(data.norm <- sweep(data.centrado, 2, data.sd, "/"))
##            [,1]       [,2]       [,3]
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,]  0.3872983  0.3872983  0.3872983
## [4,]  1.1618950  1.1618950  1.1618950

Esta función sweep() espera los siguientes parámetros:

  • Un x: una matriz de entrada;
  • Un MARGIN, 2 para indicar las columnas;
  • Un STATS, resumen de estadísticas (aquí mean); y
  • FUN, una función a aplicar. En este caso se usa el operador aritmético “-” para la resta.

aggregate

Esta función está contenida en el paquete base stats y la utilizas así: aggregate(x, by, FUN, …, simplify = TRUE).

Funciona de manera similar a la función apply(): se especifica el objeto, la función y dice si desea simplificar, al igual que con la función sapply(). La diferencia clave es el uso de la cláusula by, que establece la variable o el campo de marco de datos mediante el cual queremos realizar la agregación.

Usando la base de datos iris se obtiene el promedio de las mediciones de Sepal.Length para cada Species:

data("iris")
aggregate(iris$Sepal.Length, by = iris["Species"], FUN = mean)
##      Species     x
## 1     setosa 5.006
## 2 versicolor 5.936
## 3  virginica 6.588

by

Similar a la función tapply(), pero by() funciona también cuando el tamaño de los 2 primeros argumentos son distintos, por ejemplo:

data("iris")
by(iris[1:4], iris$Species, summary)
## iris$Species: setosa
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100  
##  1st Qu.:4.800   1st Qu.:3.200   1st Qu.:1.400   1st Qu.:0.200  
##  Median :5.000   Median :3.400   Median :1.500   Median :0.200  
##  Mean   :5.006   Mean   :3.428   Mean   :1.462   Mean   :0.246  
##  3rd Qu.:5.200   3rd Qu.:3.675   3rd Qu.:1.575   3rd Qu.:0.300  
##  Max.   :5.800   Max.   :4.400   Max.   :1.900   Max.   :0.600  
## -------------------------------------------------------- 
## iris$Species: versicolor
##   Sepal.Length    Sepal.Width     Petal.Length   Petal.Width   
##  Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000  
##  1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200  
##  Median :5.900   Median :2.800   Median :4.35   Median :1.300  
##  Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326  
##  3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500  
##  Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800  
## -------------------------------------------------------- 
## iris$Species: virginica
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400  
##  1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800  
##  Median :6.500   Median :3.000   Median :5.550   Median :2.000  
##  Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026  
##  3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300  
##  Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500

Otras funciones

Bibliografía

Fanara, Carlo. 2016. “R tutorial on the Apply family of functions (article) - DataCamp.” https://www.datacamp.com/community/tutorials/r-tutorial-apply-family.

Máxima Formación, MF. n.d. “Cómo calcular resúmenes estadísticos por grupo rápidamente.” Accessed January 6, 2019. https://www.maximaformacion.es/blog-dat/como-calcular-resumenes-por-grupo-rapidamente/.

Avatar
Eduardo Guamán Daquilema
Ing. en Estadística Informática