【问题标题】:Cumulative sum by rolling window of time滚动时间窗口累计
【发布时间】:2026-01-17 08:55:01
【问题描述】:

我有关于咒语的开始和结束日期的数据,按人员标识符。

temp <- structure(list(id = structure(c(1L, 1L, 1L, 2L, 2L, 2L), .Label = c("1", 
"2"), class = "factor"), spell = c(1L, 2L, 3L, 1L, 2L, 3L), date1 = structure(c(14611, 
14654, 15141, 14853, 14867, 14975), class = "Date"), date2 = structure(c(14612, 
14656, 15142, 14862, 14872, 14976), class = "Date")), class = "data.frame", .Names = c("id", 
"spell", "date1", "date2"), row.names = c(NA, -6L))

我想计算过去一年内咒语的滚动总天数(由 365 天窗口定义),以便每一行获得当前咒语之前包含在咒语中的总天数, 一年内。

所以在我的示例中,人 1 有 3 个法术,其中两个发生在一年的窗口内(第 1 行和第 2 行)。第一个法术没有先验,因此 days.observed.in.past.yr 为 0。第二个法术(第 1 行)具有 1 天的长度,因此 days.observed.in.past.yr 为 0。

第 2 个人有三个咒语,都发生在一年内。对于咒语 2,人 2,前一个咒语是 9 天,因此 days.observed.in.past.yr 是 9。然后对于咒语 3,有两个前咒语,days.observed.in.past.yr 是 14 (9+5)。这将继续为当前行的 365 天窗口内添加尽可能多的法术。

id  spell   date1       date2       days.observed.in.past.yr
1     1     2010-01-02 2010-01-03   0
1     2     2010-02-14 2010-02-16   1
1     3     2011-06-16 2011-06-17   0
2     1     2010-09-01 2010-09-10   0
2     2     2010-09-15 2010-09-20   9
2     3     2011-01-01 2011-01-02   14

但是,除了计算拼写长度和一年前的日期等琐碎的事情之外,我不知道该怎么做。我发现的最接近的类似问题是rgolf:rolling window,但我不知道如何将其应用于我的问题。

谁能帮忙?

【问题讨论】:

    标签: r


    【解决方案1】:

    这是使用dplyr 的一种方法:

    require(dplyr)
    
    temp %>%
      mutate(year1 = format(date1, "%Y"),
             year2 = format(date2, "%Y")) %>%
      group_by(id) %>%
      mutate(count = ifelse(lag(year1, 1, default = 0) == year1, lag(date2, 1) - lag(date1,1), 0 )) %>%
      select(-c(year1, year2))
    
    #Source: local data frame [5 x 4]
    #Groups: id
    #
    #  id      date1      date2 count
    #1  1 2010-01-02 2010-01-03     0
    #2  1 2010-02-14 2010-02-16     1
    #3  1 2011-06-16 2011-06-17     0
    #4  2 2010-09-01 2010-09-10     0
    #5  2 2010-09-15 2010-09-20     9
    

    评论后编辑 1

    要使用 365 天“滚动”窗口执行此操作,您可以使用以下方法:

    temp %>%
      group_by(id) %>%
      mutate(count = ifelse(date1 - lag(date1, 1, default = 0) <= 365, lag(date2, 1) - lag(date1,1), 0))
    

    示例数据的结果与上述相同。

    编辑 2

    我再次考虑了这一点,并怀疑第一次编辑实际上是否按预期运行,因为它只查看前一行以检查该行是否比当前 date1 早 365 天。所以我想出了另一个版本,它查看每个 id 的所有行,创建 365 天窗口内的数据组,然后总结日期差异 - 也许这就是你想要的。

    df %>%
      group_by(id) %>%
      arrange(id, date1) %>%
      mutate(delta = floor(c(0, diff(date1)) / 365),
             delta = cumsum(delta)) %>%
      group_by(delta, add = TRUE) %>%
      mutate(count = cumsum(as.numeric(date2-date1)) - (date2 - date1)) %>%
      ungroup() %>%
      select(-delta)
    

    我不太清楚的问题是您是否只想总结前 1 行中的日期差异(如果它存在并且小于 365 天,在这种情况下,编辑 1 应该可以工作)还是做您想总结 所有 早于 365 天之前的差异(在这种情况下,编辑 2 应该可以工作)。

    【讨论】:

    • 谢谢,这涵盖了一个日历年窗口。是否可以使用 365 天窗口?也许我应该再次编辑我的问题。
    • 当然可以。但它究竟是如何定义的呢?使用date1date2?
    【解决方案2】:

    这可以通过runner 包来实现。 OP 需要过去 365 天的滚动总和,不包括当前观察。 sum_run 计算 365 天期间 date2 - date1 的总和。要排除当前行,可以减去 - spell_days 或使用 lag = 1(排除当前日期)。

    library(dplyr)
    library(runner)
    
    temp %>%
      group_by(id) %>%
      mutate(
    
        spell_days = date2 - date1,
    
        days.observed.in.past.yr = sum_run(
          x = spell_days, 
          k = 365, 
          idx = date1
        ) - spell_days
    
      )
    
    #   id    spell date1      date2      spell_days days.observed.in.past.yr
    #   <fct> <int> <date>     <date>     <drtn>     <drtn>                  
    # 1 1         1 2010-01-02 2010-01-03 1 days      0 days                 
    # 2 1         2 2010-02-14 2010-02-16 2 days      1 days                 
    # 3 1         3 2011-06-16 2011-06-17 1 days      0 days                 
    # 4 2         1 2010-09-01 2010-09-10 9 days      0 days                 
    # 5 2         2 2010-09-15 2010-09-20 5 days      9 days                 
    # 6 2         3 2011-01-01 2011-01-02 1 days     14 days 
    
    

    【讨论】: