【问题标题】:overlap join to calculate averages within intervals?重叠连接计算区间内的平均值?
【发布时间】:2015-08-05 09:01:56
【问题描述】:

我经常必须在给定的时间间隔(“事件”)内平均时间序列数据,基本上就像here 所问的那样。

正如答案中所建议的,我以“长”格式对我的数据使用 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 语句进行比较。但我不知道该怎么做......

如果专家提出任何建议,我们将不胜感激。

更新

按照 cmets 中的建议,我已将索引的创建添加到我的 SQL 语句中。不幸的是,这并没有加快我在现实世界中的一个大问题的速度。计算仍然需要大约 40 分钟才能运行。

然后,我复制粘贴了由 David 提供的 data.table 解决方案,令人印象深刻的是,它在完全相同的真实数据集上运行时间不到 1 秒。

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

【问题讨论】:

  • 您是否尝试过使用data.table::foverlaps?您可以查看其文档或搜索 SO。有许多类似的 Q/A 说明了如何在不同情况下使用它。
  • 你看过?foverlaps吗?如果是这样,你不明白什么?
  • 我确实看过它,但我没有尝试使用它,因为我不确定它是否是我需要的。老实说,我对data.table语法有点害怕,我对编码不是很有经验……但如果这是我需要的功能,那么我会尝试理解它。谢谢。
  • 如果你被吓倒了,你可能应该坚持使用 dplyr / SQL。祝你好运!
  • 尝试添加索引:sqldf(c("CREATE INDEX a1 ON data(class, value)", "SELECT x.id, y.class, avg(y.value) AS mean FROM events as x, main.data as y WHERE y.date between x.start and x.stop GROUP BY x.id, y.class "))

标签: r data.table dplyr


【解决方案1】:

这是一个可能的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. data 中的startstop 列设置为重叠。
  2. keyevents数据集由相同的列。
  3. 运行 foverlaps 并删除不匹配的间隔 (nomatch = 0L)。
  4. 通过idclass 计算mean(value)

【讨论】:

  • 我想复制/粘贴不会像花时间浏览?foverlaps 中的示例那样令人生畏。我很确定这里也有类似的foverlaps() Q/A...
  • @Arun 所以你认为不值得在这里发布这个答案?
  • @David:我认为它值得发布,感谢您抽出宝贵时间。
【解决方案2】:

您最好将任务完全卸载到数据库中。看看RSQLite package。如果您的数据非常大,请将它们存储在 SQLite 之类的数据库中,并让数据库进行子集和分组,这应该会提高您的任务速度。我写了几篇可能会有所帮助的帖子,一篇在 writing to SQLite 上,另一篇在 reading from SQLite 上包含一个部分。

您可能不想这样做的一个原因是,如果您在大量数据集上重复此操作,因为在将数据写入数据库所花费的时间期间,您的查询速度改进将会丢失。

【讨论】:

  • 这与 OP 已经提供的sqldf 解决方案有何不同?另外,为什么这会更快?您的帖子谈论阅读和写作,但不是关于计算区间范围内的重叠。我很困惑这如何回答问题。
  • 感谢 Arun 澄清反对意见,很高兴知道原因。我没有比较 sqldf 和 SQLite 的基准,所以你可能是对的 - 也许没有速度差异。从我自己的工作中,我发现一个数据库(通常是 SQLite)比基础 R 更快并且能够处理更大的子集任务数据。我没有谈论计算重叠,因为 OP 的 SQL 可以在 dbSendQuery 中按原样使用() 命令。
猜你喜欢
  • 2020-04-03
  • 1970-01-01
  • 2021-12-31
  • 2017-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-01
相关资源
最近更新 更多