Промежуточная сумма для ежедневных данных, которая сбрасывается при смене месяца - PullRequest
2 голосов
/ 28 апреля 2020

У меня есть таблица из двух столбцов (tibble), состоящая из объекта даты и числовой переменной c. Существует не более одной записи в день, но не каждый день имеет запись (ie дата является естественным первичным ключом). Я пытаюсь сделать промежуточную сумму в столбце цифр c вместе с датами, но сбрасываю промежуточную сумму при смене месяца (данные сортируются по возрастанию). Я повторил то, что хочу получить в результате ниже.

Date         score  monthly.running.sum
10/2/2019       7       7
10/9/2019       6       13
10/16/2019      12      25
10/23/2019      2       27
10/30/2019      13      40
11/6/2019       2       2
11/13/2019      4       6
11/20/2019      15      21
11/27/2019      16      37
12/4/2019       4       4
12/11/2019      24      28
12/18/2019      28      56
12/25/2019      8       64
1/1/2020        1       1
1/8/2020        15      16
1/15/2020       9       25
1/22/2020       8       33

Похоже, пакет "runner", возможно, подходит для этого, но я не совсем понимаю, как его проинструктировать. Я знаю, что мог бы использовать операцию join плюс group_by, используя dplyr, чтобы сделать это, но набор данных очень очень большой, и это было бы крайне неэффективно. я мог бы также вручную перебрать список с помощью al oop, но это также кажется не элегантным. Последний вариант, о котором я могу подумать, - это выбрать уникальный вектор из yearmon объектов, а затем разрезать исходный список на множество более коротких списков и запустить на нем простой cumsum, но это также кажется неоптимальным. Я уверен, что это не первый раз, когда кто-то должен это делать, и учитывая, сколько инструментов есть в tidyverse, чтобы сделать что-то, я думаю, что мне просто нужна помощь в поиске правильного. Причина, по которой я ищу инструмент вместо использования одного из методов, которые я описал выше (который занял бы меньше времени, чем написание этого поста), заключается в том, что этот код должен быть очень очень читабельным для аудитории, которая менее знакома с кодом.

Ответы [ 4 ]

2 голосов
/ 28 апреля 2020

Альтернатива Base R:

df$Date <- as.Date(df$Date, "%m/%d/%Y")
df$monthly.running.sum <- with(df, ave(score, format(Date, "%Y-%m"),FUN = cumsum))
df

#         Date score monthly.running.sum
#1  2019-10-02     7                   7
#2  2019-10-09     6                  13
#3  2019-10-16    12                  25
#4  2019-10-23     2                  27
#5  2019-10-30    13                  40
#6  2019-11-06     2                   2
#7  2019-11-13     4                   6
#8  2019-11-20    15                  21
#9  2019-11-27    16                  37
#10 2019-12-04     4                   4
#11 2019-12-11    24                  28
#12 2019-12-18    28                  56
#13 2019-12-25     8                  64
#14 2020-01-01     1                   1
#15 2020-01-08    15                  16
#16 2020-01-15     9                  25
#17 2020-01-22     8                  33
2 голосов
/ 28 апреля 2020

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

library(lubridate)
library(dplyr)

df %>% mutate(Month = month(mdy(Date)),
              Year = year(mdy(Date))) %>%
  group_by(Month, Year) %>%
  mutate(SUM = cumsum(score))

# A tibble: 17 x 6
# Groups:   Month, Year [4]
   Date       score monthly.running.sum Month  Year   SUM
   <chr>      <int>               <int> <int> <int> <int>
 1 10/2/2019      7                   7    10  2019     7
 2 10/9/2019      6                  13    10  2019    13
 3 10/16/2019    12                  25    10  2019    25
 4 10/23/2019     2                  27    10  2019    27
 5 10/30/2019    13                  40    10  2019    40
 6 11/6/2019      2                   2    11  2019     2
 7 11/13/2019     4                   6    11  2019     6
 8 11/20/2019    15                  21    11  2019    21
 9 11/27/2019    16                  37    11  2019    37
10 12/4/2019      4                   4    12  2019     4
11 12/11/2019    24                  28    12  2019    28
12 12/18/2019    28                  56    12  2019    56
13 12/25/2019     8                  64    12  2019    64
14 1/1/2020       1                   1     1  2020     1
15 1/8/2020      15                  16     1  2020    16
16 1/15/2020      9                  25     1  2020    25
17 1/22/2020      8                  33     1  2020    33

