R Language
* aplicar familia de funciones (funcionales)
Buscar..
Observaciones
Una función en la familia *apply
es una abstracción de un bucle for
. En comparación con el for
bucles *apply
las funciones tienen las siguientes ventajas:
- Requieren menos código para escribir.
- No tiene un contador de iteración.
- No utiliza variables temporales para almacenar resultados intermedios.
Sin embargo for
bucles son más generales y nos puede dar más control que permite lograr cálculos complejos que no siempre son triviales para hacer uso de *apply
funciones.
La relación entre los bucles for
y las funciones *apply
se explica en la documentación for
bucles for
.
Miembros de la familia *apply
La familia de funciones *apply
contiene varias variantes del mismo principio que difieren principalmente en función del tipo de resultado que devuelven.
función | Entrada | Salida |
---|---|---|
apply | matrix , data.frame o array | Vector o matriz (dependiendo de la longitud de cada elemento devuelto) |
sapply | vector o list | Vector o matriz (dependiendo de la longitud de cada elemento devuelto) |
lapply | vector o list | list |
vapply | vector o `lista | Vector o matriz (dependiendo de la longitud de cada elemento devuelto) de la clase designada por el usuario |
mapply | Múltiples vectores, lists o una combinación. | list |
Consulte "Ejemplos" para ver cómo se utiliza cada una de estas funciones.
Utilizar funciones anónimas con aplicar.
apply
se utiliza para evaluar una función (tal vez anónima) sobre los márgenes de una matriz o matriz.
Usemos el conjunto de datos del iris
para ilustrar esta idea. El conjunto de datos del iris
tiene medidas de 150 flores de 3 especies. Veamos cómo está estructurado este conjunto de datos:
> head(iris)
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
Ahora, imagina que quieres saber la media de cada una de estas variables. Una forma de resolver esto podría ser usar un bucle for
, pero los programadores de R a menudo preferirán usar apply
(por los motivos, ver Comentarios):
> apply(iris[1:4], 2, mean)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
- En el primer parámetro, subcompusimos el
iris
para incluir solo las primeras 4 columnas, porque lamean
solo funciona en datos numéricos. - El segundo valor de parámetro de
2
indica que queremos trabajar solo en las columnas (el segundo subíndice de la matriz r × c);1
daría a los medios de fila.
De la misma manera podemos calcular valores más significativos:
# standard deviation
apply(iris[1:4], 2, sd)
# variance
apply(iris[1:4], 2, var)
Advertencia : R tiene algunas funciones incorporadas que son mejores para calcular sumas y medios de columnas y filas: colMeans
y rowMeans
.
Ahora, hagamos una tarea diferente y más significativa: calculemos la media solo para aquellos valores que son mayores que 0.5
. Para eso, crearemos nuestra propia función mean
.
> our.mean.function <- function(x) { mean(x[x > 0.5]) }
> apply(iris[1:4], 2, our.mean.function)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.665347
(Note la diferencia en la media de Petal.Width
)
Pero, ¿qué pasa si no queremos usar esta función en el resto de nuestro código? Luego, podemos usar una función anónima y escribir nuestro código así:
apply(iris[1:4], 2, function(x) { mean(x[x > 0.5]) })
Entonces, como hemos visto, podemos usar apply
para ejecutar la misma operación en columnas o filas de un conjunto de datos usando solo una línea.
Advertencia : dado que la apply
devuelve tipos de salida muy diferentes dependiendo de la longitud de los resultados de la función especificada, puede que no sea la mejor opción en los casos en que no esté trabajando de forma interactiva. Algunas de las otras *apply
funciones de la familia de *apply
son un poco más predecibles (ver Comentarios).
Carga masiva de archivos
para una gran cantidad de archivos que pueden necesitar ser operados en un proceso similar y con nombres de archivos bien estructurados.
En primer lugar, se debe crear un vector de los nombres de archivo a los que se va a acceder, hay varias opciones para esto:
Creando el vector manualmente con
paste0()
files <- paste0("file_", 1:100, ".rds")
El uso de
list.files()
con un término de búsqueda regex para el tipo de archivo, requiere conocimiento de expresiones regulares ( regex ) si hay otros archivos del mismo tipo en el directorio.files <- list.files("./", pattern = "\\.rds$", full.names = TRUE)
donde X
es un vector de parte del formato de denominación de archivos utilizado.
lapply
dará salida a cada respuesta como elemento de una lista.
readRDS
es específico de los archivos .rds
y cambiará según la aplicación del proceso.
my_file_list <- lapply(files, readRDS)
Esto no es necesariamente más rápido que un bucle for de las pruebas, pero permite que todos los archivos sean un elemento de una lista sin asignarlos explícitamente.
Finalmente, a menudo necesitamos cargar varios paquetes a la vez. Este truco puede hacerlo fácilmente aplicando library()
a todas las bibliotecas que deseamos importar:
lapply(c("jsonlite","stringr","igraph"),library,character.only=TRUE)
Combinando múltiples `data.frames` (` lapply`, `mapply`)
En este ejercicio, generaremos cuatro modelos de regresión lineal bootstrap y combinaremos los resúmenes de estos modelos en un solo marco de datos.
library(broom)
#* Create the bootstrap data sets
BootData <- lapply(1:4,
function(i) mtcars[sample(1:nrow(mtcars),
size = nrow(mtcars),
replace = TRUE), ])
#* Fit the models
Models <- lapply(BootData,
function(BD) lm(mpg ~ qsec + wt + factor(am),
data = BD))
#* Tidy the output into a data.frame
Tidied <- lapply(Models,
tidy)
#* Give each element in the Tidied list a name
Tidied <- setNames(Tidied, paste0("Boot", seq_along(Tidied)))
En este punto, podemos adoptar dos enfoques para insertar los nombres en el data.frame.
#* Insert the element name into the summary with `lapply`
#* Requires passing the names attribute to `lapply` and referencing `Tidied` within
#* the applied function.
Described_lapply <-
lapply(names(Tidied),
function(nm) cbind(nm, Tidied[[nm]]))
Combined_lapply <- do.call("rbind", Described_lapply)
#* Insert the element name into the summary with `mapply`
#* Allows us to pass the names and the elements as separate arguments.
Described_mapply <-
mapply(
function(nm, dframe) cbind(nm, dframe),
names(Tidied),
Tidied,
SIMPLIFY = FALSE)
Combined_mapply <- do.call("rbind", Described_mapply)
Si es un fanático de las magrittr
estilo magrittr
, puede realizar la tarea completa en una sola cadena (aunque puede que no sea prudente hacerlo si necesita alguno de los objetos intermedios, como los objetos del modelo en sí):
library(magrittr)
library(broom)
Combined <- lapply(1:4,
function(i) mtcars[sample(1:nrow(mtcars),
size = nrow(mtcars),
replace = TRUE), ]) %>%
lapply(function(BD) lm( mpg ~ qsec + wt + factor(am), data = BD)) %>%
lapply(tidy) %>%
setNames(paste0("Boot", seq_along(.))) %>%
mapply(function(nm, dframe) cbind(nm, dframe),
nm = names(.),
dframe = .,
SIMPLIFY = FALSE) %>%
do.call("rbind", .)
Usando funciones incorporadas
Funcionales incorporados: lapply (), sapply () y mapply ()
R viene con funciones integradas, de las cuales quizás las más conocidas son la familia de funciones de aplicación. Aquí hay una descripción de algunas de las funciones de aplicación más comunes:
-
lapply()
= toma una lista como argumento y aplica la función especificada a la lista. -
sapply()
= igual quelapply()
pero intenta simplificar la salida a un vector o una matriz.-
vapply()
= una variante desapply()
en la que se debe especificar el tipo de objeto de salida.
-
-
mapply()
= comolapply()
pero puede pasar múltiples vectores como entrada a la función especificada. Se puede simplificar comosapply()
.-
Map()
es un alias paramapply()
conSIMPLIFY = FALSE
.
-
lapturar ()
lapply()
se puede utilizar con dos iteraciones diferentes:
-
lapply(variable, FUN)
-
lapply(seq_along(variable), FUN)
# Two ways of finding the mean of x
set.seed(1)
df <- data.frame(x = rnorm(25), y = rnorm(25))
lapply(df, mean)
lapply(seq_along(df), function(x) mean(df[[x]))
suministro ()
sapply()
intentará resolver su salida a un vector o una matriz.
# Two examples to show the different outputs of sapply()
sapply(letters, print) ## produces a vector
x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE))
sapply(x, quantile) ## produces a matrix
mapply ()
mapply()
funciona de manera muy similar a lapply()
excepto que puede tomar múltiples vectores como entrada (de ahí la m para multivariable).
mapply(sum, 1:5, 10:6, 3) # 3 will be "recycled" by mapply
Usando funciones definidas por el usuario
Funcionales definidos por el usuario
Los usuarios pueden crear sus propios funcionales en diversos grados de complejidad. Los siguientes ejemplos son de Functionals by Hadley Wickham:
randomise <- function(f) f(runif(1e3))
lapply2 <- function(x, f, ...) {
out <- vector("list", length(x))
for (i in seq_along(x)) {
out[[i]] <- f(x[[i]], ...)
}
out
}
En el primer caso, randomise
acepta un solo argumento f
, y lo llama en una muestra de variables aleatorias uniformes. Para demostrar la equivalencia, llamamos set.seed
continuación:
set.seed(123)
randomise(mean)
#[1] 0.4972778
set.seed(123)
mean(runif(1e3))
#[1] 0.4972778
set.seed(123)
randomise(max)
#[1] 0.9994045
set.seed(123)
max(runif(1e3))
#[1] 0.9994045
El segundo ejemplo es una reimplementación de base::lapply
, que utiliza funciones para aplicar una operación ( f
) a cada elemento de una lista ( x
). El parámetro ...
permite al usuario pasar argumentos adicionales a f
, como la opción na.rm
en la función mean
:
lapply(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
#
# [[2]]
# [1] NA
lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
#
# [[2]]
# [1] NA
lapply(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
#
# [[2]]
# [1] 4
lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
#
# [[2]]
# [1] 4