使用有条件的累加器重置两个场景

问题描述

我有一个数据框,我试图在其中找到两件事情:1)事件的开始,以及2)事件的结束。事件的开始基于累积总阈值,而事件的结束取决于最后一行(大于0的值)与当前时间之间是否有5行(值为0)。

示例数据如下

# hourly time series
a <- seq(from=as.POSIXct("2012-06-01 0:00",tz="UTC"),to=as.POSIXct("2012-09-01 00:00",by="hour")  

# mock data 
b <- sample.int(10,2209,replace = TRUE)*sample(c(0,1),replace=TRUE,size=2209)

# mock time series data table
c <- data.table(a,b)
                        a b
   1: 2012-06-01 00:00:00 0
   2: 2012-06-01 01:00:00 0
   3: 2012-06-01 02:00:00 0
   4: 2012-06-01 03:00:00 7
   5: 2012-06-01 04:00:00 0
  ---                      
2205: 2012-08-31 20:00:00 8
2206: 2012-08-31 21:00:00 4
2207: 2012-08-31 22:00:00 2
2208: 2012-08-31 23:00:00 0
2209: 2012-09-01 00:00:00 0
---

我想根据累积总和为10的阈值(在b列中)确定时间序列内的事件。因此,当日期/时间的总和等于或大于10时,事件就会开始。

c$cumsum <- with(c,ave(b,cumsum(b == 0),FUN = cumsum))

                        a b cumsum
   1: 2012-06-01 00:00:00 0      0
   2: 2012-06-01 01:00:00 0      0
   3: 2012-06-01 02:00:00 0      0
   4: 2012-06-01 03:00:00 7      7
   5: 2012-06-01 04:00:00 0      0
  ---                             
2205: 2012-08-31 20:00:00 8      8
2206: 2012-08-31 21:00:00 4     12
2207: 2012-08-31 22:00:00 2     14
2208: 2012-08-31 23:00:00 0      0
2209: 2012-09-01 00:00:00 0      0

例如,在上面的代码中,由于b = 12的累积总和,因此事件将在2012-08-31 21:00:00开始。此外,尽管2012-08-31 22:00:00具有累计值为14,这不是事件的开始,因为该事件已在事件的前一小时开始(基于事件在cumsum => 10时开始的条件)。

我还需要找到事件的结束,这就是我遇到的问题。事件结束将在5个小时过去且没有任何值(即b列中有0的5行)时发生。然后我想创建一个仅由事件组成的数据框(即事件开始的日期/时间,以及同一事件结束的相应日期/时间)。看起来像(手动,伪造的示例):

# dataframe for event start,and the corresponding cumsum of b
              event_start cumsum_b
   1: 2012-06-01 00:00:00 12
   2: 2012-06-09 11:00:00 11
   3: 2012-06-15 02:00:00 10

# dataframe for event end
              event_end   b
   1: 2012-06-01 00:7:00  0
   2: 2012-06-09 18:00:00 0
   3: 2012-06-15 12:00:00 0

解决方法

library(tidyverse)

df <- tibble(
  a = seq.Date(from = as.Date('2020-01-01'),length.out = 20,by = "days"),b = c(0,7,8,12,14,3,0)
)

我们可以使用lag找到结尾。然后使用cumsumcummax创建重置累积总和。

events <-
  df %>%
  mutate(
    is_end = coalesce(b == 0 & lag(b) == 0 & lag(b,2) == 0 & lag(b,3) == 0 & lag(b,4) == 0 & lag(b,5) != 0,FALSE),cumsum = cumsum(b) - cummax(is_end * cumsum(b)),is_event = cumsum >= 10,start = is_event & !lag(is_event),end = !is_event & lag(is_event)
  )
events
#> # A tibble: 20 x 7
#>   a               b  is_end  cumsum is_event start end  
#>   <date>      <dbl>  <lgl>   <dbl>  <lgl>    <lgl> <lgl>
#> 1 2020-01-01      0  FALSE   0      FALSE    FALSE NA   
#> 2 2020-01-02      0  FALSE   0      FALSE    FALSE FALSE
#> 3 2020-01-03      0  FALSE   0      FALSE    FALSE FALSE
#> 4 2020-01-04      7  FALSE   7      FALSE    FALSE FALSE
#> 5 2020-01-05      0  FALSE   7      FALSE    FALSE FALSE
#> 6 2020-01-06      8  FALSE   15     TRUE     TRUE  FALSE
#> 7 2020-01-07     12  FALSE   27     TRUE     FALSE FALSE
#> 8 2020-01-08      0  FALSE   27     TRUE     FALSE FALSE
#> 9 2020-01-09      0  FALSE   27     TRUE     FALSE FALSE
#> 10 2020-01-10     0  FALSE   27     TRUE     FALSE FALSE
#> 11 2020-01-11     0  FALSE   27     TRUE     FALSE FALSE
#> 12 2020-01-12     0  TRUE    0      FALSE    FALSE TRUE 
#> 13 2020-01-13     0  FALSE   0      FALSE    FALSE FALSE
#> 14 2020-01-14    14  FALSE   14     TRUE     TRUE  FALSE
#> 15 2020-01-15     3  FALSE   17     TRUE     FALSE FALSE
#> 16 2020-01-16     0  FALSE   17     TRUE     FALSE FALSE
#> 17 2020-01-17     0  FALSE   17     TRUE     FALSE FALSE
#> 18 2020-01-18     0  FALSE   17     TRUE     FALSE FALSE
#> 19 2020-01-19     0  FALSE   17     TRUE     FALSE FALSE
#> 20 2020-01-20     0  TRUE    0      FALSE    FALSE TRUE 

然后,取出开始日期和结束日期。

tibble(
  event_start = events %>% filter(start) %>% pull(a),event_end = events %>% filter(end) %>% pull(a)
)
#> # A tibble: 2 x 2
#>   event_start event_end 
#>   <date>      <date>    
#> 1 2020-01-06  2020-01-12
#> 2 2020-01-14  2020-01-20

如果您不想手动指定滞后时间

find_end <- function(x,n) {
  is_n_consecutive_zeros <-
    map(0:(n-1),~lag(x,.)) %>%
    pmap_lgl(function(...) all(c(...) == 0))

  coalesce(is_n_consecutive_zeros & lag(x,n) != 0,FALSE)
}

df %>%
  mutate(
    is_end = find_end(b,5),end = !is_event & lag(is_event)
  )
#> # A tibble: 20 x 7
#>    a              b is_end cumsum is_event start end  
#>    <date>     <dbl> <lgl>   <dbl> <lgl>    <lgl> <lgl>
#>  1 2020-01-01     0 FALSE       0 FALSE    FALSE NA   
#>  2 2020-01-02     0 FALSE       0 FALSE    FALSE FALSE
#>  3 2020-01-03     0 FALSE       0 FALSE    FALSE FALSE
#>  4 2020-01-04     7 FALSE       7 FALSE    FALSE FALSE
#>  5 2020-01-05     0 FALSE       7 FALSE    FALSE FALSE
#>  6 2020-01-06     8 FALSE      15 TRUE     TRUE  FALSE
#>  7 2020-01-07    12 FALSE      27 TRUE     FALSE FALSE
#>  8 2020-01-08     0 FALSE      27 TRUE     FALSE FALSE
#>  9 2020-01-09     0 FALSE      27 TRUE     FALSE FALSE
#> 10 2020-01-10     0 FALSE      27 TRUE     FALSE FALSE
#> 11 2020-01-11     0 FALSE      27 TRUE     FALSE FALSE
#> 12 2020-01-12     0 TRUE        0 FALSE    FALSE TRUE 
#> 13 2020-01-13     0 FALSE       0 FALSE    FALSE FALSE
#> 14 2020-01-14    14 FALSE      14 TRUE     TRUE  FALSE
#> 15 2020-01-15     3 FALSE      17 TRUE     FALSE FALSE
#> 16 2020-01-16     0 FALSE      17 TRUE     FALSE FALSE
#> 17 2020-01-17     0 FALSE      17 TRUE     FALSE FALSE
#> 18 2020-01-18     0 FALSE      17 TRUE     FALSE FALSE
#> 19 2020-01-19     0 FALSE      17 TRUE     FALSE FALSE
#> 20 2020-01-20     0 TRUE        0 FALSE    FALSE TRUE