根据逻辑条件将值求和并移动到最近的较小日期

Sum and shift values to nearest smaller date based on logical condition

我有一个数据集 testdf(dput 下面)看起来像这样:

head(testdf)
#        Date ID Value InCalendar
#1 2014-01-01  A     0       TRUE
#2 2014-01-02  A    18       TRUE
#3 2014-01-03  A     0       TRUE
#4 2014-01-04  A    10      FALSE
#5 2014-01-05  A     0      FALSE
#6 2014-01-06  A     6       TRUE
# ... 

最终,我想删除 testdf$InCalendarFALSE 的所有行。但是在删除这些行之前,我想将 InCalendarFALSE 的那些 Value 条目移动到最近的日期,其中 InCalendarTRUE 并且日期是在 InCalendar 中具有 FALSE 条目的行之前。

整个操作需要ID组完成。

有一个有效的假设,即 InCalendar 中的第一个条目始终是 TRUE,并且 InCalendar 条目和 Date 在所有 [=23] 中都是相同的=]s.

请注意,InCalendar 中的 FALSE 个条目可能会在工作日或周末连续多次出现 - 没有固定的模式。


这是我想要的,但感觉笨拙而且很长:

library(dplyr)
testdf %>%
  group_by(ID) %>% 
  group_by(InCalendar, grp = cumsum(c(0L, diff(InCalendar)) == 1L), add = TRUE) %>% 
  mutate(Value = ifelse(InCalendar, Value, sum(Value))) %>%
  group_by(ID, grp) %>%
  mutate(Value = ifelse(lead(InCalendar), Value, Value + lead(Value, default = 0))) %>% 
  ungroup() %>% 
  filter(InCalendar) %>% 
  select(-grp)

#Source: local data frame [48 x 4]
#
#         Date ID Value InCalendar
#1  2014-01-01  A     0       TRUE
#2  2014-01-02  A    18       TRUE
#3  2014-01-03  A    10       TRUE
#4  2014-01-06  A     6       TRUE
#5  2014-01-07  A    10       TRUE
#6  2014-01-08  A     6       TRUE
#7  2014-01-09  A     9       TRUE
#8  2014-01-10  A    20       TRUE
#9  2014-01-14  A    10       TRUE
#10 2014-01-15  A     8       TRUE
#..        ... ..   ...        ...

我的问题是如何使用 base R 或 dplyr 或 data.table 以更简洁的方式产生相同的结果。我认为这可能是 data.table 的滚动连接功能的一个用例,但我对此不是很熟悉,因此非常感谢任何建议。


这是我的测试数据dput:

testdf <- structure(list(Date = structure(c(16071, 16072, 16073, 16074, 
16075, 16076, 16077, 16078, 16079, 16080, 16081, 16082, 16083, 
16084, 16085, 16086, 16087, 16088, 16089, 16090, 16091, 16092, 
16093, 16094, 16095, 16071, 16072, 16073, 16074, 16075, 16076, 
16077, 16078, 16079, 16080, 16081, 16082, 16083, 16084, 16085, 
16086, 16087, 16088, 16089, 16090, 16091, 16092, 16093, 16094, 
16095, 16071, 16072, 16073, 16074, 16075, 16076, 16077, 16078, 
16079, 16080, 16081, 16082, 16083, 16084, 16085, 16086, 16087, 
16088, 16089, 16090, 16091, 16092, 16093, 16094, 16095), class = "Date"), 
    ID = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
    2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 
    3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 
    3L, 3L, 3L, 3L, 3L), .Label = c("A", "B", "C"), class = "factor"), 
    Value = c(0L, 18L, 0L, 10L, 0L, 6L, 10L, 6L, 9L, 0L, 13L, 
    0L, 7L, 10L, 8L, 3L, 0L, 20L, 0L, 7L, 5L, 4L, 6L, 0L, 12L, 
    0L, 476L, 48L, 470L, 0L, 166L, 222L, 220L, 219L, 32L, 454L, 
    0L, 231L, 195L, 205L, 193L, 36L, 474L, 0L, 258L, 239L, 214L, 
    203L, 29L, 438L, 0L, 98L, 14L, 96L, 0L, 36L, 58L, 46L, 38L, 
    5L, 90L, 0L, 51L, 49L, 54L, 50L, 7L, 108L, 0L, 55L, 45L, 
    48L, 35L, 6L, 86L), InCalendar = c(TRUE, TRUE, TRUE, FALSE, 
    FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, 
    TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, 
    TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, 
    TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, 
    TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, 
    FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, 
    TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, 
    FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)), class = "data.frame", row.names = c(NA, 
-75L), .Names = c("Date", "ID", "Value", "InCalendar"))

我认为你让这比现在更难了:

dt = as.data.table(testdf)

dt[, Value := sum(Value), by = list(ID, cumsum(InCalendar))][(InCalendar)]
#         Date ID Value InCalendar
#1: 2014-01-01  A     0       TRUE
#2: 2014-01-02  A    18       TRUE
#3: 2014-01-03  A    10       TRUE
#4: 2014-01-06  A     6       TRUE
#5: 2014-01-07  A    10       TRUE
#...

请注意,这将更改原始 dt 中的值,如果不需要,请使用 copy。并且不要忘记在开始之前按日期排序(您的示例数据已经排序)。此外,由于您说您的 InCalendar 始终以每个组中的 TRUE 开头,如果您的数据按 IDDate 排序,如示例所示,您不需要实际上不需要按 ID.

分组