Capitulo 9 La familia apply

La familia de funciones apply es usada para aplicar una función a cada elemento de una estructura de datos. En particular, es usada para aplicar funciones en matrices, data frames, arrays y listas.

Con esta familia de funciones podemos automatizar tareas complejas usando pocas líneas de código y es una de las características distintivas de R como lenguaje de programación.

La familia de funciones apply es una expresión de los rasgos del paradigma funcional de programación presentes en R. Sobre esto no profundizaremos demasiado, pero se conviene saber que en R las funciones son “ciudadanos de primera”, con la misma importancia que los objetos, y por lo tanto, operamos en ellas.

La familia de funciones apply no sólo recibe datos como argumentos, también recibe funciones.

9.0.1 Recordatorio sobre vectorización

Para entender más fácilmente el uso de la familia apply, recordemos la vectorización de operaciones.

Hay operaciones que, si las aplicamos a un vector, son aplicadas a todos sus elementos.

mi_vector <- 1:10
mi_vector
##  [1]  1  2  3  4  5  6  7  8  9 10
mi_vector*3
##  [1]  3  6  9 12 15 18 21 24 27 30

Lo anterior es, generalmente, preferible a escribir una operación para cada elemento o a usar un bucle for, como vimos en el capítulo sobre estructuras de control.

Como todo lo que ocurre en R es una función, podemos decir que al vectorizar estamos aplicando una función a cada elemento de un vector. La familia de funciones apply nos permite implementar esto en estructuras de datos distintas a los vectores.

9.1 Las funciones de la familia apply

La familia apply tiene todas estas funciones:

  • apply()
  • eapply()
  • lapply()
  • mapply()
  • rapply()
  • sapply()
  • tapply()
  • vapply()
  • etc.

Es una familia numerosa y esta variedad de funciones se debe a que varias de ellas tienen aplicaciones sumamente específicas. Todas las funciones de esta familia tienen una característica en común: reciben como argumentos a un objeto y al menos una función.

Hasta ahora, todas las funciones que hemos usado han recibido como argumentos estructuras de datos, sean vectores, data.frames o de otro tipo. Las funciones de la familia apply tienen la particularidad que pueden recibir a otra función como un argumento. Lo anterior puede sonar confuso, pero es más bien intuitivo al verlo implementado.

Nosotros trabajaremos con las funciones más general y de uso común de esta familia: apply() y lapply()

Esta función nos permitirá solucionar casi todos los problemas con los que nos encontremos. Además, conociendo su uso, las demás funciones de la familia apply serán relativamente fáciles de entender.

9.2 apply()

apply() aplica una función a todos los elementos de una matriz o data.frame o tibble.

La estructura de esta función es la siguiente:

apply(X, MARGIN, FUN)

apply() tiene tres argumentos:

  • X: Una matriz o un objeto que pueda coercionarse a una matriz, e.g., data.frame o tibble.
  • MARGIN: La dimensión (margen) que agrupará los elementos de la matriz X, para aplicarles una función. Son identificadas con números, 1 son filas y 2 son colummnas.
  • FUN: La función que aplicaremos a la matriz X en su dimensión MARGIN.

Por ejemplo, vamos a calcular las medias de cada fila en nuestra base de datos “data”:

data=data.frame(v1 = runif(100), v2 = rnorm(100))
head(data)
##          v1         v2
## 1 0.2443068  0.2721108
## 2 0.4368168  1.0453228
## 3 0.2178143  0.1692785
## 4 0.6221501 -0.9944350
## 5 0.4811240 -0.4153374
## 6 0.7692669  1.0936361
apply(X = data, MARGIN = 1, FUN = mean)
##   [1]  0.258208826  0.741069794  0.193546438 -0.186142420  0.032893326  0.931451523  0.516408823  0.551857843  0.910836640  0.652905750  0.758555419 -0.409241672
##  [13]  0.172068324  0.233657959  0.228851520  0.994860575  0.383993447 -0.246017923 -0.087285244 -0.237212431  0.268211459  0.212746356  0.892696950 -0.117459929
##  [25]  0.748382872  0.487705375  0.001571842  1.141796362  0.359838336 -0.158211097  0.499928233 -0.277173485  0.514776214 -0.044434084  1.019900756 -0.753017346
##  [37] -0.199776248 -0.010009894 -0.124749692  1.139671207  0.231060302  0.342511708  0.725066906  0.472253514  0.725271765  0.587751180  0.532663484 -0.575973421
##  [49] -0.425031165 -0.615828747  0.180593488  0.911748624 -0.250171182  0.178950893  0.398000014  0.333853428  0.142930778  0.004982485 -0.208513288  0.431706395
##  [61]  0.755396775 -0.016240417  0.396338702  0.193207475  0.490238026  0.375902061  0.630817805 -0.271446776  0.442412665  0.197342202 -0.343418118 -1.140250383
##  [73]  0.143264516  0.445888189  0.237706966  0.178414281  0.387078274  0.209893223 -0.082884287  0.420332230  0.244019865 -0.282975717  0.397573980  0.208456791
##  [85]  0.528117040  0.721521575  0.305431464  0.420995727 -0.007116461  0.408057628 -0.363491973  0.550265829 -0.188696905  1.343225139 -0.055192945 -1.430211709
##  [97]  0.710876914 -0.258716406  0.384693519  0.494691698

O por variables

apply(X = data, MARGIN = 2, FUN = mean)
##           v1           v2 
##  0.480766822 -0.002706695

Hay veces que una función tiene varios argumentos. Por ejemplo la función quantile, que sirve para calcular quantiles de nuestros datos. Para especificar estos no vale hacer esto:

apply(X = data, MARGIN = 2, FUN = quantile(probs = c(.9)))

Hay que hacerlo de esta manera:

apply(X = data, MARGIN = 2, FUN = quantile, probs = .9)
##        v1        v2 
## 0.8470737 1.0501541

O si queremos calcular varios quantiles:

apply(X = data, MARGIN = 2, FUN = quantile, probs = c(.1,.9))
##            v1        v2
## 10% 0.1561958 -1.004556
## 90% 0.8470737  1.050154

Muchas otras veces querremos aplicar funciones hechas por nosotros. En otro capítulo vamos a aprender a hacer funciones básicas. Pero he aquí un ejemplo:

Queremos ver la media redondeada a dos dígitos de las columnas. Para ello:

mi_func <- function(x) { round( mean (x) , digits=2) }
apply(X = data, MARGIN = 2, FUN = mi_func)
##   v1   v2 
## 0.48 0.00

9.3 lapply()

lapply() se aplica sobre listas o vectores y retorna una lista. La función tiene dos argumentos:

  • X: Una lista o vector.
  • FUN: La función que aplicaremos a X.

Si es una lista:

x <- list(a = 1:10, beta = exp(-3:3), logico = c(TRUE,FALSE,FALSE,TRUE))
x
## $a
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## $beta
## [1]  0.04978707  0.13533528  0.36787944  1.00000000  2.71828183  7.38905610 20.08553692
## 
## $logico
## [1]  TRUE FALSE FALSE  TRUE
lapply(x, mean)
## $a
## [1] 5.5
## 
## $beta
## [1] 4.535125
## 
## $logico
## [1] 0.5

Si es un vector:

x <- 1:6
lapply(x, log)
## [[1]]
## [1] 0
## 
## [[2]]
## [1] 0.6931472
## 
## [[3]]
## [1] 1.098612
## 
## [[4]]
## [1] 1.386294
## 
## [[5]]
## [1] 1.609438
## 
## [[6]]
## [1] 1.791759