5.1 For loop y familia Apply

La familia de funciones apply pertenece a la librería base en R y facilitan la manipulación de datos de forma repetitiva.

Las funciones de esta familia son: apply(), lapply(), sapply(), vapply(), mapply(), rapply(), and tapply().

La estructura de los datos de entrada y el formato del resultado o salida determinarán cual función usar.

En este taller solo se verán las primeras tres funciones.


5.1.1 apply()

Esta es la función que manipula arreglos homogéneos, en particular, se revisa el caso de matrices que son arreglos de dos dimensiones.

La función tiene los siguientes argumentos: apply(X, MARGIN, FUN, ...)

  • X representa el arreglo de dos dimensiones.
  • MARGIN representa la dimensión sobre la que se va a resumir la información. Donde 1 = renglon o primera dimensión y 2 = columna o segunda dimensión.
  • FUN representa la función que resume la información.

Tomemos la siguiente matriz de simulaciones

set.seed(1)
mat_norm <- matrix(rnorm(24, mean = 0, sd = 1), nrow = 4, ncol = 6)
mat_norm
##            [,1]       [,2]       [,3]        [,4]        [,5]        [,6]
## [1,] -0.6264538  0.3295078  0.5757814 -0.62124058 -0.01619026  0.91897737
## [2,]  0.1836433 -0.8204684 -0.3053884 -2.21469989  0.94383621  0.78213630
## [3,] -0.8356286  0.4874291  1.5117812  1.12493092  0.82122120  0.07456498
## [4,]  1.5952808  0.7383247  0.3898432 -0.04493361  0.59390132 -1.98935170


Deseamos obtener la suma de cada columna de la matriz.


El primer método, quizá el mas intuitivo en este momento, es obtener cada elemento o columna, aplicar la función a cada elemento y concatenar:

prom_col_m1 <- c(sum(mat_norm[, 1]), 
                 sum(mat_norm[, 2]), 
                 sum(mat_norm[, 3]), 
                 sum(mat_norm[, 4]),
                 sum(mat_norm[, 5]),
                 sum(mat_norm[, 6]))
prom_col_m1
## [1]  0.3168417  0.7347931  2.1720174 -1.7559432  2.3427685 -0.2136730

Segundo método

prom_col_m2 <- vector( length = ncol(mat_norm))
for(j in 1:ncol(mat_norm)){
  prom_col_m2[j] <- sum(mat_norm[, j])
}
prom_col_m2
## [1]  0.3168417  0.7347931  2.1720174 -1.7559432  2.3427685 -0.2136730

Tercer método

prom_col_m3 <- apply(X = mat_norm, MARGIN = 2, FUN = sum)
prom_col_m3
## [1]  0.3168417  0.7347931  2.1720174 -1.7559432  2.3427685 -0.2136730

Cuarto método

prom_col_m4 <- colSums(mat_norm)
prom_col_m4
## [1]  0.3168417  0.7347931  2.1720174 -1.7559432  2.3427685 -0.2136730


Ahora, para obtener la suma por renglón usando el tercer método de la función apply(), únicamente es necesario cambiar la dimensión sobre la que voy a resumir con el argumento MARGIN = 1.

prom_row_m3 <- apply(mat_norm, 1, sum)
prom_row_m3
## [1]  0.5603818 -1.4309408  3.1842987  1.2830648

Que es equivalente al primer método que usamos.

prom_row_m1 <- c(sum(mat_norm[1, ]), 
                 sum(mat_norm[2, ]), 
                 sum(mat_norm[3, ]), 
                 sum(mat_norm[4, ]))
prom_row_m1
## [1]  0.5603818 -1.4309408  3.1842987  1.2830648

La ventaja de usar la función apply() es que se puede usar cualquier función. Por ejemplo, obtener la desviación estándar.

apply(mat_norm, 1, sd)
## [1] 0.6341809 1.1718660 0.8338847 1.2066403

O bien, una crear una función propia (definida por el usuario) con la función function()

cv_vec_m3 <- apply(mat_norm, 1, function(reng){
  cv <- mean(reng)/sd(reng)
  return(cv)
})
cv_vec_m3
## [1]  0.1472718 -0.2035131  0.6364386  0.1772228

Funciones Anónimas:

A este tipo de funciones se les llama funciones anónimas porque no se nombran ni guardan en el ambiente de R y únicamente funcionan dentro del comando que las llama.


5.1.2 lapply()

La función lapply() aplica una función sobre una lista o un vector y regresa el resultado en otra lista.

Usando el vector de ciudades,

ciudades_vec <- c("Aguascalientes", "Monterrey", "Guadalajara", "México")
ciudades_vec
## [1] "Aguascalientes" "Monterrey"      "Guadalajara"    "México"
res_nchar_l <- lapply(ciudades_vec, nchar)
res_nchar_l
## [[1]]
## [1] 14
## 
## [[2]]
## [1] 9
## 
## [[3]]
## [1] 11
## 
## [[4]]
## [1] 6
Esta función permite implementar funciones que regresen objetos de diferentes tipos, porque la listas permiten almacenar contenido heterogéneo.


La función lapply() permite incluir argumentos de las funciones que implementa. Estos argumentos se incluyen dentro de lapply() después de la función a implementar.

Por ejemplo, usamos la función potencia que se creó antes.

potencia_fun <- function(base, exponente){
  base^exponente
}

El objetivo es aplicar a cada elemento de la siguiente lista la función potencia y elevarlo al cubo.

nums_lista <- list(1, 3, 4)
nums_lista
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 3
## 
## [[3]]
## [1] 4

En la función lapply() se agrega el argumento exponente = 3 como último argumento.

potencia_lista <- lapply(nums_lista, potencia_fun, exponente = 3)
potencia_lista
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 27
## 
## [[3]]
## [1] 64

Una forma de reducir la lista obtenida a un vector es con la función unlist() que vimos antes.

unlist(potencia_lista)
## [1]  1 27 64



5.1.3 sapply()

La función sapply() es muy similar a lapply(). La única diferencia es la s que surge de simplified apply.

Al igual que lapply() aplica una función sobre una lista o un vector pero simplifica el resultado en un arreglo.

res_nchar_s <- sapply(ciudades_vec, nchar)
res_nchar_s
## Aguascalientes      Monterrey    Guadalajara         México 
##             14              9             11              6
Esta función es peligrosa ya que únicamente simplifica la estructura del resultado cuando es posible, de lo contrario, regresará una lista igual que lapply().