Funciones de agrupamiento (por ejemplo, por, agregado) y la familia * apply

Cada vez que quiero hacer algo “map” py en R, generalmente trato de usar una función en la familia apply .

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 será la entrada, por lo que A menudo los repaso todos hasta que obtengo lo que quiero.

¿Alguien puede explicar cómo usar cuál?

Mi comprensión actual (probablemente incorrecta / incompleta) es …

  1. sapply(vec, f) : input es un vector. output es un vector / matriz, donde el elemento i es f(vec[i]) , dándote una matriz si f tiene una salida de elementos múltiples

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

  3. apply(matrix, 1/2, f) : la entrada es una matriz. output es un vector, donde el elemento i es f (fila / col i de la matriz)
  4. tapply(vector, grouping, f) : output es una matriz / matriz, donde un elemento en la matriz / matriz es el valor de f en una agrupación g del vector, y g se empuja a los nombres de filas / columnas
  5. by(dataframe, grouping, f) : let g sea ​​una agrupación. aplique f a cada columna del grupo / dataframe. imprima bastante la agrupación y el valor de f en cada columna.
  6. aggregate(matrix, grouping, f) : similar a by , pero en lugar de imprimir bastante la salida, el agregado pega todo en un dataframe.

Pregunta al margen: todavía no aprendí ni plyr ni remodelación, ¿ plyr o plyr todo esto por completo?

R tiene muchas * funciones de aplicación que se describen hábilmente en los archivos de ayuda (por ejemplo, ?apply ). Sin embargo, hay suficientes como para que los usuarios principiantes tengan dificultades para decidir cuál es el apropiado para su situación o incluso para recordarlos todos. Pueden tener una sensación general de que “debería estar usando una función * aplicar aquí”, pero puede ser difícil mantenerlos todos rectos al principio.

A pesar del hecho (señalado en otras respuestas) de que gran parte de la funcionalidad de la familia * apply está cubierta por el extremadamente popular paquete plyr , las funciones básicas siguen siendo útiles y vale la pena conocerlas.

Esta respuesta está destinada a actuar como una especie de señal para nuevos usuarios para ayudarlos a dirigir la función correcta * aplicar 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 lo ayude a decidir qué función de aplicación se adapte a su situación y luego depende de usted investigar más a fondo. Con una excepción, las diferencias de rendimiento no se abordarán.

  • 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 forzará a una matriz primero.

     # 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[*, , ] - ie Sum across 2nd and 3rd dimension apply(M, 1, sum) # Result is one-dimensional [1] 120 128 136 144 # Apply sum across each M[*, *, ] - ie 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 quiere medios de fila / columna o sums para una matriz 2D, asegúrese de investigar los colMeans , rowMeans , colSums , rowSums altamente optimizados y rápidos.

  • aplicar - Cuando desee aplicar una función a cada elemento de una lista y obtener una lista de vuelta.

    Este es el caballo de batalla de muchas de las otras * funciones de aplicación. Despegue su código y, a menudo, 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, pero desea un vector de regreso, en lugar de una lista.

    Si te encuentras escribiendo unlist(lapply(...)) , detente y considera sapply .

     x < - list(a = 1, b = 1:3, c = 10:100) # Compare with above; a named vector, not a list sapply(x, FUN = length) abc 1 3 91 sapply(x, FUN = sum) abc 1 6 5005 

    En usos más avanzados de 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 usará 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 usará las matrices individuales para construir una matriz multidimensional:

     sapply(1:5,function(x) matrix(x,2,2), simplify = "array") 

    Cada uno de estos comportamientos, por supuesto, depende de que nuestra función devuelva vectores o matrices de la misma longitud o dimensión.

  • vapply : cuando quieres usar sapply pero tal vez necesites exprimir algo más de velocidad de tu código.

    Por vapply , básicamente le das a R un ejemplo de qué tipo de cosa devolverá tu función, lo que puede ahorrar algo de tiempo al obligar a los valores devueltos a encajar en un solo 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) abc 1 3 91 
  • mapply - Para cuando tiene varias estructuras de datos (por ejemplo, vectores, listas) y desea aplicar una función a los primeros elementos de cada uno, y luego a los segundos elementos de cada uno, etc., forzando el resultado a un vector / matriz como en sapply

    Esto es multivariante 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 con mapply 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 , recursivamente.

    Para darte una idea de cuán poco rapply es la rapply , ¡me olvidé de ella al publicar 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.

    Las ovejas negras de la * familia de aplicación, de tipo. El uso del archivo de ayuda de la frase "matriz fragmentada" puede ser 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 en x dentro de cada subgrupo definido por y :

     tapply(x, y, sum) abcde 10 26 42 58 74 

    Se pueden manejar ejemplos más complejos donde los subgrupos están definidos por las combinaciones únicas de una lista de varios factores. tapply es similar en espíritu a las funciones de dividir-aplicar-combinar que son comunes en R ( aggregate , by , ave , ddply , etc.) De ahí su estado de oveja negra.

