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()
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 matrizX
, para aplicarles una función. Son identificadas con números,1
son filas y2
son colummnas.FUN
: La función que aplicaremos a la matrizX
en su dimensiónMARGIN
.
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
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:
Hay que hacerlo de esta manera:
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:
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 aX
.
Si es una lista:
## $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
## $a
## [1] 5.5
##
## $beta
## [1] 4.535125
##
## $logico
## [1] 0.5
Si es un vector:
## [[1]]
## [1] 0
##
## [[2]]
## [1] 0.6931472
##
## [[3]]
## [1] 1.098612
##
## [[4]]
## [1] 1.386294
##
## [[5]]
## [1] 1.609438
##
## [[6]]
## [1] 1.791759