Операции таблицы данных с несколькими группами по наборам переменных

У меня есть data.table, для которого я хотел бы выполнять групповые операции, но хотел бы сохранить нулевые переменные и использовать разные групповые наборы переменных.

Пример с игрушкой:

library(data.table)
set.seed(1)
DT <- data.table(
        id = sample(c("US", "Other"), 25, replace = TRUE), 
        loc = sample(LETTERS[1:5], 25, replace = TRUE), 
        index = runif(25)
        )

Я хотел бы найти сумму index всеми комбинациями ключевых переменных (включая нулевой набор). Концепция аналогична "наборам группировок" в Oracle SQL, вот пример моего текущего обходного пути:

rbind(
  DT[, list(id = "", loc = "", sindex = sum(index)), by = NULL],
  DT[, list(loc = "", sindex = sum(index)), by = "id"],
  DT[, list(id = "", sindex = sum(index)), by = "loc"],
  DT[, list(sindex = sum(index)), by = c("id", "loc")]
)[order(id, loc)]
       id loc      sindex
 1:           11.54218399
 2:         A  2.82172063
 3:         B  0.98639578
 4:         C  2.89149433
 5:         D  3.93292900
 6:         E  0.90964424
 7: Other      6.19514146
 8: Other   A  1.12107080
 9: Other   B  0.43809711
10: Other   C  2.80724742
11: Other   D  1.58392886
12: Other   E  0.24479728
13:    US      5.34704253
14:    US   A  1.70064983
15:    US   B  0.54829867
16:    US   C  0.08424691
17:    US   D  2.34900015
18:    US   E  0.66484697

Существует ли предпочтительная "таблица данных" для этого?

Ответ 1

От this commit, теперь это возможно с dev-версией data.table с cube или groupingsets:

library("data.table")
# data.table 1.10.5 IN DEVELOPMENT built 2017-08-08 18:31:51 UTC
#   The fastest way to learn (by data.table authors): https://www.datacamp.com/courses/data-analysis-the-data-table-way
#   Documentation: ?data.table, example(data.table) and browseVignettes("data.table")
#   Release notes, videos and slides: http://r-datatable.com

cube(DT, list(sindex = sum(index)), by = c("id", "loc"))
#        id loc      sindex
#  1:    US   B  0.54829867
#  2:    US   A  1.70064983
#  3: Other   B  0.43809711
#  4: Other   E  0.24479728
#  5: Other   C  2.80724742
#  6: Other   A  1.12107080
#  7:    US   E  0.66484697
#  8:    US   D  2.34900015
#  9: Other   D  1.58392886
# 10:    US   C  0.08424691
# 11:    NA   B  0.98639578
# 12:    NA   A  2.82172063
# 13:    NA   E  0.90964424
# 14:    NA   C  2.89149433
# 15:    NA   D  3.93292900
# 16:    US  NA  5.34704253
# 17: Other  NA  6.19514146
# 18:    NA  NA 11.54218399

groupingsets(DT, j = list(sindex = sum(index)), by = c("id", "loc"), sets = list(character(), "id", "loc", c("id", "loc")))
#        id loc      sindex
#  1:    NA  NA 11.54218399
#  2:    US  NA  5.34704253
#  3: Other  NA  6.19514146
#  4:    NA   B  0.98639578
#  5:    NA   A  2.82172063
#  6:    NA   E  0.90964424
#  7:    NA   C  2.89149433
#  8:    NA   D  3.93292900
#  9:    US   B  0.54829867
# 10:    US   A  1.70064983
# 11: Other   B  0.43809711
# 12: Other   E  0.24479728
# 13: Other   C  2.80724742
# 14: Other   A  1.12107080
# 15:    US   E  0.66484697
# 16:    US   D  2.34900015
# 17: Other   D  1.58392886
# 18:    US   C  0.08424691

Ответ 2

У меня есть общая функция, которую вы можете подавать в фреймворк данных и вектор размеров, который хотите группировать, и он будет возвращать сумму всех числовых полей, сгруппированных по этим размерам.

rollSum = function(input, dimensions){
   #cast dimension inputs to character in case a dimension input is numeric 
   for (x in 1:length(dimensions)){
       input[[eval(dimensions[x])]] = as.character(input[[eval(dimensions[x])]])
    }
    numericColumns = which(lapply(input,class) %in% c("integer", "numeric")) 
    output = input[,lapply(.SD, sum, na.rm = TRUE), by = eval(dimensions),
    .SDcols = numericColumns]
    return(output)
}

Итак, вы можете создать список своей другой группы по векторам:

groupings = list(c("id"),c("loc"),c("id","loc"))

И затем используйте его с lapply и rbindlist следующим образом:

groupedSets = rbindlist(lapply(groupings, function(x){
    return(rollSum(DT,x))}), fill = TRUE)

Ответ 3

с помощью dplyr, адаптация этого должна работать, если я правильно понимаю ваш вопрос.

sum <- mtcars %>%
  group_by(vs, am) %>%
  summarise(Sum=sum(mpg))

Я не проверял, как он относится к значениям missung, но он должен просто создать другую группу из них (последняя группа).