En la nota al margen, aquí se muestra cómo las diversas funciones plyr corresponden a la base *apply funciones de *apply (desde el documento de introducción a plyr desde la página web de plyr http://had.co.nz/plyr/ )

 Base function Input Output plyr function --------------------------------------- aggregate dd ddply + colwise apply aa/l aaply / alply by dl dlply lapply ll llply mapply aa/l maply / mlply replicate ra/l raply / rlply sapply la laply 

Uno de los objectives de plyr es proporcionar convenciones de nomenclatura consistentes 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 consistencia en la salida, en que la salida de dlply() es fácilmente pasable a ldply() para producir resultados útiles, etc.

Conceptualmente, aprender plyr no es más difícil que entender las funciones base *apply .

plyr funciones plyr y reshape han reemplazado casi todas estas funciones en mi uso diario. Pero, también desde el documento de introducción a Plyr:

Las funciones relacionadas tapply y sweep no tienen función correspondiente en plyr , 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 :

aplicar, sapply, lapply, by, aggregate

(Afortunadamente está claro que apply corresponde a @ Hadley’s aaply y aggregate corresponde a @ Hadley’s ddply etc. La diapositiva 20 del mismo slideshare aclarará si no la obtiene de esta imagen).

(a la izquierda se ingresa, en la parte superior sale)

Primero comience con la excelente respuesta de Joran : dudoso de que nada pueda mejorar eso.

Entonces los siguientes mnemónicos pueden ayudar a recordar las distinciones entre cada uno. Mientras que algunos son obvios, otros pueden ser menos, para estos encontrarás justificación en las discusiones de Joran.

Mnemotécnica

  • lapply es una lista que actúa sobre una lista o vector y devuelve una lista.
  • sapply es una aplicación simple (la función predeterminada es devolver un vector o matriz cuando sea posible)
  • vapply es una aplicación verificada (permite especificar previamente el tipo de objeto de retorno)
  • rapply es una aplicación recursiva para listas anidadas, es decir, listas dentro de listas
  • tapply es una aplicación etiquetada donde las tags identifican los subconjuntos
  • apply es genérico : aplica una función a las filas o columnas de una matriz (o, más generalmente, a las dimensiones de una matriz)

Construyendo el fondo correcto

Si usar la familia de apply todavía se siente un poco extraño para usted, entonces es posible que le falte un punto de vista clave.

Estos dos artículos pueden ayudar. Proporcionan los antecedentes necesarios para motivar las técnicas de progtwigción funcional que están siendo provistas por la familia de funciones apply .

Los usuarios de Lisp reconocerán el paradigma inmediatamente. Si no está familiarizado con Lisp, una vez que tenga su cabeza alrededor de FP, habrá ganado un poderoso punto de vista para usar en R, y apply tendrá mucho más sentido.

  • Advanced R: Progtwigción funcional , por Hadley Wickham
  • Progtwigción funcional simple en R , por Michael Barton

Desde que me di cuenta de que (las excelentes) respuestas de este post carecían de explicaciones aggregate . Aquí está mi contribución.

POR

La función by , como se indica en la documentación, puede ser sin embargo, como una “envoltura” para tapply . El poder de by surge cuando queremos calcular una tarea que tapply no puede manejar. Un ejemplo es este código:

 ct < - tapply(iris$Sepal.Width , iris$Species , summary ) cb <- by(iris$Sepal.Width , iris$Species , summary ) cb iris$Species: setosa Min. 1st Qu. Median Mean 3rd Qu. Max. 2.300 3.200 3.400 3.428 3.675 4.400 -------------------------------------------------------------- iris$Species: versicolor Min. 1st Qu. Median Mean 3rd Qu. Max. 2.000 2.525 2.800 2.770 3.000 3.400 -------------------------------------------------------------- iris$Species: virginica Min. 1st Qu. Median Mean 3rd Qu. Max. 2.200 2.800 3.000 2.974 3.175 3.800 ct $setosa Min. 1st Qu. Median Mean 3rd Qu. Max. 2.300 3.200 3.400 3.428 3.675 4.400 $versicolor Min. 1st Qu. Median Mean 3rd Qu. Max. 2.000 2.525 2.800 2.770 3.000 3.400 $virginica Min. 1st Qu. Median Mean 3rd Qu. Max. 2.200 2.800 3.000 2.974 3.175 3.800 

Si imprimimos estos dos objetos, ct y cb , tenemos "esencialmente" los mismos resultados y las únicas diferencias están en cómo se muestran y los diferentes atributos de class , respectivamente by cb y array para ct .

Como ya he dicho, el poder de by surge cuando no podemos usar tapply ; el siguiente código es un ejemplo:

  tapply(iris, iris$Species, summary ) Error in tapply(iris, iris$Species, summary) : arguments must have same length 

R dice que los argumentos deben tener las mismas longitudes, decir "queremos calcular el summary de todas las variables en iris largo del factor Species ": pero R simplemente no puede hacer eso porque no sabe cómo manejarlo.

Con la función by R distribuya un método específico para data frame clase de data frame y luego deje que la función de summary funcione incluso si la longitud del primer argumento (y el tipo también) son diferentes.

 bywork < - by(iris, iris$Species, summary ) bywork iris$Species: setosa Sepal.Length Sepal.Width Petal.Length Petal.Width Species Min. :4.300 Min. :2.300 Min. :1.000 Min. :0.100 setosa :50 1st Qu.:4.800 1st Qu.:3.200 1st Qu.:1.400 1st Qu.:0.200 versicolor: 0 Median :5.000 Median :3.400 Median :1.500 Median :0.200 virginica : 0 Mean :5.006 Mean :3.428 Mean :1.462 Mean :0.246 3rd Qu.:5.200 3rd Qu.:3.675 3rd Qu.:1.575 3rd Qu.:0.300 Max. :5.800 Max. :4.400 Max. :1.900 Max. :0.600 -------------------------------------------------------------- iris$Species: versicolor Sepal.Length Sepal.Width Petal.Length Petal.Width Species Min. :4.900 Min. :2.000 Min. :3.00 Min. :1.000 setosa : 0 1st Qu.:5.600 1st Qu.:2.525 1st Qu.:4.00 1st Qu.:1.200 versicolor:50 Median :5.900 Median :2.800 Median :4.35 Median :1.300 virginica : 0 Mean :5.936 Mean :2.770 Mean :4.26 Mean :1.326 3rd Qu.:6.300 3rd Qu.:3.000 3rd Qu.:4.60 3rd Qu.:1.500 Max. :7.000 Max. :3.400 Max. :5.10 Max. :1.800 -------------------------------------------------------------- iris$Species: virginica Sepal.Length Sepal.Width Petal.Length Petal.Width Species Min. :4.900 Min. :2.200 Min. :4.500 Min. :1.400 setosa : 0 1st Qu.:6.225 1st Qu.:2.800 1st Qu.:5.100 1st Qu.:1.800 versicolor: 0 Median :6.500 Median :3.000 Median :5.550 Median :2.000 virginica :50 Mean :6.588 Mean :2.974 Mean :5.552 Mean :2.026 3rd Qu.:6.900 3rd Qu.:3.175 3rd Qu.:5.875 3rd Qu.:2.300 Max. :7.900 Max. :3.800 Max. :6.900 Max. :2.500 

funciona de verdad y el resultado es muy sorprendente. Es un objeto de clase by que a lo largo de las Species (por ejemplo, para cada uno de ellos) calcula el summary de cada variable.

Tenga en cuenta que si el primer argumento es un data frame , la función despachada debe tener un método para esa clase de objetos. Por ejemplo, si usamos este código con la función mean tendremos este código que no tiene ningún sentido:

  by(iris, iris$Species, mean) iris$Species: setosa [1] NA ------------------------------------------- iris$Species: versicolor [1] NA ------------------------------------------- iris$Species: virginica [1] NA Warning messages: 1: In mean.default(data[x, , drop = FALSE], ...) : argument is not numeric or logical: returning NA 2: In mean.default(data[x, , drop = FALSE], ...) : argument is not numeric or logical: returning NA 3: In mean.default(data[x, , drop = FALSE], ...) : argument is not numeric or logical: returning NA 

AGREGAR

aggregate se puede ver como otra forma de uso tapply si lo usamos de tal manera.

 at < - tapply(iris$Sepal.Length , iris$Species , mean) ag <- aggregate(iris$Sepal.Length , list(iris$Species), mean) at setosa versicolor virginica 5.006 5.936 6.588 ag Group.1 x 1 setosa 5.006 2 versicolor 5.936 3 virginica 6.588 

Las dos diferencias inmediatas son que el segundo argumento de aggregate debe ser una lista, mientras que tapply puede (no obligatorio) ser una lista y que el resultado de aggregate es un dataframe mientras que el de tapply es una array .

El poder del aggregate es que puede manejar fácilmente subconjuntos de los datos con argumento de subset y que también tiene métodos para los objetos ts y la formula .

Estos elementos hacen que el aggregate sea ​​más fácil de trabajar con eso en ciertas situaciones. Aquí hay algunos ejemplos (disponibles en la documentación):

 ag < - aggregate(len ~ ., data = ToothGrowth, mean) ag supp dose len 1 OJ 0.5 13.23 2 VC 0.5 7.98 3 OJ 1.0 22.70 4 VC 1.0 16.77 5 OJ 2.0 26.06 6 VC 2.0 26.14 

Podemos lograr lo mismo con tapply pero la syntax es un poco más difícil y la salida (en algunas circunstancias) menos legible:

 att < - tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean) att OJ VC 0.5 13.23 7.98 1 22.70 16.77 2 26.06 26.14 

