Роллинг cumsum в data.table

Попытка получить (обратные) кумулятивные суммы в движущемся окне по группе в data.table. Например, из следующих данных я хотел бы получить эти значения в столбце roll_cumsum:

dt = data.table()
dt[, a := seq(1, 10, 1)]
dt[, group := rep(1:2, each = 5)]
dt[, roll_cumsum := c(15, 14, 12, 9, 5, 40, 34, 27, 19, 10)]

Я получил желаемые результаты с помощью приведенного ниже кода, но он довольно медленный для большого набора данных:

partial_sum = function(x) { n <- seq_along(x); cumsum(x)[length(x)] - cumsum(x)[n] + x[n] }
dt[, partial_sum(a), by = group]

Любые предложения, чтобы сделать расчет быстрее? Большое спасибо!


person jayc    schedule 15.01.2021    source источник


Ответы (2)


Есть функция revcumsum

library(spatstat.utils)
dt[, roll_cumsum2 := revcumsum(a), group]

-выход

dt
#     a group roll_cumsum roll_cumsum2
# 1:  1     1          15           15
# 2:  2     1          14           14
# 3:  3     1          12           12
# 4:  4     1           9            9
# 5:  5     1           5            5
# 6:  6     2          40           40
# 7:  7     2          34           34
# 8:  8     2          27           27
# 9:  9     2          19           19
#10: 10     2          10           10

Или просто сделайте reverse

dt[, roll_cumsum2 := rev(cumsum(rev(a))), group]

-выход

dt
#     a group roll_cumsum roll_cumsum2
# 1:  1     1          15           15
# 2:  2     1          14           14
# 3:  3     1          12           12
# 4:  4     1           9            9
# 5:  5     1           5            5
# 6:  6     2          40           40
# 7:  7     2          34           34
# 8:  8     2          27           27
# 9:  9     2          19           19
#10: 10     2          10           10

Или другой способ

dt[, roll_cumsum2 := cumsum(a[.N:1])[.N:1], group]

ПРИМЕЧАНИЕ. Обе версии являются компактными.

Ориентиры

dt1 <- data.table(a = 1:1e7, group = rep(1:1e6, length.out = 1e7,  10))
system.time(dt1[, roll_cumsum := partial_sum(a), by = group])
#user  system elapsed 
# 2.073   0.037   2.094 
system.time(dt1[, roll_cumsum2 := revcumsum(a), group])
#user  system elapsed 
#  2.623   0.029   2.637 

system.time(dt1[, roll_cumsum3 := rev(cumsum(rev(a))), group])
#user  system elapsed 
#  4.275   0.051   4.276 

system.time(dt1[, roll_cumsum4 := cumsum(a[.N:1])[.N:1], group])
#user  system elapsed 
# 1.703   0.028   1.722 

system.time(dt1[, roll_cumsum5 := sum(a) - cumsum(shift(a, fill = 0)), group])
# user  system elapsed 
# 10.095   0.041  10.129 
person akrun    schedule 15.01.2021
comment
Мне последний вариант больше понравился! Большое спасибо! - person jayc; 15.01.2021
comment
Как насчет frollapply времени? Я сомневаюсь, что это будет конкурентоспособным, но это довольно популярный метод для функций прокрутки UDF. - person jangorecki; 15.01.2021
comment
@jangorecki Понятия не имею. Было бы здорово проверить и их. - person akrun; 15.01.2021

Вы можете вычесть совокупную сумму a из sum(a) в каждой группе.

library(data.table)
dt[, roll_cumsum1 :=  sum(a) - cumsum(shift(a, fill = 0)), group]
dt

#     a group roll_cumsum roll_cumsum1
# 1:  1     1          15           15
# 2:  2     1          14           14
# 3:  3     1          12           12
# 4:  4     1           9            9
# 5:  5     1           5            5
# 6:  6     2          40           40
# 7:  7     2          34           34
# 8:  8     2          27           27
# 9:  9     2          19           19
#10: 10     2          10           10
person Ronak Shah    schedule 15.01.2021