R:计算由id变量和基于时间的窗口分组的不规则时间序列的滚动总和

时间:2014-08-04 17:22:26

标签: r time-series grouped-table

我喜欢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")

3 个答案:

答案 0 :(得分:4)

我发现这也是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 :(得分:1)

我们假设一个区间由同一个人的两行定义。对于每个人,我们希望第一个这样的间隔(时间)至少为6小时,其中这两行和任何中间行的Value的总和小于30.如果不止一个这样的一个人任意选择一个人的第一个间隔。

这可以通过SQL中的三重连接来表示。内部选择选择包含间隔开始(a.DateTime),间隔结束(b.DateTime)和它们之间的行(c.DateTime)的所有行,按Person进行分组和Value上的间隔和求和,只要它跨越至少6 hours。然后外部选择仅保留total为&lt; 1}的行。 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) ) 

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

{{1}}

答案 2 :(得分:0)

从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

这些时间间隔用于在非等额联接中进行汇总。过滤结果的总值小于30,最后选择每个人的第一个匹配项。