Hay otros momentos en los que no podemos usar by o tapply y tenemos que usar aggregate .

  ag1 < - aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean) ag1 Month Ozone Temp 1 5 23.61538 66.73077 2 6 29.44444 78.22222 3 7 59.11538 83.88462 4 8 59.96154 83.96154 5 9 31.44828 76.89655 

No podemos obtener el resultado previo con una sola llamada, pero tenemos que calcular la media a lo largo del Month para cada elemento y luego combinarlos (también tenga en cuenta que debemos llamar a na.rm = TRUE , porque los métodos de formula de la función aggregate tiene por defecto el na.action = na.omit ):

 ta1 < - tapply(airquality$Ozone, airquality$Month, mean, na.rm = TRUE) ta2 <- tapply(airquality$Temp, airquality$Month, mean, na.rm = TRUE) cbind(ta1, ta2) ta1 ta2 5 23.61538 65.54839 6 29.44444 79.10000 7 59.11538 83.90323 8 59.96154 83.96774 9 31.44828 76.90000 

mientras que con simplemente no podemos lograr eso, de hecho, la siguiente llamada de función devuelve un error (pero lo más probable es que esté relacionado con la función proporcionada, mean ):

 by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE) 

Otras veces los resultados son los mismos y las diferencias son solo en la clase (y luego cómo se muestra / imprime y no solo, por ejemplo, cómo subconjunto) objeto:

 byagg < - by(airquality[c("Ozone", "Temp")], airquality$Month, summary) aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary) 

