Используя текущие ответы, я добавил поддержку нескольких показателей и агрегатных функций и могу добавить индикатор уровня агрегации.
#' @title SQL's ROLLUP function
#' @description Returns data.table of aggregates value for each level of hierarchy provided in `by`.
#' @param x data.table input data.
#' @param j expression to evaluate in `j`, support multiple measures.
#' @param by character a hierarchy level for aggregations.
#' @param level logical, use `TRUE` to add `level` column of sub-aggregation.
#' @seealso [postgres: GROUPING SETS, CUBE, and ROLLUP](http://www.postgresql.org/docs/9.5/static/queries-table-expressions.html#QUERIES-GROUPING-SETS), [SO: Aggregating sub totals and grand totals with data.table](http://stackoverflow.com/a/24828162/2490497)
#' @return data.table
#' @examples
#' set.seed(1)
#' x = data.table(group=sample(letters[1:2],100,replace=TRUE),
#' year=sample(2010:2012,100,replace=TRUE),
#' v=runif(100))
#' rollup(x, .(vmean=mean(v), vsum=sum(v)), by = c("group","year"))
library(data.table)
rollup = function(x, j, by, level=FALSE){
stopifnot(is.data.table(x), is.character(by), length(by) >= 2L, is.logical(level))
j = substitute(j)
aggrs = rbindlist(c(
lapply(1:(length(by)-1L), function(i) x[, eval(j), c(by[1:i])][, (by[-(1:i)]) := NA]), # subtotals
list(x[, eval(j), c(by)]), # leafs aggregations
list(x[, eval(j)][, c(by) := NA]) # grand total
), use.names = TRUE, fill = FALSE)
if(level) aggrs[, c("level") := sum(sapply(.SD, is.na)), 1:nrow(aggrs), .SDcols = by]
setcolorder(aggrs, neworder = c(by, names(aggrs)[!names(aggrs) %in% by]))
setorderv(aggrs, cols = by, order=1L, na.last=TRUE)
return(aggrs[])
}
set.seed(1)
x = data.table(group=sample(letters[1:2],100,replace=TRUE),
year=sample(2010:2012,100,replace=TRUE),
month=sample(1:12,100,replace=TRUE),
v=runif(100))
rollup(x, .(vmean=mean(v), vsum=sum(v)), by = c("group","year","month"), level=TRUE)