Funciones de agrupación (tapply, by, agregado) y la familia *apply
Siempre que quiero hacer algo "mapear" py en R, normalmente intento usar una función de la apply
familia.
Sin embargo, nunca he entendido bien las diferencias entre ellos: cómo { sapply
, lapply
, etc.} aplica la función a la entrada/entrada agrupada, cómo se verá la salida o incluso cuál puede ser la entrada, por lo que a menudo simplemente revíselos todos hasta que obtenga lo que quiero.
¿Alguien puede explicar cómo usar cuál y cuándo?
Mi comprensión actual (probablemente incorrecta/incompleta) es...
sapply(vec, f)
: la entrada es un vector. la salida es un vector/matriz, donde el elementoi
esf(vec[i])
, lo que le proporciona una matriz sif
tiene una salida de múltiples elementoslapply(vec, f)
: ¿igual quesapply
, pero la salida es una lista?apply(matrix, 1/2, f)
: la entrada es una matriz. la salida es un vector, donde el elementoi
es f(fila/col i de la matriz)tapply(vector, grouping, f)
: la salida es una matriz/matriz, donde un elemento en la matriz/matriz es el valor def
una agrupacióng
del vector, yg
se envía a los nombres de fila/colby(dataframe, grouping, f)
: seag
una agrupación. se aplicanf
a cada columna del grupo/marco de datos. Imprima bastante la agrupación y el valor def
en cada columna.aggregate(matrix, grouping, f)
: similar aby
, pero en lugar de imprimir la salida de forma bonita, el agregado coloca todo en un marco de datos.
Pregunta paralela: todavía no he aprendido a plyr o remodelar. ¿ Los plyr
reemplazaría reshape
todos por completo?
R tiene muchas funciones *apply que se describen hábilmente en los archivos de ayuda (por ejemplo ?apply
). Sin embargo, hay suficientes como para que los usuarios principiantes puedan tener dificultades para decidir cuál es apropiado para su situación o incluso recordarlos todos. Es posible que tengan una sensación general de que "debería usar una función *apply aquí", pero puede ser difícil mantenerlos claros al principio.
A pesar del hecho (observado en otras respuestas) de que gran parte de la funcionalidad de la familia *apply está cubierta por el plyr
paquete extremadamente popular, las funciones básicas siguen siendo útiles y vale la pena conocerlas.
Esta respuesta pretende actuar como una especie de señal para que los nuevos usuarios los ayuden a dirigirlos a la función *apply correcta para su problema particular. Tenga en cuenta que esto no pretende simplemente regurgitar o reemplazar la documentación de R. La esperanza es que esta respuesta le ayude a decidir qué función *apply se adapta a su situación y luego le corresponde a usted investigarla más a fondo. Con una excepción, no se abordarán las diferencias de rendimiento.
aplicar : cuando desea aplicar una función a las filas o columnas de una matriz (y análogos de dimensiones superiores); Generalmente no es aconsejable para marcos de datos, ya que primero obligará a una matriz.
# Two dimensional matrix M <- matrix(seq(1,16), 4, 4) # apply min to rows apply(M, 1, min) [1] 1 2 3 4 # apply max to columns apply(M, 2, max) [1] 4 8 12 16 # 3 dimensional array M <- array( seq(32), dim = c(4,4,2)) # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimension apply(M, 1, sum) # Result is one-dimensional [1] 120 128 136 144 # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimension apply(M, c(1,2), sum) # Result is two-dimensional [,1] [,2] [,3] [,4] [1,] 18 26 34 42 [2,] 20 28 36 44 [3,] 22 30 38 46 [4,] 24 32 40 48
Si desea medias o sumas de filas/columnas para una matriz 2D, asegúrese de investigar el altamente optimizado y ultrarrápido
colMeans
,rowMeans
,colSums
.rowSums
lapply : cuando desea aplicar una función a cada elemento de una lista por turno y obtener una lista de vuelta.
Este es el caballo de batalla de muchas de las otras funciones de *apply. Retire su código y, a menudo, lo encontrará
lapply
debajo.x <- list(a = 1, b = 1:3, c = 10:100) lapply(x, FUN = length) $a [1] 1 $b [1] 3 $c [1] 91 lapply(x, FUN = sum) $a [1] 1 $b [1] 6 $c [1] 5005
sapply : cuando desea aplicar una función a cada elemento de una lista por turno, pero desea obtener un vector , en lugar de una lista.
Si se encuentra escribiendo
unlist(lapply(...))
, deténgase y consideresapply
.x <- list(a = 1, b = 1:3, c = 10:100) # Compare with above; a named vector, not a list sapply(x, FUN = length) a b c 1 3 91 sapply(x, FUN = sum) a b c 1 6 5005
En usos más avanzados,
sapply
intentará forzar el resultado a una matriz multidimensional, si corresponde. Por ejemplo, si nuestra función devuelve vectores de la misma longitud,sapply
los usaremos como columnas de una matriz:sapply(1:5,function(x) rnorm(3,x))
Si nuestra función devuelve una matriz bidimensional,
sapply
hará esencialmente lo mismo, tratando cada matriz devuelta como un único vector largo:sapply(1:5,function(x) matrix(x,2,2))
A menos que especifiquemos
simplify = "array"
, en cuyo caso utilizará las matrices individuales para construir una matriz multidimensional:sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
Por supuesto, cada uno de estos comportamientos depende de que nuestra función devuelva vectores o matrices de la misma longitud o dimensión.
vapply : cuando desea utilizar
sapply
pero tal vez necesite exprimir un poco más la velocidad de su código o desea más seguridad de tipos .Básicamente
vapply
, le das a R un ejemplo de qué tipo de cosas devolverá tu función, lo que puede ahorrar algo de tiempo al forzar los valores devueltos para que quepan en un único vector atómico.x <- list(a = 1, b = 1:3, c = 10:100) #Note that since the advantage here is mainly speed, this # example is only for illustration. We're telling R that # everything returned by length() should be an integer of # length 1. vapply(x, FUN = length, FUN.VALUE = 0L) a b c 1 3 91
mapply : para cuando tienes varias estructuras de datos (por ejemplo, vectores, listas) y deseas aplicar una función al primer elemento de cada una, y luego al segundo elemento de cada una, etc., coaccionando el resultado a un vector/matriz como en
sapply
.Esto es multivariado en el sentido de que su función debe aceptar múltiples argumentos.
#Sums the 1st elements, the 2nd elements, etc. mapply(sum, 1:5, 1:5, 1:5) [1] 3 6 9 12 15 #To do rep(1,4), rep(2,3), etc. mapply(rep, 1:4, 4:1) [[1]] [1] 1 1 1 1 [[2]] [1] 2 2 2 [[3]] [1] 3 3 [[4]] [1] 4
Mapa : un contenedor para
mapply
withSIMPLIFY = FALSE
, por lo que se garantiza que devolverá una lista.Map(sum, 1:5, 1:5, 1:5) [[1]] [1] 3 [[2]] [1] 6 [[3]] [1] 9 [[4]] [1] 12 [[5]] [1] 15
rapply : para cuando desea aplicar una función a cada elemento de una estructura de lista anidada , de forma recursiva.
Para darle una idea de lo poco común
rapply
que es, ¡lo olvidé cuando publiqué esta respuesta por primera vez! Obviamente, estoy seguro de que mucha gente lo usa, pero YMMV.rapply
se ilustra mejor con una función definida por el usuario para aplicar:# Append ! to string, otherwise increment myFun <- function(x){ if(is.character(x)){ return(paste(x,"!",sep="")) } else{ return(x + 1) } } #A nested list structure l <- list(a = list(a1 = "Boo", b1 = 2, c1 = "Eeek"), b = 3, c = "Yikes", d = list(a2 = 1, b2 = list(a3 = "Hey", b3 = 5))) # Result is named vector, coerced to character rapply(l, myFun) # Result is a nested list like l, with values altered rapply(l, myFun, how="replace")
tapply : para cuando desea aplicar una función a subconjuntos de un vector y los subconjuntos están definidos por algún otro vector, generalmente un factor.
La oveja negra de la familia *apply, más o menos. El uso de la frase "matriz irregular" en el archivo de ayuda puede resultar un poco confuso , pero en realidad es bastante simple.
Un vector:
x <- 1:20
Un factor (¡de la misma longitud!) que define grupos:
y <- factor(rep(letters[1:5], each = 4))
Sume los valores dentro
x
de cada subgrupo definido pory
:tapply(x, y, sum) a b c d e 10 26 42 58 74
Se pueden manejar ejemplos más complejos donde los subgrupos están definidos por combinaciones únicas de una lista de varios factores.
tapply
es similar en espíritu a las funciones dividir-aplicar-combinar que son comunes en R (aggregate
,by
,ave
,ddply
etc.) De ahí su estatus de oveja negra.
En una nota al margen, así es como las distintas plyr
funciones corresponden a las *apply
funciones base (de la introducción al documento de plyr de la página web de plyr http://had.co.nz/plyr/ )
Base function Input Output plyr function
---------------------------------------
aggregate d d ddply + colwise
apply a a/l aaply / alply
by d l dlply
lapply l l llply
mapply a a/l maply / mlply
replicate r a/l raply / rlply
sapply l a laply
Uno de los objetivos plyr
es proporcionar convenciones de nomenclatura coherentes para cada una de las funciones, codificando los tipos de datos de entrada y salida en el nombre de la función. También proporciona coherencia en la producción, en el sentido de que la salida dlply()
es fácilmente transitable para ldply()
producir resultados útiles, etc.
Conceptualmente, aprender plyr
no es más difícil que comprender las *apply
funciones básicas.
plyr
y reshape
las funciones han reemplazado casi todas estas funciones en mi uso diario. Pero, también del documento Introducción a Plyr:
Las funciones relacionadas
tapply
ysweep
no tienen función correspondiente enplyr
y siguen siendo útiles.merge
Es útil para combinar resúmenes con los datos originales.
De la diapositiva 21 de http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :
(Con suerte, queda claro que apply
corresponde a @Hadley aaply
y aggregate
corresponde a @Hadley, ddply
etc. La diapositiva 20 del mismo slideshare aclarará si no la obtienes de esta imagen).
(a la izquierda está la entrada, arriba está la salida)