El código anterior logra el mismo objective y resultados, en algunos puntos, qué herramienta usar es solo una cuestión de gustos y necesidades personales; los dos objetos anteriores tienen necesidades muy diferentes en términos de subconjunto.

Hay muchas respuestas excelentes que discuten las diferencias en los casos de uso para cada función. Ninguna de las respuestas discute las diferencias en el rendimiento. Eso es razonable porque varias funciones esperan varias entradas y producen varios resultados, sin embargo, la mayoría de ellas tienen un objective común general para evaluar por series / grupos. Mi respuesta se centrará en el rendimiento. Debido a lo anterior, la creación de la entrada de los vectores se incluye en el tiempo, también la función de apply no se mide.

He probado dos funciones diferentes sum y length a la vez. El volumen probado es 50M en la entrada y 50K en la salida. También incluí dos paquetes actualmente populares que no se usaban ampliamente en el momento en que se hizo la pregunta, data.table y dplyr . Ambos definitivamente valen la pena si buscas un buen rendimiento.

 library(dplyr) library(data.table) set.seed(123) n = 5e7 k = 5e5 x = runif(n) grp = sample(k, n, TRUE) timing = list() # sapply timing[["sapply"]] = system.time({ lt = split(x, grp) r.sapply = sapply(lt, function(x) list(sum(x), length(x)), simplify = FALSE) }) # lapply timing[["lapply"]] = system.time({ lt = split(x, grp) r.lapply = lapply(lt, function(x) list(sum(x), length(x))) }) # tapply timing[["tapply"]] = system.time( r.tapply < - tapply(x, list(grp), function(x) list(sum(x), length(x))) ) # by timing[["by"]] = system.time( r.by <- by(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE) ) # aggregate timing[["aggregate"]] = system.time( r.aggregate <- aggregate(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE) ) # dplyr timing[["dplyr"]] = system.time({ df = data_frame(x, grp) r.dplyr = summarise(group_by(df, grp), sum(x), n()) }) # data.table timing[["data.table"]] = system.time({ dt = setnames(setDT(list(x, grp)), c("x","grp")) r.data.table = dt[, .(sum(x), .N), grp] }) # all output size match to group count sapply(list(sapply=r.sapply, lapply=r.lapply, tapply=r.tapply, by=r.by, aggregate=r.aggregate, dplyr=r.dplyr, data.table=r.data.table), function(x) (if(is.data.frame(x)) nrow else length)(x)==k) # sapply lapply tapply by aggregate dplyr data.table # TRUE TRUE TRUE TRUE TRUE TRUE TRUE 

 # print timings as.data.table(sapply(timing, `[[`, "elapsed"), keep.rownames = TRUE )[,.(fun = V1, elapsed = V2) ][order(-elapsed)] # fun elapsed #1: aggregate 109.139 #2: by 25.738 #3: dplyr 18.978 #4: tapply 17.006 #5: lapply 11.524 #6: sapply 11.326 #7: data.table 2.686 

