R - Как запустить средний и максимальный значения в разных столбцах data.table на основе нескольких факторов и вернуть исходные имена кодов

Я меняю свой R-код с data.frame + plyr на data.table, так как мне нужен более быстрый и эффективный с точки зрения памяти способ обработки большого набора данных. К сожалению, мои навыки R крайне ограничены, и я ударил стену целый день. Был бы признателен, если бы эксперты SO могли просветить.

Мои цели

  • Совокупные строки в моей таблице данных. на основе двух функций - средний и макс - запуск по выбранным столбцам (с именами столбцов, переданными через вектор), тогда как группировка по столбцам также передается через вектор.
  • В результате DT должен содержать исходные имена столбцов.
  • Не должно быть ненужного копирования DT для сохранения памяти

Мой тестовый код

DT = data.table( a=LETTERS[c(1,1,1:4)],b=4:9, c=3:8, d = rnorm(6), 
                 e=LETTERS[c(rep(25,3),rep(26,3))], key="a" )

GrpVar1 <- "a"
GrpVar2 <- "e"
VarToMax <- "b"
VarToAve <- c( "c", "d")

Что я пробовал, но не работал у меня

DT[, list( b=max( b ), c=mean(c), d=mean(d) ), by=c( GrpVar1, GrpVar2 ) ]  
# Hard-code col name - not what I want

DT[, list( max( get(VarToMax) ), mean( get(VarToAve) )), by=c( GrpVar1, GrpVar2 ) ]  
# Col names become 'V1', 'V2', worse, 1 column goes missing - Not what I want either

DT[, list( get(VarToMax)=max( get(VarToMax) ), 
           get(VarToAve)=mean( get(VarToAve) ) ), by=c( GrpVar1, GrpVar2 ) ]
# Above code gave Error!

Дополнительный вопрос

Основываясь на моем очень ограниченном понимании DT, аргумент with = F должен указывать R для анализа значений VarToMax и VarToAve, но запуск кода ниже приводит к ошибке.

DT[, list( max(VarToMax), mean(VarToAve) ), by=c( GrpVar1, GrpVar2 ), with=F ]

# Error in `[.data.table`(DT, , list(max(VarToMax), mean(VarToAve)), by = c(GrpVar1,  : 
#   object 'ansvals' not found
# In addition: Warning message:
# In mean.default(VarToAve) :
#   argument is not numeric or logical: returning NA

Существующие SO-решения не могут помочь

Arun решение было таким, каким я дошел до этого момента, но я очень застрял. Его другое решение с использованием lapply и .SDcols предполагает создание 2 дополнительных DT, которые не соответствуют моему требованию сохранения памяти.

dt1 <- dt[, lapply(.SD, sum), by=ID, .SDcols=c(3,4)]
dt2 <- dt[, lapply(.SD, head, 1), by=ID, .SDcols=c(2)]

Я ТАК запутался в data.table! Любая помощь будет оценена по достоинству!

Ответ 1

Вот моя скромная попытка

DT[, as.list(c(setNames(max(get(VarToMax)), VarToMax), 
               lapply(.SD[, VarToAve, with = FALSE], mean))), 
     c(GrpVar1, GrpVar2)]    
#    a e b c          d
# 1: A Y 6 4 -0.8000173
# 2: B Z 7 6  0.2508633
# 3: C Z 8 7  1.1966517
# 4: D Z 9 8  1.7291615

Или для максимальной эффективности вы можете использовать комбинацию colMeans и eval(as.name()) вместо lapply и get

DT[, as.list(c(setNames(max(eval(as.name(VarToMax))), VarToMax), 
             colMeans(.SD[, VarToAve, with = FALSE]))), 
     c(GrpVar1, GrpVar2)]   
#    a e b c          d
# 1: A Y 6 4 -0.8000173
# 2: B Z 7 6  0.2508633
# 3: C Z 8 7  1.1966517
# 4: D Z 9 8  1.7291615

Ответ 2

Подобным образом, как @David Arenburg, но используя .SDcols, чтобы упростить обозначение. Также я покажу код до слияния.

DTaves <- DT[, lapply(.SD, mean), .SDcols = VarToAve, by = c(GrpVar1, GrpVar2)]
DTmaxs <- DT[, lapply(.SD, max), .SDcols = VarToMax, by = c(GrpVar1, GrpVar2)]
merge(DTmaxs, DTaves)
##    a e b c          d
## 1: A Y 6 4  0.2230091
## 2: B Z 7 6  0.5909434
## 3: C Z 8 7 -0.4828223
## 4: D Z 9 8 -1.3591240

В качестве альтернативы вы можете сделать это за один раз, подмножив .SD и используя with = FALSE

DT[, c(lapply(.SD[, VarToAve, with=FALSE], mean), 
       lapply(.SD[, VarToMax, with=FALSE], max)), 
   by = c(GrpVar1, GrpVar2)]
##    a e c          d b
## 1: A Y 4  0.2230091 6
## 2: B Z 6  0.5909434 7
## 3: C Z 7 -0.4828223 8
## 4: D Z 8 -1.3591240 9