Funciones de agrupación (tapply, by, agregado) y la familia *apply

Resuelto grautur asked hace 54 años • 12 respuestas

Siempre que quiero hacer algo "mapear" py en R, normalmente intento usar una función de la applyfamilia.

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...

  1. sapply(vec, f): la entrada es un vector. la salida es un vector/matriz, donde el elemento ies f(vec[i]), lo que le proporciona una matriz si ftiene una salida de múltiples elementos

  2. lapply(vec, f): ¿igual que sapply, pero la salida es una lista?

  3. apply(matrix, 1/2, f): la entrada es una matriz. la salida es un vector, donde el elemento ies f(fila/col i de la matriz)
  4. tapply(vector, grouping, f): la salida es una matriz/matriz, donde un elemento en la matriz/matriz es el valor de funa agrupación gdel vector, y gse envía a los nombres de fila/col
  5. by(dataframe, grouping, f): sea guna agrupación. se aplican fa cada columna del grupo/marco de datos. Imprima bastante la agrupación y el valor de fen cada columna.
  6. aggregate(matrix, grouping, f): similar a by, 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 plyrreemplazaría reshapetodos por completo?

grautur avatar Jan 01 '70 08:01 grautur
Aceptado

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 plyrpaquete 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á lapplydebajo.

     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 considere sapply.

     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, sapplyintentará forzar el resultado a una matriz multidimensional, si corresponde. Por ejemplo, si nuestra función devuelve vectores de la misma longitud, sapplylos usaremos como columnas de una matriz:

     sapply(1:5,function(x) rnorm(3,x))
    

    Si nuestra función devuelve una matriz bidimensional, sapplyhará 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 sapplypero 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 mapplywith SIMPLIFY = 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 rapplyque es, ¡lo olvidé cuando publiqué esta respuesta por primera vez! Obviamente, estoy seguro de que mucha gente lo usa, pero YMMV. rapplyse 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 xde cada subgrupo definido por y:

     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. tapplyes similar en espíritu a las funciones dividir-aplicar-combinar que son comunes en R ( aggregate, by, ave, ddplyetc.) De ahí su estatus de oveja negra.

joran avatar Aug 21 '2011 22:08 joran

En una nota al margen, así es como las distintas plyrfunciones corresponden a las *applyfunciones 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 plyres 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 plyrno es más difícil que comprender las *applyfunciones básicas.

plyry reshapelas funciones han reemplazado casi todas estas funciones en mi uso diario. Pero, también del documento Introducción a Plyr:

Las funciones relacionadas tapplyy sweepno tienen función correspondiente en plyry siguen siendo útiles. mergeEs útil para combinar resúmenes con los datos originales.

JoFrhwld avatar Aug 17 '2010 19:08 JoFrhwld

De la diapositiva 21 de http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :

aplicar, aplicar, aplicar, por, agregar

(Con suerte, queda claro que applycorresponde a @Hadley aaplyy aggregatecorresponde a @Hadley, ddplyetc. La diapositiva 20 del mismo slideshare aclarará si no la obtienes de esta imagen).

(a la izquierda está la entrada, arriba está la salida)

isomorphismes avatar Oct 09 '2011 05:10 isomorphismes