A pesar de todas las excelentes respuestas aquí, hay 2 funciones básicas más que merecen ser mencionadas, la útil función outer y la oscura función de eapply

exterior

outer es una función muy útil escondida como una más mundana. Si lees la ayuda para outer su descripción dice:

 The outer product of the arrays X and Y is the array A with dimension c(dim(X), dim(Y)) where element A[c(arrayindex.x, arrayindex.y)] = FUN(X[arrayindex.x], Y[arrayindex.y], ...). 

lo que hace que parezca que esto solo es útil para cosas del tipo de álgebra lineal. Sin embargo, se puede usar de forma muy similar a aplicar una función a dos vectores de entradas. La diferencia es que mapply aplicará la función a los primeros dos elementos y luego a los segundos dos, etc., mientras que outer aplicará la función a cada combinación de un elemento del primer vector y uno del segundo. Por ejemplo:

  A< -c(1,3,5,7,9) B<-c(0,3,6,9,12) mapply(FUN=pmax, A, B) > mapply(FUN=pmax, A, B) [1] 1 3 6 9 12 outer(A,B, pmax) > outer(A,B, pmax) [,1] [,2] [,3] [,4] [,5] [1,] 1 3 6 9 12 [2,] 3 3 6 9 12 [3,] 5 5 6 9 12 [4,] 7 7 7 9 12 [5,] 9 9 9 9 12 

