【问题标题】:R: Compute a rolling sum on irregular time series grouped by id variables with time-based windowR:计算不规则时间序列的滚动和,这些时间序列由 id 变量分组,具有基于时间的窗口
【发布时间】:2014-09-27 06:03:53
【问题描述】:

我喜欢 R,但有些问题实在是太难了。

挑战是在基于时间的窗口大于或等于 6 小时的不规则时间序列中找到滚动和小于 30 的第一个实例。我有这个系列的样本

Row Person  DateTime    Value
1   A   2014-01-01 08:15:00 5
2   A   2014-01-01 09:15:00 5
3   A   2014-01-01 10:00:00 5
4   A   2014-01-01 11:15:00 5
5   A   2014-01-01 14:15:00 5
6   B   2014-01-01 08:15:00 25
7   B   2014-01-01 10:15:00 25
8   B   2014-01-01 19:15:00 2
9   C   2014-01-01 08:00:00 20
10  C   2014-01-01 09:00:00 5
11  C   2014-01-01 13:45:00 1
12  D   2014-01-01 07:00:00 1
13  D   2014-01-01 08:15:00 13
14  D   2014-01-01 14:15:00 15

For Person A, Rows 1 & 5 create a minimum 6 hour interval with a running sum of 25 (which is less than 30).
For Person B, Rows 7 & 8 create a 9 hour interval with a running sum of 27 (again less than 30).
For Person C, using Rows 9 & 10, there is no minimum 6 hour interval (it is only 5.75 hours) although the running sum is 26 and is less than 30.
For Person D, using Rows 12 & 14, the interval is 7.25 hours but the running sum is 30 and is not less than 30.

给定 n 个观察值,必须比较 n*(n-1)/2 个区间。例如,当 n=2 时,只需要计算 1 个区间。对于 n=3,有 3 个区间。以此类推。

我假设这是子集和问题的一种变体 (http://en.wikipedia.org/wiki/Subset_sum_problem)

虽然可以对数据进行排序,但我怀疑这需要一个蛮力解决方案来测试每个间隔。

任何帮助将不胜感激。


编辑:这是 DateTime 列格式为 POSIXct 的数据:

df <- structure(list(Person = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 3L, 3L, 3L, 4L, 4L, 4L), .Label = c("A", "B", "C", "D"), class = "factor"), 
DateTime = structure(c(1388560500, 1388564100, 1388566800, 
1388571300, 1388582100, 1388560500, 1388567700, 1388600100, 
1388559600, 1388563200, 1388580300, 1388556000, 1388560500, 
1388582100), class = c("POSIXct", "POSIXt"), tzone = ""), 
Value = c(5L, 5L, 5L, 5L, 5L, 25L, 25L, 2L, 20L, 5L, 1L, 
1L, 13L, 15L)), .Names = c("Person", "DateTime", "Value"), row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", 
"14"), class = "data.frame")

