Скользящая сумма одной переменной в data.frame в количестве шагов, определяемых другой переменной

Я пытаюсь суммировать значения в 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)
)

Я пытаюсь суммировать значения b в группах a. Хитрость в том, что мне нужна сумма 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))

person adl    schedule 17.08.2020    source источник


Ответы (4)


Я не уверен, возможно ли иметь переменный размер окна в любой из скользящих функций. Вот один из способов сделать это с помощью 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
person Ronak Shah    schedule 17.08.2020

1) rollapply

rollapply в зоопарке поддерживает векторную ширину. 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")
person G. Grothendieck    schedule 17.08.2020

Вот решение с пакетом 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() 

Он гибок для окон разных размеров.

Выход:

# 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 - пакет tidyverse возрастом пару месяцев, предназначенный для функций скользящего окна. Дополнительную информацию см. Здесь: страница , виньетка

hop - двигатель slider. С помощью этого решения мы запускаем разные .start и .stop для суммирования значений b в соответствии с a группами.

С помощью _vec вы просите hop вернуть вектор: в данном случае double.

row_number() - это dplyr функция, которая позволяет вам возвращать номер строки каждой группы, что позволяет вам перемещаться по строкам.

person Edo    schedule 17.08.2020

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]
person pseudospin    schedule 17.08.2020