根据逻辑条件将值求和并移动到最近的较小日期
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$InCalendar
为 FALSE
的所有行。但是在删除这些行之前,我想将 InCalendar
是 FALSE
的那些 Value
条目移动到最近的日期,其中 InCalendar
是 TRUE
并且日期是在 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
开头,如果您的数据按 ID
和 Date
排序,如示例所示,您不需要实际上不需要按 ID
.
分组
我有一个数据集 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$InCalendar
为 FALSE
的所有行。但是在删除这些行之前,我想将 InCalendar
是 FALSE
的那些 Value
条目移动到最近的日期,其中 InCalendar
是 TRUE
并且日期是在 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
开头,如果您的数据按 ID
和 Date
排序,如示例所示,您不需要实际上不需要按 ID
.