是否有像 rollapplyr 这样的快速 R 函数随着 window 大小的增加?

Is there a fast R function like rollapplyr with increasing window size?

我想计算分组数据滑动 window 的总和。

因为我想尽可能坚持官方功能,所以我开始使用 rollapplyr 是这样的:

library(tidyverse)
library(reshape2)
library(zoo)  

data = data.frame(Count=seq(1,10,1),
                  group=c("A","B","A","A","B","B","B","B","A","A"))


window_size <- 3    

data_rolling <- data %>%
  arrange(group) %>%
  group_by(group) %>%
  mutate(Rolling_Count = rollapplyr(Count, width=window_size, FUN=sum, fill = NA)) %>%
  ungroup()

对于小于宽度的第一个条目(在本例中为 3),它会按照定义填充 NA,但实际上我想像这样获得可能数据的总和:

 Count group Rolling_Count expected_Result
 1     A            NA    1
 3     A            NA    4
 4     A            8     8
 9     A            16    16
10     A            23    23
 2     B            NA    2
 5     B            NA    7
 6     B            13    13
 7     B            18    18
 8     B            21    21

我知道我可以用这样的东西替换 width=window_size

c(rep(1:window_size,1),rep(window_size:window_size,(n()-window_size)))

得到我想要的,但这真的很慢。此外,这种方法会假设 n() 大于 window_size。

那么:是否已经有一个 R/zoo 函数可以处理像上面这样的分组数据以及少于 window_size 条目的数据,并且比上述方法更快?

感谢任何提示!

基于 data.tableRcppRoll 的解决方案,性能应该更高。

它不像我想要的那么干净——实际上 RcppRoll::roll_sum() 中有一个 partial 参数尚未实现,理论上可以干净地解决这个问题,但似乎没有这样很快就会奏效——请参阅 GH Issue #18

无论如何,直到有人在 R 中实现滚动总和以允许您在这里需要的东西,在第一个 n - 1 行添加 cumsum 似乎是一个明智的解决方案。

library(data.table)
library(RcppRoll)

data = data.frame(Count=seq(1,10,1),
                  group=c("A","B","A","A","B","B","B","B","A","A"))

## Convert to a `data.table` by reference
setDT(data)
window_size <- 3    

## Add a counter row so that we can go back and fill in rows
## 1 & 2 of each group
data[,Group_RowNumber := seq_len(.N), keyby = .(group)]

## Do a rolling window -- this won't fill in the first 2 rows
data[,Rolling_Count := RcppRoll::roll_sum(Count,
                                          n = window_size,
                                          align = "right",
                                          fill = NA), keyby = .(group)]

## Go back and fill in the ones we missed
data[Group_RowNumber < window_size, Rolling_Count := cumsum(Count), by = .(group)]

data

#     Count group Group_RowNumber Rolling_Count
#  1:     1     A               1             1
#  2:     3     A               2             4
#  3:     4     A               3             8
#  4:     9     A               4            16
#  5:    10     A               5            23
#  6:     2     B               1             2
#  7:     5     B               2             7
#  8:     6     B               3            13
#  9:     7     B               4            18
# 10:     8     B               5            21

这是另一个解决方案,它更基础一些,但在性能上仍然不落后。它实际上可能更快,因为它缺少 rolling 函数添加的所有功能。我们可以用 base-R 操作替换 data.table 中的 shift 函数,然后应该是你在 base R 中可以获得的最快的。
请注意,如果输入中存在某些 NA,此函数将严重失败,也更有可能出现浮点舍入错误。

data = data.frame(Count=seq(1,10,1),
                  group=c("A","B","A","A","B","B","B","B","A","A"))
window_size = 3

library(data.table)
setDT(data)
# base R fast rolling sum
bRfrs = function(x, n) {
  cumx = cumsum(x)
  cumx - shift(cumx, n, fill=0)
}
data[, .(Count, Rolling_Count=bRfrs(Count, window_size)), group]
#    group Count Rolling_Count
# 1:     A     1             1
# 2:     A     3             4
# 3:     A     4             8
# 4:     A     9            16
# 5:     A    10            23
# 6:     B     2             2
# 7:     B     5             7
# 8:     B     6            13
# 9:     B     7            18
#10:     B     8            21

在 data.table 的 1.12.4 版本中,我们已经计划添加 frollsum 功能,那么它将是实现您所寻找的另一个高性能选项。