как агрегировать фрейм данных и добавить 0 для категорий, которые не найдены

У меня есть кадр данных, например:

> prova
  sent weeknumber processed
1  100          1         1
2   23          1         0
3  254          1         1
4  321          2         0
5 1241          2         0
6  323          2         1
7 1221          3         1

structure(list(sent = c(100, 23, 254, 321, 1241, 323, 1221), 
weeknumber = c(1, 1, 1, 2, 2, 2, 3), processed = c(1, 0, 
1, 0, 0, 1, 1)), .Names = c("sent", "weeknumber", "processed"
), row.names = c(NA, -7L), class = "data.frame")

Если я хочу извлечь количество отправленных по номеру недели для строк с обработанным = 0, я могу сделать:

aggregate(prova[prova$processed==0,]$sent, by=list(prova[prova$processed==0,]$weeknumber), FUN = sum)
  Group.1    x
1       1   23
2       2 1562

И если я хочу извлечь сумму отправленных по номеру недели при обработке = 1, я делаю:

aggregate(prova[prova$processed==1,]$sent, by=list(prova[prova$processed==1,]$weeknumber), FUN = sum)
  Group.1    x
1       1  354
2       2  323
3       3 1221

Однако я хотел бы найти способ всегда иметь одинаковую длину результата, то есть в случае обработки = 0, что-то вроде этого:

  Group.1    x
1       1   23
2       2 1562
3       3    0  // this is the new row I'd like to add

Если я просто передам весь список возможных номеров недель, я получу:

aggregate(prova[prova$processed==0,]$sent, by=list(prova$weeknumber), FUN = sum)
Error in aggregate.data.frame(as.data.frame(x), ...) : 
arguments must have same length

Любая подсказка / совет очень ценится!


person user299791    schedule 01.11.2016    source источник


Ответы (1)


Мы можем использовать условие if/else с data.table. Преобразуйте «data.frame» в «data.table» (setDT(prova)), сгруппированный по «номеру недели», if нет any 0 значений в «обработано», верните 0 или else получите sum «отправлено», где «обработано» равно 0.

library(data.table)
setDT(prova)[, .(sent = if(!any(processed==0)) 0 
               else sum(sent[processed==0])), by = weeknumber]
#   weeknumber sent
#1:          1   23
#2:          2 1562
#3:          3    0

Но, если нам нужно sum «отправлено» для каждого значения «обработано», сгруппированного по «номеру недели», удобным вариантом будет dcast.

dcast(setDT(prova), weeknumber~processed, value.var="sent", sum)
#  weeknumber    0    1
#1:          1   23  354
#2:          2 1562  323
#3:          3    0 1221

Или с xtabs из base R, что также делает sum «отправлено» для каждой из комбинаций «номер недели» с «обработано».

xtabs(sent~weeknumber + processed, prova)

Если мы используем aggregate, одним из вариантов является merge вывод aggregate с набором unique «номер недели», а затем замена элементов NA в «отправлено» на 0.

res <- merge(data.frame(weeknumber = unique(prova$weeknumber)), 
      aggregate(sent~weeknumber, prova, subset = processed ==0, FUN = sum),
     all.x=TRUE)
res$sent[is.na(res$sent)] <- 0
res
#   weeknumber sent
#1          1   23
#2          2 1562
#3          3    0
person akrun    schedule 01.11.2016