data.frame 中一个变量在另一个变量定义的步数中的滚动总和

Rolling sum of one variable in data.frame in number of steps defined by another variable

我正在尝试以累积的方式总结 data.frame 中的值。

我有这个:

df <- data.frame(
  a = rep(1:2, each = 5),
  b = 1:10,
  step_window = c(2,3,1,2,4, 1,2,3,2,1)
)

我正在尝试汇总组 ab 的值。诀窍是,我想要 b 值的总和对应于 step_window.

给出的当前行之后的行数

这是我正在寻找的输出:

data.frame(
    a = rep(1:2, each = 5),
    step_window = c(2,3,1,2,4, 
                    1,2,3,2,1),
    b = 1:10,
    sum_b_step_window = c(3, 9, 3, 9, 5,
                          6, 15, 27, 19, 10)
  ) 

我尝试使用 RcppRoll 执行此操作,但出现错误 Expecting a single value:

df %>% 
  group_by(a) %>% 
  mutate(sum_b_step_window = RcppRoll::roll_sum(x = b, n = step_window))

我不确定是否可以在任何滚动函数中使用可变 window 大小。这是使用 map2_dbl 执行此操作的一种方法:

library(dplyr)
df %>% 
  group_by(a) %>% 
  mutate(sum_b_step_window = purrr::map2_dbl(row_number(), step_window, 
                             ~sum(b[.x:(.x + .y - 1)], na.rm = TRUE)))

#      a     b step_window sum_b_step_window
#   <int> <int>       <dbl>             <dbl>
# 1     1     1           2                 3
# 2     1     2           3                 9
# 3     1     3           1                 3
# 4     1     4           2                 9
# 5     1     5           4                 5
# 6     2     6           1                 6
# 7     2     7           2                15
# 8     2     8           3                27
# 9     2     9           2                19
#10     2    10           1                10

这是包 slider.

的解决方案
library(dplyr)
library(slider)

df %>%
    group_by(a) %>% 
    mutate(sum_b_step_window = hop_vec(b, row_number(), step_window+row_number()-1, sum)) %>% 
    ungroup() 

它可以灵活地适应不同的 window 尺寸。

输出:

# A tibble: 10 x 4
       a     b step_window sum_b_step_window
   <int> <int>       <dbl>             <int>
 1     1     1           2                 3
 2     1     2           3                 9
 3     1     3           1                 3
 4     1     4           2                 9
 5     1     5           4                 5
 6     2     6           1                 6
 7     2     7           2                15
 8     2     8           3                27
 9     2     9           2                19
10     2    10           1                10

slider 是一个 couple-of-months-old tidyverse 包,专门用于滑动 window 函数。在这里查看更多信息:page, vignette

hopslider 的引擎。使用此解决方案,我们将触发不同的 .start.stop 以根据 a 组对 b 的值求和。

对于 _vec,您要求 hop 到 return 向量:在这种情况下是双精度数。

row_number() 是一个 dplyr 函数,允许您 return 每组的行号,从而允许您沿着行滑动。

1) 滚动应用

rollapply in zoo 支持矢量宽度。 partial=TRUE 表示如果宽度超过末尾则仅使用数据中的值。 (另一种可能性是使用 fill=NA 代替,在这种情况下,如果没有足够的数据,它将用 NA 填充)。 align="left" 指定每一步的当前值是求和范围的左端。

library(dplyr)
library(zoo)

df %>%
  group_by(a) %>%
  mutate(sum = rollapply(b, step_window, sum, partial = TRUE, align = "left")) %>%
  ungroup

2) SQL

这也可以在 SQL 中完成,方法是在指定条件下将 df 左连接到自身,然后对条件匹配的所有行求和的每一行。

library(sqldf)

sqldf("select A.*, sum(B.b) as sum
  from df A 
  left join df B on B.rowid between A.rowid and A.rowid + A.step_window - 1
    and A.a = B.a
  group by A.rowid")

data.table 使用累积和的解决方案

setDT(df)
df[, sum_b_step_window := {
  cs <- c(0,cumsum(b))
  cs[pmin(.N+1, 1:.N+step_window)]-cs[pmax(1, (1:.N))]
},by = a]