Альтернативой будет использование функции floor_date Для того чтобы пересчитать каждую дату как первый день каждого месяца и рассчитать совокупную сумму:

library(lubridate)
library(dplyr)

df %>% mutate(Floor = floor_date(mdy(Date), unit = "month")) %>%
  group_by(Floor) %>%
  mutate(SUM = cumsum(score))

# A tibble: 17 x 5
# Groups:   Floor [4]
   Date       score monthly.running.sum Floor        SUM
   <chr>      <int>               <int> <date>     <int>
 1 10/2/2019      7                   7 2019-10-01     7
 2 10/9/2019      6                  13 2019-10-01    13
 3 10/16/2019    12                  25 2019-10-01    25
 4 10/23/2019     2                  27 2019-10-01    27
 5 10/30/2019    13                  40 2019-10-01    40
 6 11/6/2019      2                   2 2019-11-01     2
 7 11/13/2019     4                   6 2019-11-01     6
 8 11/20/2019    15                  21 2019-11-01    21
 9 11/27/2019    16                  37 2019-11-01    37
10 12/4/2019      4                   4 2019-12-01     4
11 12/11/2019    24                  28 2019-12-01    28
12 12/18/2019    28                  56 2019-12-01    56
13 12/25/2019     8                  64 2019-12-01    64
14 1/1/2020       1                   1 2020-01-01     1
15 1/8/2020      15                  16 2020-01-01    16
16 1/15/2020      9                  25 2020-01-01    25
17 1/22/2020      8                  33 2020-01-01    33
1 голос
/ 28 апреля 2020

Класс yearmon представляет объекты год / месяц, поэтому просто конвертируйте даты в yearmon и накапливайте их, используя эту строку:

library(zoo)

transform(DF, run.sum = ave(score, as.yearmon(Date, "%m/%d/%Y"), FUN = cumsum))

, что дает:

         Date score run.sum
1   10/2/2019     7       7
2   10/9/2019     6      13
3  10/16/2019    12      25
4  10/23/2019     2      27
5  10/30/2019    13      40
6   11/6/2019     2       2
7  11/13/2019     4       6
8  11/20/2019    15      21
9  11/27/2019    16      37
10  12/4/2019     4       4
11 12/11/2019    24      28
12 12/18/2019    28      56
13 12/25/2019     8      64
14   1/1/2020     1       1
15   1/8/2020    15      16
16  1/15/2020     9      25
17  1/22/2020     8      33
1 голос
/ 28 апреля 2020

Мы также можем использовать data.table

library(data.table)
setDT(df)[, Date := as.IDate(Date, "%m/%d/%Y")
           ][, monthly.running.sum :=  cumsum(score),format(Date, "%Y-%m")][]
#          Date score monthly.running.sum
# 1: 2019-10-02     7                   7
# 2: 2019-10-09     6                  13
# 3: 2019-10-16    12                  25
# 4: 2019-10-23     2                  27
# 5: 2019-10-30    13                  40
# 6: 2019-11-06     2                   2
# 7: 2019-11-13     4                   6
# 8: 2019-11-20    15                  21
# 9: 2019-11-27    16                  37
#10: 2019-12-04     4                   4
#11: 2019-12-11    24                  28
#12: 2019-12-18    28                  56
#13: 2019-12-25     8                  64
#14: 2020-01-01     1                   1
#15: 2020-01-08    15                  16
#16: 2020-01-15     9                  25
#17: 2020-01-22     8                  33

данные

df <- structure(list(Date = c("10/2/2019", "10/9/2019", "10/16/2019", 
"10/23/2019", "10/30/2019", "11/6/2019", "11/13/2019", "11/20/2019", 
"11/27/2019", "12/4/2019", "12/11/2019", "12/18/2019", "12/25/2019", 
"1/1/2020", "1/8/2020", "1/15/2020", "1/22/2020"), score = c(7L, 
6L, 12L, 2L, 13L, 2L, 4L, 15L, 16L, 4L, 24L, 28L, 8L, 1L, 15L, 
9L, 8L)), row.names = c(NA, -17L), class = "data.frame")
...