Personalmente he usado esto cuando tengo un vector de valores y un vector de condiciones y deseo ver qué valores cumplen con qué condiciones.

eapply

eapply es como lapply excepto que, en lugar de aplicar una función a cada elemento de una lista, aplica una función a cada elemento de un entorno. Por ejemplo, si desea encontrar una lista de funciones definidas por el usuario en el entorno global:

 A< -c(1,3,5,7,9) B<-c(0,3,6,9,12) C<-list(x=1, y=2) D<-function(x){x+1} > eapply(.GlobalEnv, is.function) $A [1] FALSE $B [1] FALSE $C [1] FALSE $D [1] TRUE 

Francamente, no uso mucho esto, pero si construyes muchos paquetes o creas muchos entornos, puede ser útil.

Quizás vale la pena mencionar ave . ave es el primo amistoso de tapply . Devuelve resultados en una forma que puede volver a conectar directamente a su dataframe.

 dfr < - data.frame(a=1:20, f=rep(LETTERS[1:5], each=4)) means <- tapply(dfr$a, dfr$f, mean) ## ABCDE ## 2.5 6.5 10.5 14.5 18.5 ## great, but putting it back in the data frame is another line: dfr$m <- means[dfr$f] dfr$m2 <- ave(dfr$a, dfr$f, FUN=mean) # NB argument name FUN is needed! dfr ## afm m2 ## 1 A 2.5 2.5 ## 2 A 2.5 2.5 ## 3 A 2.5 2.5 ## 4 A 2.5 2.5 ## 5 B 6.5 6.5 ## 6 B 6.5 6.5 ## 7 B 6.5 6.5 ## ... 

No hay nada en el paquete base que funcione como ave para los marcos de datos completos ( by ejemplo, es similar a los marcos de datos). Pero puedes cambiarlo:

 dfr$foo < - ave(1:nrow(dfr), dfr$f, FUN=function(x) { x <- dfr[x,] sum(x$m*x$m2) }) dfr ## afm m2 foo ## 1 1 A 2.5 2.5 25 ## 2 2 A 2.5 2.5 25 ## 3 3 A 2.5 2.5 25 ## ... 

Recientemente descubrí la función de sweep más útil y la agregué aquí para completar:

barrer

La idea básica es barrer a través de una fila de filas o columnas y devolver una matriz modificada. Un ejemplo lo dejará en claro (fuente: datacamp ):

Digamos que tiene una matriz y desea estandarizarla en columnas:

 dataPoints < - matrix(4:15, nrow = 4) # Find means per column with `apply()` dataPoints_means <- apply(dataPoints, 2, mean) # Find standard deviation with `apply()` dataPoints_sdev <- apply(dataPoints, 2, sd) # Center the points dataPoints_Trans1 <- sweep(dataPoints, 2, dataPoints_means,"-") print(dataPoints_Trans1) ## [,1] [,2] [,3] ## [1,] -1.5 -1.5 -1.5 ## [2,] -0.5 -0.5 -0.5 ## [3,] 0.5 0.5 0.5 ## [4,] 1.5 1.5 1.5 # Return the result dataPoints_Trans1 ## [,1] [,2] [,3] ## [1,] -1.5 -1.5 -1.5 ## [2,] -0.5 -0.5 -0.5 ## [3,] 0.5 0.5 0.5 ## [4,] 1.5 1.5 1.5 # Normalize dataPoints_Trans2 <- sweep(dataPoints_Trans1, 2, dataPoints_sdev, "/") # Return the result dataPoints_Trans2 ## [,1] [,2] [,3] ## [1,] -1.1618950 -1.1618950 -1.1618950 ## [2,] -0.3872983 -0.3872983 -0.3872983 ## [3,] 0.3872983 0.3872983 0.3872983 ## [4,] 1.1618950 1.1618950 1.1618950 

NB: para este simple ejemplo, el mismo resultado, por supuesto, se puede lograr más fácilmente mediante
apply(dataPoints, 2, scale)