2017-01-25 8 views
2

Следующая проблема мешает мне так далеко от очень гибкого использования агрегатов data.table.Гибкое смешивание множественных агрегатов в таблице данных для разных комбинаций столбцов

Пример:

library(data.table) 
set.seed(1) 
DT <- data.table(C1=c("a","b","b"), 
       C2=round(rnorm(4),4), 
       C3=1:12, 
       C4=9:12) 

sum_cols <- c("C2","C3") 

#I want to apply a custom aggregation over multiple columns 
DT[,lapply(.SD,sum),by=C1,.SDcols=sum_cols] 

### Part 1 of question ### 
#but what if I want to add another aggregation, e.g. count 
DT[,.N,by=C1] 

#this is not working as intended (creates 4 rows instead of 2 and doesnt contain sum_cols) 
DT[,.(.N,lapply(.SD,sum)),by=C1,.SDcols=sum_cols] 

### Part 2 of question ### 
# or another function for another set of colums and adding a prefix to keep them appart? 
mean_cols <- c("C3","C4") 

#intended table structure (with 2 rows again) 
C1 sum_C2 sum_C3 mean_C3 mean_C4 

Я знаю, что всегда можно объединить различные одиночные результаты агрегации некоторым ключом, но Я уверен, что должно быть правильным, гибкий и простой способ сделать то, что я хотел бы сделать (особенно Часть 2).

ответ

2

Первое, что нужно обратить внимание, это j аргумент, что data.table ожидает выход список, который может быть построен с c , как упоминалось в ответе @ akrun. Есть два способа сделать это:

set.seed(1) 
DT <- data.table(C1=c("a","b","b"), C2=round(rnorm(4),4), C3=1:12, C4=9:12) 
sum_cols <- c("C2","C3") 
mean_cols <- c("C3","C4") 

# with the development version, 1.10.1+ 
DT[, c(
    .N, 
    sum = lapply(.SD[, ..sum_cols], sum), 
    mean = lapply(.SD[, ..mean_cols], mean) 
), by=C1] 

# in earlier versions 
DT[, c(
    .N, 
    sum = lapply(.SD[, sum_cols, with=FALSE], sum), 
    mean = lapply(.SD[, mean_cols, with=FALSE], mean) 
), by=C1] 

mget возвращает список и c соединяет элементы вместе, чтобы сделать список.


Комментарии

Если вы включите verbose data.table вариант для этих вызовов, то вы увидите сообщение:

Результат J именованный список. Очень неэффективно создавать одни и те же имена снова и снова для каждой группы. Когда j = список (...), все имена обнаруживаются, удаляются и возвращаются после завершения группировки для эффективности. Например, использование j = transform() предотвращает это ускорение (рассмотрим изменение на: =). В будущем это сообщение может быть обновлено до предупреждения.

Кроме того, вы увидите, что оптимизированная групповая средняя и сумма не используются (см. ?GForce). Мы можем обойти это by following FAQ 1.6, возможно, но я не мог понять, как это сделать.

+0

Отлично, спасибо - это сделает мою жизнь с dt намного проще и удобнее. Выполнение второго (в данном случае) - я проверю ваши замечания, если у меня возникнут проблемы. Я удивлен, однако, что .SDcols здесь не так полезны ... в общем случае это кажется довольно ограниченным. – katsumi

+0

@katsumi Да, .SDcols - это всего лишь один набор cols за раз, поэтому, если вам нужны несколько (возможно, перекрывающихся) наборов, это будет не так полезно, я думаю. – Frank

0

Выход являются list s, поэтому мы используем c конкатенировать оба list выходы

DT[,c(.N,lapply(.SD,sum)),by=C1,.SDcols=sum_cols] 
# C1 N C2 C3 
# 1: a 4 0.288 22 
# 2: b 8 0.576 56 
+0

Спасибо, что действительно решает первую часть вопроса. Любые идеи для второй части, где я хочу иметь разные агрегации для разных наборов столбцов (в идеале с некоторой зависимой от агеграции схемой именования для выходных столбцов)? – katsumi