5.4 Familia apply()

5.4.1 Función apply()

La familia de funciones apply() reúne a varias funciones que permiten, como se implica de su nombre, aplicar algo a un objeto. Ese algo es una función.

Ejemplo 5.21 Para explorar los beneficios de este grupo de funciones, comencemos con un problema básico: calcular el promedio de varias columnas numéricas de una data frame.

### Activar la data frame de ejemplo llamada iris
data(iris)

### Ver las primeras 10 filas de la tabla
head(iris, 10)
#    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1           5.1         3.5          1.4         0.2  setosa
# 2           4.9         3.0          1.4         0.2  setosa
# 3           4.7         3.2          1.3         0.2  setosa
# 4           4.6         3.1          1.5         0.2  setosa
# 5           5.0         3.6          1.4         0.2  setosa
# 6           5.4         3.9          1.7         0.4  setosa
# 7           4.6         3.4          1.4         0.3  setosa
# 8           5.0         3.4          1.5         0.2  setosa
# 9           4.4         2.9          1.4         0.2  setosa
# 10          4.9         3.1          1.5         0.1  setosa

Calcular el promedio de las primeras cuatro columnas (las únicas numéricas), se puede hacer manualmente:

mean(iris$Sepal.Length)
# [1] 5.843
mean(iris$Sepal.Width)
# [1] 3.057
mean(iris$Petal.Length)
# [1] 3.758
mean(iris$Petal.Width)
# [1] 1.199

O con apply(). Esta función necesita que especifiquemos como argumentos: una base de datos tabular (DF), un modo de aplicación de la función (fila por fila: MARGIN = 1, columna por columna: MARGIN = 2), y la función (sin paréntesis) a ser aplicada. El resultado obtenido es un vector, cuyos elementos reciben el nombre de la columna de la cual proceden. En consecuencia, la estructura básica es:

### Modo largo
apply(DF, MARGIN = 2, FUN = FUNCIÓN)

### Modo corto
apply(DF, 2, FUNCIÓN)

Poniendo en contexto apply() para el ejemplo planteado:

### Seleccionando las 4 primeras columnas de iris
apply(iris[,1:4], 2, FUN = mean)
# Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
#        5.843        3.057        3.758        1.199

Dentro de la familia apply() podrás encontrar:

Tabla 5.1: Funciones de la familia apply() que se pueden utilizar en R para automatizar la aplicación de funciones sobre diferentes estructuras de datos.
Función Descripción
apply() Aplica una función a las columnas (MARGIN = 2) o filas (MARGIN = 1) de una base de datos en dos dimensiones (tablas, matrices, data frames). Produce un vector nombrado como resultado.
lapply() Aplicar una función sobre un vector o lista. Produce una lista como resultado.
sapply() Igual que lapply() pero produce un vector o matriz como resultado.
vapply() Igual que sapply() pero produce un resultado con estructura predefinida por el usuario.
tapply() Aplica una función a un vector separándolo primero por grupos en base a otro vector. Devuelve como resultado un array.
mapply() Es la versión “multivariada” de sapply(). Aplica una función a la vez sobre varios vectores. En estos evalúa sobre la función de manera agrupada los primeros elementos, segundos elementos, terceros elementos, y así para delante, hasta acabar con los elementos de todos los vectores.

5.4.2 Función lapply()

La función lapply() se aplica tanto a una base de datos tabular o sobre una lista. Si se aplica a una tabla (DF), lo hace siempre de manera columna a columa, lo mismo que hace apply() con el argumento MARGIN = 2. No obstante, el resultado es siempre una lista, no un vector. La estructura básica es:

lapply(DF,  FUN = FUNCIÓN)

Ejemplo 5.22 Aplicando lapply() al ejemplo propuesto anteriormente, se tiene:

lapply(iris[,1:4],  FUN = mean)
# $Sepal.Length
# [1] 5.843
# 
# $Sepal.Width
# [1] 3.057
# 
# $Petal.Length
# [1] 3.758
# 
# $Petal.Width
# [1] 1.199

Siempre que se necesite “deslistar” una lista y extraer los elementos como un único vector, se utiliza unlist():

lista <- lapply(iris[,1:4],  FUN = mean)
unlist(lista)
# Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
#        5.843        3.057        3.758        1.199

En este último ejemplo, vemos como el resultado converge con el de apply().

5.4.3 Función tapply()

Esta función es excepcional. Permite calcular el resultado de una función aplicada sobre un vector, pero muestra los resultados agrupados en base a las categorías de otro vector de misma longitud. El resultado obtenido es un vector. La estructura básica es:

tapply(VECTOR_NUMÉRICO, VECTOR_DE_AGRUPAMIENTO, FUN = FUNCIÓN)

Ejemplo 5.23 El siguiente código sirve para calcular el promedio por especie (grupo especificado en la columna Species), de la variable en la columna 1 de la base de datos iris:

tapply(iris[,1], iris$Species, FUN = mean)
#     setosa versicolor  virginica 
#      5.006      5.936      6.588

Recuerda que siempre que necesites puedes crear una función. Si unificamos el uso de lapply() para aplicar tapply() a cada elemento de columna de iris[,1:4], es necesario crear una función con tapply() dentro del cual se especifique el uso de la función mean():

lapply(iris[,1:4],  FUN = function(x) tapply(x, iris$Species, mean))
# $Sepal.Length
#     setosa versicolor  virginica 
#      5.006      5.936      6.588 
# 
# $Sepal.Width
#     setosa versicolor  virginica 
#      3.428      2.770      2.974 
# 
# $Petal.Length
#     setosa versicolor  virginica 
#      1.462      4.260      5.552 
# 
# $Petal.Width
#     setosa versicolor  virginica 
#      0.246      1.326      2.026

5.4.4 Función sapply()

Una forma de usar funciones elemento a elemento (element-wise) en un vector sin tener que vectorizar (ver Sección 5.3), y que otorgue como resultado un vector, se obtiene con sapply(). Esta se puede aplicar sobre vectores. La estructura básica es:

sapply(VECTOR, FUN = FUNCIÓN)

Ejemplo 5.24 Aplicado al caso de la Sección 5.3 Operadores de Función en el que se requería aplicar primo.logic() sobre un vector numérico llamado sec para la búsqueda de números primos, se tiene:

### Recreando sec para buscar los números primos en él
sec <- 1:100

### Aplicar sapply()
sapply(sec, FUN = primo.logic)
#   [1] FALSE  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE  TRUE
#  [14] FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE
#  [27] FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
#  [40] FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
#  [53]  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE
#  [66] FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
#  [79]  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
#  [92] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE