我在R中有一个data.table
:
library(data.table)
set.seed(1)
DT = data.table(
group=sample(letters[1:2],100,replace=TRUE),
year=sample(2010:2012,100,replace=TRUE),
v=runif(100))
按组和年份将这些数据汇总到汇总表中简单而优雅:
table <- DT[,mean(v),by='group, year']
但是,将这些数据汇总到汇总表(包括小计和总计)中会稍微困难一点,并且不那么优雅:
library(plyr)
yearTot <- DT[,list(mean(v),year='Total'),by='group']
groupTot <- DT[,list(mean(v),group='Total'),by='year']
Tot <- DT[,list(mean(v), year='Total', group='Total')]
table <- rbind.fill(table,yearTot,groupTot,Tot)
table$group[table$group==1] <- 'Total'
table$year[table$year==1] <- 'Total'
这会产生:
table[order(table$group, table$year), ]
是否有一种简单的方法可以使用data.table指定小计和总计,例如plyr的margins=TRUE
命令?我更喜欢在我的数据集上使用data.table over plyr,因为它是一个非常大的数据集,我已经拥有data.table格式。
答案 0 :(得分:11)
在最近的devel data.table中,您可以使用名为“分组集”的新功能来生成子总计:
$ pip install git+http://github.com/networkx/networkx
答案 1 :(得分:9)
我不知道一个简单的方法。这是对实现的第一次尝试。我不知道plyr中的margins=TRUE
,这是做什么的?
crossby = function(DT, j, by) {
j = substitute(j)
ans = rbind(
DT[,eval(j),by],
DT[,list("Total",eval(j)),by=by[1]],
cbind("Total",DT[,eval(j),by=by[2]]),
list("Total","Total",DT[,eval(j)]),
use.names=FALSE
# 'use.names' argument added in data.table v1.8.0
)
setkeyv(ans,by)
ans
}
crossby(DT, mean(v), c("group","year"))
group year V1
[1,] a 2010 0.2926945
[2,] a 2011 0.4176346
[3,] a 2012 0.4227796
[4,] a Total 0.3901875
[5,] b 2010 0.5231845
[6,] b 2011 0.4997119
[7,] b 2012 0.4306871
[8,] b Total 0.4835788
[9,] Total 2010 0.4278093
[10,] Total 2011 0.4463616
[11,] Total 2012 0.4271160
[12,] Total Total 0.4350153
答案 2 :(得分:5)
请参阅下面的解决方案 - 类似于上面的@ MattDowle - 可以使用任意数量的组。
crossby2 <- function(data, j, by, grand.total = T, total.label = "(all)", value.label = "value") {
j = substitute(j)
# Calculate by each group
lst <- lapply(1:length(by), function(i) {
x <- data[, list(..VALUE.. = eval(j)), by = eval(by[1:i])]
if (i != length(by)) x[, (by[-(1:i)]) := total.label]
return(x)
})
# Grand total
if (grand.total) lst <- c(lst, list(data[, list(..VALUE.. = eval(j))][, (by) := total.label]))
# Combine all tables
res <- rbindlist(lst, use.names = T, fill = F)
# Change value column name
setnames(res, "..VALUE..", value.label)
# Set proper column order
setcolorder(res, c(by, value.label))
# Sort values
setkeyv(res, by)
return(res)
}
答案 3 :(得分:5)
使用当前答案我已经添加了对多个度量和聚合函数的支持,并且可以添加聚合级别指示符。
#' @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)
答案 4 :(得分:1)
借用此答案(https://stackoverflow.com/a/39536828/4241780),下面提供了一个全子集摘要(与crossby2
不同,而rollup
似乎错过了OP&#39的第9行到第11行; s期望的输出)。此函数可扩展为任意数量的by或aggregate变量,但在其当前状态下仅允许一种类型的聚合函数。非常适合通过组交互计算行子站点(我用它来做)。
add_col_sums.data.table <- function(data, aggvars, byvars, FUN = sum, level = "level") {
# Find all possible subsets of your data
subsets <- lapply(0:length(byvars), combn, x = byvars, simplify = FALSE)
subsets <- do.call(c, subsets)
# Calculate summary value by each subset
agg_values <- lapply(subsets, function(x)
data[,lapply(.SD, FUN), by = x, .SDcols = aggvars])
# Pull them all into one dataframe
dat_out <- rbindlist(agg_values, fill = TRUE)
# Order columns and rows
setorderv(dat_out, byvars, na.last = TRUE)
setcolorder(dat_out, c(byvars, aggvars))
# Add level indication
dat_out[, c(level) := Reduce("+", lapply(.SD, is.na))]
# Return data.table
dat_out[]
}
add_col_sums.data.table(DT, "v", c("group", "year"), FUN = mean)