根据时间区间 + 或 - 日期列表筛选数据框的子集

3

我有一个包含20,000个观测值的数据框,按唯一ID分组并按小时计算。 我还有一个日期列表(每个日期都出现在数据框中)。 我正在尝试将日期与数据框匹配,然后提取与匹配日期相差+或-某个时间间隔的日期时间。 例如,在以下数据框中:

 setAs("character","myDate", function(from) as.POSIXct(from, "%m/%e/%Y    %H:%M", tz="UTC")) 
# previous function formats date input as UTC 
   df <- read.table(textConnection("datetimeUTC id  value
                             '5/1/2013 5:00'    153 0.53
                            '5/1/2013 6:00'     153 0.46
                            '5/1/2013 7:00'     153 0.53
                            '5/1/2013 8:00'     153 0.46
                            '5/1/2013 9:00'     153 0.44
                            '5/1/2013 10:00'    153 0.48
                            '5/1/2013 11:00'    153 0.49
                            '5/1/2013 12:00'    153 0.49
                            '5/1/2013 13:00'    153 0.51
                            '5/1/2013 14:00'    153 0.53
                            '11/24/2013 9:00'   154 0.45
                            '11/24/2013 10:00'  154 0.46
                            '11/24/2013 11:00'  154 0.49
                            '11/24/2013 12:00'  154 0.55
                            '11/24/2013 13:00'  154 0.61
                            '11/24/2013 14:00'  154 0.7
                            '11/24/2013 15:00'  154 0.74
                            '11/24/2013 16:00'  154 0.78
                            '11/24/2013 17:00'  154 0.77
                            '11/24/2013 18:00'  154 0.79
                            '8/2/2015 1:00'     240 0.2
                            '8/2/2015 2:00'     240 0.2
                            '8/2/2015 3:00'     240 0.2
                            '8/2/2015 4:00'     240 0.22
                            '8/2/2015 5:00'     240 0.22
                            '8/2/2015 6:00'     240 0.27
                            '8/2/2015 7:00'     240 0.23
                            '8/2/2015 8:00'     240 0.21
                            '8/2/2015 9:00'     240 0.22
                            '8/2/2015 10:00'    240 0.22
                            '8/2/2015 11:00'    240 0.21
                            '8/2/2015 12:00'    240 0.21
                            '8/2/2015 13:00'    240 0.21
                            '8/2/2015 14:00'    240 0.22
                            '8/2/2015 15:00'    240 0.24
                            '8/2/2015 16:00'    240 0.25
                            '8/2/2015 17:00'    240 0.12
                            '8/2/2015 18:00'    240 0.32
                            "), header=TRUE, colClasses=c("myDate", "character", "numeric"))

我想提取与此关键字匹配的日期时间前后两个小时内的每个ID的所有观察结果:

  key <-read.table(textConnection("
     datetimeUTC        id
    '5/1/2013 9:00'     153
    '11/24/2013 14:00'  154
    '8/2/2015 5:00'     240
    '8/2/2015 15:00'        240"), header=TRUE, colClasses=c("myDate",  "character"))

期望的结果应如下所示:
  result <- read.table(textConnection("datetimeUTC  id  value
                            '5/1/2013 7:00'     153 0.53
                            '5/1/2013 8:00'     153 0.46
                            '5/1/2013 9:00'     153 0.44
                            '5/1/2013 10:00'    153 0.48
                            '5/1/2013 11:00'    153 0.49
                            '11/24/2013 12:00'  154 0.55
                            '11/24/2013 13:00'  154 0.61
                            '11/24/2013 14:00'  154 0.7
                            '11/24/2013 15:00'  154 0.74
                            '11/24/2013 16:00'  154 0.78
                            '8/2/2015 3:00'     240 0.2
                            '8/2/2015 4:00'     240 0.22
                            '8/2/2015 5:00'     240 0.22
                            '8/2/2015 6:00'     240 0.27
                            '8/2/2015 7:00'     240 0.23
                            '8/2/2015 13:00'    240 0.21
                            '8/2/2015 14:00'    240 0.22
                            '8/2/2015 15:00'    240 0.24
                            '8/2/2015 16:00'    240 0.25
                            '8/2/2015 17:00'    240 0.12
                            "), header=TRUE, colClasses=c("myDate", "character", "numeric"))

看起来是一个简单的任务,但我似乎无法得到我想要的结果。我尝试了几个方法。

result <-df[which(df$id == key$id &(df$datetimeUTC >= key$datetimeUTC -2*60*60 |df$datetimeUTC <= key$datetimeUTC + 2*60*60 )),]

 library(data.table)
  dt <- setDT(df)
  dt[dt$datetimeUTC %between% c(dt$datetimeUTC - 2*60*60,dt$datetimeUTC +   2*60*60) ]

对于ID 153,在你的输出中为什么有8:00?难道不应该只是7:00和9:00,因为你想要“2小时之前或之后”吗? - CuriousBeing
我进行了编辑以更清楚地表达我的意思,我想提取与匹配日期相差加减2小时之间的所有日期。 - Wyldsoul
3个回答

4

以下是几种data.table的解决方案:

1. 笛卡尔积连接

将所有内容连接在一起,然后过滤掉不需要的部分。

library(data.table)
dt <- as.data.table(df)
dt_key <- as.data.table(key)

dt_join <- dt[ dt_key, on="id", allow.cartesian=T][difftime(i.datetimeUTC, datetimeUTC, units="hours") <= 2 & difftime(i.datetimeUTC, datetimeUTC, units="hours") >= -2]

 #          datetimeUTC  id value       i.datetimeUTC
 #1: 2013-05-01 07:00:00 153  0.53 2013-05-01 09:00:00
 #2: 2013-05-01 08:00:00 153  0.46 2013-05-01 09:00:00
 #3: 2013-05-01 09:00:00 153  0.44 2013-05-01 09:00:00
 #4: 2013-05-01 10:00:00 153  0.48 2013-05-01 09:00:00
   ... etc

2. 每个I的条件

利用我之前提出问题的答案,指定在连接中EACHI必须满足的j条件。

dt[ dt_key, 
        { idx = difftime(i.datetimeUTC, datetimeUTC, units="hours") <= 2 & difftime(i.datetimeUTC, datetimeUTC, units="hours") >= -2
        .(datetime = datetimeUTC[idx],
            value = value[idx])
            },
        on=c("id"),
        by=.EACHI]

谢谢tspig,这两个解决方案在我的样本数据上都有效,我明天会在工作中尝试它们。 - Wyldsoul
@Wyldsoul - 没问题。根据您的数据大小,cartesian连接可能会使用大量RAM,但如果不是这样,它应该运行得更快。 - tospig
1
两种解决方案在我的完整数据集上同样有效(不到1秒),但是笛卡尔联接还可以为每个时间间隔分配唯一的i.datetimeUTC变量,这对我很有用。再次感谢! - Wyldsoul
@tospig,非常好的回答(已经点赞)。只是想让您知道最近在data.table中有了新的发展,包括非等值连接。我已经提供了一个答案。干杯。 - Arun
1
@Arun - 谢谢:我一直在观察/使用开发版本,非等值连接是一个很棒的功能。感谢您的实现。 - tospig

4

@Tospig的解决方案非常好。但是现在,在当前data.table开发版本中实现了non-equi连接功能,这变得非常简单:

require(data.table) # v1.9.7+
setDT(df)
setDT(key) ## converting data.frames to data.tables by reference
df[key, .(x.datetimeUTC, i.datetimeUTC, id, value), 
  on=.(datetimeUTC >= d1, datetimeUTC <= d2), nomatch=0L]

就是这样了。

请注意,这是一个 条件 连接而且是直接进行的,因此既节省内存(与执行笛卡尔积并根据条件过滤相比),又快速(因为使用修改后的二进制搜索来获取与给定条件匹配的行,而不是使用 @tospig's 回路变量中展示的 by=.EACHI 循环变量)。

有关devel版本的安装说明,请单击此处


就我所见,'d1'和'd2'没有被定义。虽然这很琐碎,但也许您可以在您的答案中添加它们的创建,使其成为一个漂亮、完整的规范 ;) - Henrik

1
使用,你可以做到以下事情:

library(lubridate)
do.call(rbind, apply(key,1, FUN=function(k) 
      df[df$id == k['id'] &
      df$datetimeUTC >= ymd_hms( k['datetimeUTC']) -hours(2) &
      df$datetimeUTC <= ymd_hms(k['datetimeUTC']) +hours(2),]))

 1: 2013-05-01 07:00:00 153  0.53
 2: 2013-05-01 08:00:00 153  0.46
 3: 2013-05-01 09:00:00 153  0.44
 4: 2013-05-01 10:00:00 153  0.48
 5: 2013-05-01 11:00:00 153  0.49
 6: 2013-11-24 12:00:00 154  0.55
 7: 2013-11-24 13:00:00 154  0.61
 8: 2013-11-24 14:00:00 154  0.70
 9: 2013-11-24 15:00:00 154  0.74
10: 2013-11-24 16:00:00 154  0.78
11: 2015-08-02 03:00:00 240  0.20
12: 2015-08-02 04:00:00 240  0.22
13: 2015-08-02 05:00:00 240  0.22
14: 2015-08-02 06:00:00 240  0.27
15: 2015-08-02 07:00:00 240  0.23
16: 2015-08-02 13:00:00 240  0.21
17: 2015-08-02 14:00:00 240  0.22
18: 2015-08-02 15:00:00 240  0.24
19: 2015-08-02 16:00:00 240  0.25
20: 2015-08-02 17:00:00 240  0.12

谢谢HubertL,这似乎很有效,我明天会在完整的数据集上尝试一下。 - Wyldsoul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接