Totales agregados por bloques en tablas

En ocasiones uno quiere añadir un total calculado en ciertos bloques a una tabla. Por ejemplo, en la tabla

set.seed(1234)
ventas.orig <- data.frame(
    cliente = rep(1:10, each = 5),
    producto = rep(letters[1:5], times = 10),
    importe = rlnorm(50))

tenemos clientes, productos e importes. Y nos preguntamos por el porcentaje en términos de importe que cada producto supone para cada cliente.

Una manera natural pero torpe de realizar este cálculo consiste en usar un objeto intermedio y merge:

library(plyr)
tmp <- ddply(ventas.orig, .(cliente),
    summarize, total = sum(importe))
ventas <- merge(ventas.orig, tmp)
ventas$pct.producto <- 100 * ventas$importe /
    ventas$total

No os asustéis, se puede hacer aún peor (p.e., usando sqldf). Pero existen dos maneras, cuando menos, de hacerlo mejor. La primera es usando data.table.

library(data.table)

ventas <- data.table(ventas.orig)
ventas[, total.cliente := sum(importe), by = cliente]
ventas$pct.producto <- 100 * ventas$importe /
    ventas$total.cliente

El operador := es el que hace la magia en la segunda línea. Una ventaja de data.table es que vuela literalmente con conjuntos de datos semigrandes.

También es posible hacerlo todavía más sucintamente con plyr:

library(plyr)
ventas <- ddply(ventas.orig, .(cliente),
    transform,
    pct.producto = 100 * importe / sum(importe))

Una única línea. El problema de plyr, sin embargo, es que es ineficiente con conjuntos de datos grandecitos.

Tengo pendiente hacerme con dplyr. Dicen que combina lo mejor de ambos mundos (plyr y data.table). Espero que pronto podamos saberlo todos.