La agrupación de funciones (tapply, por, agregada) y el * aplicar la familia
Pregunta
Siempre que quiero hacer algo "mapa" py en R, por lo general tratan de utilizar una función en la familia apply
.
Sin embargo, nunca he entendido bien las diferencias entre ellos - cómo {sapply
, lapply
, etc.} aplicar la función a la entrada de entrada / agrupados, lo que la salida se verá así, o incluso lo que puede ser la entrada -. así que a menudo acaba de pasar por todos ellos hasta que consigo lo que quiero
Puede alguien explicar cómo utilizar la que uno cuando?
Mi actual (incompleta probablemente incorrecta /) es la comprensión ...
-
sapply(vec, f)
: de entrada es un vector. salida es un vector / matriz, donde el elementoi
esf(vec[i])
, que le da una matriz sif
tiene una salida de múltiples elementos -
lapply(vec, f)
:? Mismo quesapply
, pero la producción es una lista ??p> -
apply(matrix, 1/2, f)
: entrada es una matriz. salida es un vector, donde el elementoi
es f (fila / col i de la matriz) -
tapply(vector, grouping, f)
: salida es una matriz / matriz, donde un elemento de la matriz / array es el valor def
a unag
agrupación del vector, yg
es empujado a los nombres de fila / COL -
by(dataframe, grouping, f)
: dejar que sea una agrupacióng
. aplicarf
a cada columna del grupo / trama de datos. bastante imprimir la agrupación y el valor def
en cada columna. -
aggregate(matrix, grouping, f)
:. Similares aby
, pero en lugar de imprimir la salida bastante, palos agregados todo en una trama de datos
pregunta al margen:? Todavía tengo plyr o la forma no se aprende - habría plyr
o reshape
reemplazar la totalidad de éstos por completo
Solución
R ha muchos * aplicar funciones que se describen hábilmente en los archivos de ayuda (por ejemplo ?apply
). Hay suficiente de ellos, sin embargo, que los usuarios principiantes pueden tener dificultades para decidir cuál es el adecuado para su situación o incluso recordar a todos ellos. Pueden tener una sensación general de que "que debería usar una función * aplican aquí", pero puede ser difícil de mantener a todos derecho al principio.
A pesar de (dicho en otras respuestas) que gran parte de la funcionalidad de la familia * aplicarán está cubierta por el paquete plyr
extremadamente popular, las funciones de base siguen siendo útiles y vale la pena conocer.
Esta respuesta es la intención de actuar como una especie de señal para los nuevos usuarios para ayudarles a dirigir a la correcta * aplicar la función para su problema particular. Tenga en cuenta, esto es no la intención de simplemente regurgitan o reemplazar la documentación R! La esperanza es que esta respuesta le ayuda a decidir qué * se aplica trajes función de su situación y entonces le corresponde a usted para investigar más a fondo. Con una excepción, no se abordarán las diferencias de rendimiento.
-
solicite - Cuando se desea aplicar una función a las filas o columnas de una matriz (y análogos de dimensiones superiores); No aconsejable en general para las tramas de datos, ya que coaccionar a una primera 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 medios fila / columna o sumas para una matriz 2D, asegúrese de investigar la altamente optimizado,
colMeans
rápido como un rayo,rowMeans
,colSums
,rowSums
. -
lapply - Cuando se desea aplicar una función a cada elemento de una Lista a su vez y obtener una copia de la lista.
Este es el caballo de batalla de muchos de los otros * Aplicar funciones. Pelar copias de su código y usted encontrará a menudo por debajo
lapply
.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 se desea aplicar una función a cada elemento de una Lista a su vez, pero desea un vector espalda, en lugar de una lista.
Si usted se encuentra escribiendo
unlist(lapply(...))
, detenerse y considerarsapply
.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 de
sapply
intentará coaccionar a la dar lugar a una matriz multi-dimensional, si es apropiado. Por ejemplo, si nuestros devuelve las vectores de la misma longitud,sapply
los utilizarán como columnas de una matriz:sapply(1:5,function(x) rnorm(3,x))
Si nuestra función devuelve una matriz de 2 dimensiones,
sapply
hará esencialmente la misma cosa, el tratamiento de cada matriz devuelta como un único vector de largo:sapply(1:5,function(x) matrix(x,2,2))
A menos que especificamos
simplify = "array"
, en cuyo caso se utilizará las matrices individuales para construir una matriz multidimensional:sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
Cada uno de estos comportamientos es de contingente curso en nuestra función que devuelve vectores o matrices de la misma longitud o dimensión.
-
vapply - Cuando se desea utilizar
sapply
pero quizás necesita exprimir un poco más de velocidad de su código.Para
vapply
, que, básicamente, da R un ejemplo de qué clase de cosa su función devolverá, lo que puede ahorrar algo de tiempo coaccionar volvió valores para caber en un único vector atómica.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 se tiene varias estructuras de datos (por ejemplo, vectores, listas) y que desea aplicar una función a los elementos 1st de cada uno, y luego la 2ª elementos de cada uno, etc., coaccionando el resultado a un vector / matriz como en
sapply
.Este 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 -. Una envoltura de
mapply
conSIMPLIFY = FALSE
, por lo que está garantizado para devolver una lista ??em>Map(sum, 1:5, 1:5, 1:5) [[1]] [1] 3 [[2]] [1] 6 [[3]] [1] 9 [[4]] [1] 12 [[5]] [1] 15
-
rapply - Paracuando se desea aplicar una función a cada elemento de un lista anidada estructura, de forma recursiva.
Para darle una idea de cómo es
rapply
raro, me olvidé del asunto al publicar por primera vez esta respuesta! Obviamente, estoy seguro que muchas personas lo utilizan, pero tu caso es distinto.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 se desea aplicar una función a subconjuntos de un vector y los subconjuntos están definidos por algún otro vector, por lo general una factor.
El garbanzo negro de la familia * aplicar, de todo tipo. el uso del archivo de ayuda de la frase "matriz irregular" puede ser un poco confuso , pero en realidad es bastante simple.
A vector:
x <- 1:20
factor A (de la misma longitud!) Grupos que definen:
y <- factor(rep(letters[1:5], each = 4))
Sume los valores en
x
dentro de cada subgrupo definido pory
:tapply(x, y, sum) a b c d e 10 26 42 58 74
ejemplos más complejos puede ser manejado donde se definen los subgrupos por las combinaciones únicas de una lista de varios factores.
tapply
es similar en espíritu a los split-aplicarán combinar funciones que son común en R (aggregate
,by
,ave
,ddply
, etc.) Por lo tanto su estado de ovejas negro.
Otros consejos
En la nota al margen, aquí es cómo las diversas funciones plyr
corresponden a las funciones de base *apply
(de la intro de documento plyr del plyr página web 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 de plyr
es proporcionar convenciones de nomenclatura coherentes para cada una de las funciones, la codificación de los tipos de datos de entrada y de salida en el nombre de la función. También proporciona la consistencia de la producción, en que la producción de dlply()
es fácilmente aceptable para ldply()
para producir una salida útil, etc.
Conceptualmente, el aprendizaje plyr
es más difícil que la comprensión de las funciones *apply
base.
plyr
y reshape
han reemplazado casi todas estas funciones en cada uno de mis días utilización. Pero, también a partir del documento Introducción a plyr:
funciones relacionadas
tapply
ysweep
haber función enplyr
no correspondiente, y siguen siendo útiles.merge
es útil para combinar resúmenes con los datos originales.
A partir de la diapositiva 21 de http://www.slideshare.net/ -analítico-estrategia plyr-one-datos Hadley / :
(Con suerte, está claro que corresponde a apply
@ aaply
y aggregate
corresponde de Hadley a @ diapositivas de Hadley ddply
etc. 20 de la misma slideshare aclarará si no la obtiene de esta imagen.)
(a la izquierda es la entrada, en la parte superior es de salida)
En primer lugar empezar con excelente respuesta de Joran -. Nada dudoso que pueda mejor
A continuación, los siguientes pueden ayudar mnemotécnicos para recordar las diferencias entre cada uno. Mientras que algunos son obvios, otros pueden ser menos --- para estos encontrará justificación en las discusiones de Joran.
Mnemónicos
-
lapply
es una lista solicite que actúa en una lista o vector y devuelve una lista. -
sapply
es un sencillalapply
(por defecto de función para devolver un vector o matriz cuando sea posible) -
vapply
es un verified aplicar (permite que el tipo de objeto de retorno que se especificó previamente) -
rapply
es un recursiva solicitar listas anidadas, listas es decir, dentro de las listas -
tapply
es un etiquetada de aplicación cuando las etiquetas identifican los subconjuntos -
apply
es genérico : se aplica una función a las filas o columnas de una matriz (o, más generalmente, a las dimensiones de una matriz)
Construyendo el derecho de fondo
Si se utiliza la familia apply
todavía se siente un poco ajeno a ti, entonces podría ser que se está perdiendo un punto clave de vista.
Estos dos artículos pueden ayudar. Ellos proporcionan los antecedentes necesarios para motivar a los técnicas de programación funcionales que están siendo proporcionados por la familia apply
de funciones.
Los usuarios de Lisp reconocerá el paradigma de inmediato. Si no está familiarizado con Lisp, una vez que su cabeza alrededor de FP, se le han ganado un punto de vista para su uso en investigación de gran alcance -. apply
y hará mucho más sentido
- avanzada R: Programación Funcional , por Hadley Wickham
- de programación sencillo y funcional en R , por Michael Barton
Desde que me di cuenta de que (los muy excelentes) respuestas de esta entrada falta de by
y aggregate
explicaciones. Aquí está mi contribución.
por
La función by
, como se indica en la documentación puede ser, sin embargo, como un "contenedor" 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 es la impresión de estos dos objetos, ct
y cb
, que "esencialmente" tiene los mismos resultados y las únicas diferencias están en la forma en que se muestran y los diferentes atributos class
, by
, respectivamente, para cb
y array
para ct
.
Como he dicho, el poder de by
surge cuando no podemos utilizar tapply
; El código siguiente 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 la summary
de toda variable en iris
a lo largo del factor de Species
":. R, pero no pueden hacerlo porque no sabe cómo manejar
Con la función by
R despachar un método específico para la clase data frame
y luego dejar que la función summary
funciona 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 hecho y el resultado es muy sorprendente. Es un objeto de la clase by
que junto Species
(por ejemplo, para cada uno de ellos) calcula la 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, es que utilice este código con la función mean
tendremos el código que no tiene ningún sentido en absoluto:
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
TOTAL
aggregate
puede ser visto como otro de una manera diferente 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 inmediatos son que el segundo argumento de aggregate
debe es una lista mientras tapply
puede (no es obligatorio) una lista y que la salida de aggregate
es una trama de datos mientras que el uno de tapply
es un array
.
El poder de aggregate
es que se puede manejar fácilmente subconjuntos de los datos con argumento subset
y que tiene métodos para objetos ts
y formula
también.
Estos elementos hacen aggregate
más fácil de trabajar que tapply
en algunas situaciones.
He aquí 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
puede lograr lo mismo con tapply
pero la sintaxis 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 utilizar by
o tapply
y tenemos que utilizar 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 anterior con tapply
en una llamada, pero tenemos que calcular la media a lo largo de Month
para cada elemento y luego combinarlas (también en cuenta que tenemos que llamar a la na.rm = TRUE
, debido a que los métodos 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 by
que simplemente no podemos lograr que, de hecho, la siguiente llamada a la función devuelve un error (pero muy probablemente se relaciona con la función suministrado, mean
):
by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)
Otras veces los resultados son los mismos y las diferencias son sólo en la clase (y luego la forma en que se muestra / impreso y no sólo - ejemplo, cómo al subconjunto IT) objeto:
byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)
El código anterior lograr el mismo objetivo y resultados, lo que en algunos puntos herramienta a utilizar es sólo una cuestión de gustos y necesidades personales; los dos objetos anteriores tienen necesidades muy diferentes en términos de creación de subconjuntos.
Hay un montón de grandes respuestas que discuten las diferencias en los casos de uso para cada función. Ninguno de la respuesta discutir las diferencias en el rendimiento. Eso causa razonable diversas funciones espera que varios terminales de entrada y de salida produce diversos, pero la mayoría de ellos tienen un objetivo general común para evaluar por ciclos / grupos. Mi respuesta va a centrarse en el rendimiento. Debido a por encima de la creación de entrada a partir de los vectores se incluye en el tiempo, también la función apply
no se mide.
I han probado dos funciones diferentes sum
y length
a la vez. Volumen evaluados es de 50M en la entrada y en la salida 50K. También he incluido dos paquetes actualmente populares que no han sido ampliamente utilizados en el momento cuando se le preguntó pregunta, data.table
y dplyr
. Ambos son definitivamente vale la pena mirar si está destinado para un rendimiento bueno.
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
Es quizás vale la pena mencionar ave
. ave
es el primo de usar de tapply
. Devuelve resultados en una forma que se puede conectar la espalda recta en su trama de datos.
dfr <- data.frame(a=1:20, f=rep(LETTERS[1:5], each=4))
means <- tapply(dfr$a, dfr$f, mean)
## A B C D E
## 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
## a f m 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 funciona como ave
de tramas de datos enteros (como by
es como tapply
de tramas de datos). Pero se puede eludir que:
dfr$foo <- ave(1:nrow(dfr), dfr$f, FUN=function(x) {
x <- dfr[x,]
sum(x$m*x$m2)
})
dfr
## a f m 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
## ...
A pesar de todas las grandes respuestas aquí, hay 2 funciones más básicas que merecen ser mencionados, la función outer
útil y la función eapply
oscura
exterior
outer
es una función muy útil oculta como uno más mundanas. Si usted lee la ayuda de 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], ...).
, que hace que parezca que esto sólo es útil para las cosas de tipo de álgebra lineal. Sin embargo, puede ser utilizado tanto como mapply
para aplicar una función de dos vectores de insumos. La diferencia es que mapply
se aplicará la función de los dos primeros elementos y luego el segundo dos etc, mientras que outer
se aplicará la función para cada combinación de un elemento del primer vector y una de la segunda. 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
Yo personalmente he utilizado esto cuando tengo un vector de valores y un vector de condiciones y deseo de ver que cumplen las condiciones de la cual los valores.
eapply
eapply
es como lapply
excepto que en lugar de aplicar una función a cada elemento en una lista, se aplica una función a cada elemento en un entorno. Por ejemplo, si usted quiere 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 yo no uso mucho esto, pero si usted está construyendo una gran cantidad de paquetes o crear una gran cantidad de ambientes que puede ser útil.
Recientemente he descubierto la función sweep
más útil y agregarlo aquí en aras de la exhaustividad:
barrido
La idea básica es la de barrido a través de una serie de fila o columna-sabia y devolver una matriz modificada. Un ejemplo aclarará esto (fuente: datacamp ) :
Digamos que usted tiene una matriz y desea estandarizar que por 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
Nota: para este ejemplo sencillo el mismo resultado, por supuesto, puede lograrse más fácilmente por
apply(dataPoints, 2, scale)