Sin embargo, otra opción es ave
. Para una buena medida, he recopilado las respuestas anteriores, hice todo lo posible para hacer que su salida sea equivalente (un vector), y proporcioné intervalos de más de 1000 ejecuciones usando sus datos de ejemplo como entrada. Primero, mi respuesta usando ave
: ave(df$x, df$index, FUN = function(z) z/sum(z))
. También muestro un ejemplo usando el paquete data.table
, ya que generalmente es bastante rápido, pero sé que estás buscando soluciones básicas, por lo que puedes ignorarlo si quieres.
Y ahora un montón de tiempos:
library(data.table)
library(plyr)
dt <- data.table(df)
plyr <- function() ddply(df, .(index), transform, z = x/sum(x))
av <- function() ave(df$x, df$index, FUN = function(z) z/sum(z))
t.apply <- function() unlist(tapply(df$x, df$index, function(x) x/sum(x)))
l.apply <- function() unlist(lapply(split(df$x, df$index), function(x){x/sum(x)}))
b.y <- function() unlist(by(df$x, df$index, function(x){x/sum(x)}))
agg <- function() aggregate(df$x, list(df$index), function(x){x/sum(x)})
d.t <- function() dt[, x/sum(x), by = index]
library(rbenchmark)
benchmark(plyr(), av(), t.apply(), l.apply(), b.y(), agg(), d.t(),
replications = 1000,
columns = c("test", "elapsed", "relative"),
order = "elapsed")
#-----
test elapsed relative
4 l.apply() 0.052 1.000000
2 av() 0.168 3.230769
3 t.apply() 0.257 4.942308
5 b.y() 0.694 13.346154
6 agg() 1.020 19.615385
7 d.t() 2.380 45.769231
1 plyr() 5.119 98.442308
la solución lapply()
parece ganar en este caso y data.table()
es sorprendentemente lento. Veamos cómo esto se adapta a un problema de agregación más grande:
df <- data.frame(x = sample(1:100, 1e5, TRUE), index = gl(1000, 100))
dt <- data.table(df)
#Replication code omitted for brevity, used 100 replications and dropped plyr() since I know it
#will be slow by comparison:
test elapsed relative
6 d.t() 2.052 1.000000
1 av() 2.401 1.170078
3 l.apply() 4.660 2.270955
2 t.apply() 9.500 4.629630
4 b.y() 16.329 7.957602
5 agg() 20.541 10.010234
que parece más consistente con lo que esperaría.
En resumen, tienes muchas buenas opciones. Encuentre uno o dos métodos que funcionen con su modelo mental de cómo deberían funcionar las tareas de agregación y domine esa función. Muchas maneras de despellejar a un gato.
Editar - y un ejemplo con filas 1E7
Probablemente no lo suficientemente grande como para Matt, pero tan grande como mi portátil puede manejar sin que se caiga:
df <- data.frame(x = sample(1:100, 1e7, TRUE), index = gl(10000, 1000))
dt <- data.table(df)
#-----
test elapsed relative
6 d.t() 0.61 1.000000
1 av() 1.45 2.377049
3 l.apply() 4.61 7.557377
2 t.apply() 8.80 14.426230
4 b.y() 8.92 14.622951
5 agg() 18.20 29.83606
Esta es una gran respuesta tan - gracias! –
Me alegro de que se haya dado cuenta de que la primera prueba fue encontrar diferencias significativas de tiempos insignificantes. No sé por qué 'benchmark' tiene realmente un argumento de 'replicaciones': parece alentar a la gente a sobrepasar el tiempo y omitir completamente el punto sobre' data.table'. –
Además, '1e5' no es lo suficientemente grande para que 'data.table' brille realmente. Pruebe '1e6',' 1e7' y '1e8'. Entonces debería ser significativamente más rápido que el siguiente más rápido ('ave()'). Una longitud de vector 'numérica'' 1e8' es 0.75GB, por lo que está empezando a ser el tamaño que queremos decir con datos de gran tamaño. En algún momento 'ave()' también fallará con 'out of memory', pero 'data.table' continuará funcionando. –