是否有像 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.table
和 RcppRoll
的解决方案,性能应该更高。
它不像我想要的那么干净——实际上 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
功能,那么它将是实现您所寻找的另一个高性能选项。
我想计算分组数据滑动 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.table
和 RcppRoll
的解决方案,性能应该更高。
它不像我想要的那么干净——实际上 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
功能,那么它将是实现您所寻找的另一个高性能选项。