【问题讨论】:

    标签: r time-series grouped-table


    【解决方案1】:

    我发现这在 R 中也是一个难题。所以我给它做了一个包!

    library("devtools")
    install_github("boRingTrees","mgahan")
    require(boRingTrees)
    

    当然,您必须正确计算出单位的上限。

    如果您有兴趣,这里还有一些文档。 https://github.com/mgahan/boRingTrees

    对于@beginneR 提供的数据df,您可以使用以下代码获得6 小时的滚动总和。

    require(data.table)
    setDT(df)
    df[ , roll := rollingByCalcs(df,dates="DateTime",target="Value",
                        by="Person",stat=sum,lower=0,upper=6*60*60)]
    
        Person            DateTime Value roll
     1:      A 2014-01-01 01:15:00     5    5
     2:      A 2014-01-01 02:15:00     5   10
     3:      A 2014-01-01 03:00:00     5   15
     4:      A 2014-01-01 04:15:00     5   20
     5:      A 2014-01-01 07:15:00     5   25
     6:      B 2014-01-01 01:15:00    25   25
     7:      B 2014-01-01 03:15:00    25   50
     8:      B 2014-01-01 12:15:00     2    2
     9:      C 2014-01-01 01:00:00    20   20
    10:      C 2014-01-01 02:00:00     5   25
    11:      C 2014-01-01 06:45:00     1   26
    12:      D 2014-01-01 00:00:00     1    1
    13:      D 2014-01-01 01:15:00    13   14
    14:      D 2014-01-01 07:15:00    15   28
    

    原来的帖子对我来说很不清楚,所以这可能不是他想要的。如果提供了具有所需输出的列,我想我可以提供更多帮助。

    【讨论】:

    • +1,太好了!您能否使用 OP 的示例数据演示您的函数输出的外观?
    • 没问题! @Dave M,你能在你的数据上使用dput 吗?或者您能否以某种方式使您的数据可重现?
    • 我已经创建了数据,所以我在问题中添加了一个 dput
    • 卓越。非常感谢。我现在不在工作,但整个周末都在梦想一个解决方案——但无济于事。这些序列在实际数据集中并没有那么长,但可能有 30,000 人需要处理。所以我会及时通知你。非常感谢。
    • 没问题!如果您需要加速(并且您正在运行 Mac OSX 或 Linux),那么您可以将内核从 1 更改为任意数量。
    【解决方案2】:

    我们假设一个区间由同一个人的两行定义。对于每个人,我们希望第一个这样的间隔(按时间)至少为 6 小时,其中这两行和任何中间行的 Value 之和小于 30。如果有多个这样的第一个间隔一个人随便挑一个。

    这可以用 SQL 中的三重连接来表示。内部选择挑选出由区间开始 (a.DateTime)、区间结束 (b.DateTime) 和它们之间的行 (c.DateTime) 组成的所有行,按 Person 和区间分组并在 @987654326 上求和@ 至少跨越 6 个hours。然后,外部选择仅保留 total 小于 30 的行,并且对于每个 Person 仅保留 DateTime 最小的行。如果Person 有多个第一行(时间方面),它会任意选择一个。

       library(sqldf)
    
       sqldf(
         "select Person, min(Datetime) DateTime, hours, total 
          from (select a.Person, 
              a.DateTime, 
              (b.Datetime - a.DateTime)/3600 hours, 
              sum(c.Value) total
              from DF a join DF b join DF c
              on a.Person = b.Person and a.Person = c.Person and hours >= 6
              and c.DateTime between a.DateTime and b.DateTime
              group by a.Person, a.DateTime, b.DateTime)
          where total < 30
          group by Person"
       )
    

    给予:

      Person            DateTime hours total
    1      A 2014-01-01 08:15:00  6.00    25
    2      B 2014-01-01 10:15:00  9.00    27
    3      D 2014-01-01 07:00:00  7.25    29
    

    注意:我们使用了这些数据:

    DF <- data.frame( Row = 1:14,
      Person = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
                 4L, 4L), .Label = c("A", "B", "C", "D"), class = "factor"),
      DateTime = structure(c(1388582100, 1388585700, 1388588400, 1388592900, 
                 1388603700, 1388582100, 1388589300, 1388621700, 1388581200, 
                 1388584800, 1388601900, 1388577600, 1388582100, 1388603700), 
                 class = c("POSIXct", "POSIXt"), tzone = ""),
      Value = c(5L, 5L, 5L, 5L, 5L, 25L, 25L, 2L, 20L, 5L, 1L, 1L, 13L, 15L) ) 
    

    【讨论】:

      【解决方案3】:

      截至 1.9.8 版(2016 年 11 月 25 日在 CRAN 上), 包已获得在非等值连接中聚合的能力。

      library(data.table)
      tmp <- setDT(df)[, CJ(start = DateTime, end = DateTime)[
        , hours := difftime(end, start, units = "hours")][hours >= 6], by = Person]
      df[tmp, on = .(Person, DateTime >= start, DateTime <= end), 
        .(hours, total = sum(Value)), by = .EACHI][
          total < 30, .SD[1L], by = Person]
      
         Person            DateTime      hours total
      1:      A 2014-01-01 08:15:00 6.00 hours    25
      2:      B 2014-01-01 10:15:00 9.00 hours    27
      3:      D 2014-01-01 07:00:00 7.25 hours    29
      

      tmp 包含每个人所有可能的 6 小时或更多小时的时间间隔。它是通过交叉连接CJ() 和后续过滤创建的:

      tmp
      
         Person               start                 end       hours
      1:      A 2014-01-01 08:15:00 2014-01-01 14:15:00  6.00 hours
      2:      B 2014-01-01 08:15:00 2014-01-01 19:15:00 11.00 hours
      3:      B 2014-01-01 10:15:00 2014-01-01 19:15:00  9.00 hours
      4:      D 2014-01-01 07:00:00 2014-01-01 14:15:00  7.25 hours
      5:      D 2014-01-01 08:15:00 2014-01-01 14:15:00  6.00 hours
      

      这些间隔被用于在非 equi 连接中聚合。结果被过滤为小于 30 的总值,最后选择每个人的第一次出现。

      【讨论】:

        猜你喜欢
        • 2013-04-04
        • 2012-05-20
        • 1970-01-01
        • 2017-03-17
        • 1970-01-01
        • 2019-12-26
        • 2019-03-30
        • 2018-04-02
        • 2012-05-12
        相关资源
        最近更新 更多