重叠连接以计算间隔内的平均值?

时间:2015-08-05 09:01:57

标签: r data.table dplyr

我经常需要在给定的时间间隔(“事件”)内平均时间序列数据,基本上就像被问到here一样。

正如答案中所建议的那样,我以'long'格式对我的数据使用SQL语句。这是一个例子:

#create dummy data frame
set.seed(1)
data <- data.frame(
  date = seq(from = as.POSIXct("2014-01-01 00:00"),
             to = as.POSIXct("2014-01-31 23:00"),
             by = 300),
  A = runif(8917),
  B = runif(8917),
  C = runif(8917),
  D = runif(8917)
)

#convert to long format
require(dplyr)
data <- data %>%
  gather(class,value,A:D)

# create dummy events
events <- data.frame(
  id = c("blue","red","green","yellow"),
  start = as.POSIXct(c("2014-01-03 13:00",
                       "2014-01-12 08:00",
                       "2014-01-18 10:00",
                       "2014-01-27 23:00")),
  stop = as.POSIXct(c("2014-01-03 19:00",
                       "2014-01-13 17:00",
                       "2014-01-20 10:00",
                       "2014-01-28 20:00"))
)


#average value within events, grouped by class
require(sqldf)
results <- sqldf("
     SELECT x.id, y.class, avg(y.value) AS mean 
     FROM events as x, data as y 
     WHERE y.date between x.start and x.stop 
     GROUP BY x.id, y.class
")

给出了所需的输出

       id class      mean
1    blue     A 0.4879129
2    blue     B 0.4945888
3    blue     C 0.5312504
4    blue     D 0.4968260
5   green     A 0.5235671
6   green     B 0.5030602
7   green     C 0.5071219
8   green     D 0.5002010
9     red     A 0.5122966
10    red     B 0.4767966
11    red     C 0.5032387
12    red     D 0.5018389
13 yellow     A 0.4727868
14 yellow     B 0.4626688
15 yellow     C 0.4930207
16 yellow     D 0.5184966

然而,由于我的真实数据很大(长格式可能达到数百万行),SQL操作需要相当长的时间。

有更有效的方法来执行此操作吗?我在data.table::foverlaps中遇到了问题,这被称为“重叠加入”,但我不完全明白这是否是我需要的。

如果有一种有效的方法将en'event'列添加到数据中,指示每一行(日期),它指向哪个事件,那么我可以使用dplyr比较SQL语句进行分组摘要。但我不知道该怎么做......

非常感谢专家的任何建议。

更新

正如评论中所建议的,我已经为我的SQL语句添加了索引的创建。不幸的是,这并没有为我的一个大的现实问题加速。计算仍需要约40分钟才能运行。

然后我复制粘贴了David提供的data.table解决方案,并且看到它在完全相同的真实数据集上运行不到1秒就给人留下了深刻的印象。

我仍然不明白它是如何以及为什么会这样做的,但是我花费一些时间学习data.table语法的动机肯定会增加很多。再次感谢!

2 个答案:

答案 0 :(得分:2)

这是一个可能的data.table::foverlaps解决方案

library(data.table)
setDT(data)[, `:=`(start = date, stop = date)]
setkey(setDT(events), start, stop)
foverlaps(data, events, nomatch = 0L)[, .(Mean = mean(value)), keyby = .(id, class)]
#         id class      Mean
#  1:   blue     A 0.4879129
#  2:   blue     B 0.4945888
#  3:   blue     C 0.5312504
#  4:   blue     D 0.4968260
#  5:  green     A 0.5235671
#  6:  green     B 0.5030602
#  7:  green     C 0.5071219
#  8:  green     D 0.5002010
#  9:    red     A 0.5122966
# 10:    red     B 0.4767966
# 11:    red     C 0.5032387
# 12:    red     D 0.5018389
# 13: yellow     A 0.4727868
# 14: yellow     B 0.4626688
# 15: yellow     C 0.4930207
# 16: yellow     D 0.5184966

这个逻辑对我来说非常简单。

  1. start内的stopdata列设置为重叠。
  2. key由相同列设置的events数据。
  3. 运行foverlaps并删除不匹配的时间间隔(nomatch = 0L)。
  4. mean(value)id
  5. 计算class

答案 1 :(得分:-1)

您最好将任务完全卸载到数据库中。看看RSQLite package。如果您的数据非常大,则将它们存储在SQLite等数据库中,并让数据库进行子集化和分组,这样可以提高任务的速度。我已经撰写了一些可能有用的帖子,一篇在writing to SQLite,一篇包含reading from SQLite部分。

您可能不希望这样做的一个原因是,如果您在许多数据集上重复此操作,因为在将数据写入数据库所花费的时间内,查询的速度提升将会丢失。