重叠连接以计算间隔内的平均值?
overlap join to calculate averages within intervals?
我经常需要在给定的时间间隔 ('events') 内对时间序列数据进行平均,基本上就像被问到的那样 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
但是由于我的真实数据很大(long format可以达到几百万行),SQL操作需要相当长的时间。
有没有更有效的方法来做这个操作?我偶然发现了 data.table::foverlaps
,它被称为 'overlap join',但我不完全明白这是否是我需要的。
如果有一种有效的方法可以将 en 'event' 列添加到数据中,指示每一行(日期)它属于哪个事件,那么我可以使用 dplyr 进行分组汇总,与SQL 语句。但是我不知道该怎么做...
我们将不胜感激专家的任何建议。
更新
正如评论中所建议的,我已将索引的创建添加到我的 SQL 语句中。不幸的是,这并没有加快解决我在现实世界中遇到的一个大问题的速度。计算仍然需要大约 40 分钟才能达到 运行。
然后我复制粘贴了 David 友善提供的 data.table 解决方案,看到它 运行 在完全相同的真实世界数据集上用了不到 1 秒的时间给我留下了深刻的印象。
我仍然不明白它是如何以及为什么这样做的,但我花一些时间学习 data.table 语法的动力肯定增加了很多。再次感谢!
您最好将任务完全卸载到数据库中。看看 RSQLite package. If your data are really large store them in a database like SQLite and get the db to do the subsetting and grouping this should improve the speed of your task. I've written a couple of posts that may help, one on writing to SQLite and one that includes a section on reading from SQLite.
您可能不想这样做的一个原因是,如果您在大量数据集上重复此操作,因为查询速度的提高将随着将数据写入数据库所花费的时间而丢失。
这是一个可能的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
我觉得逻辑很简单。
- 将
data
中的 start
和 stop
列设置为重叠。
key
events
数据集的相同列。
- 运行
foverlaps
并删除不匹配的间隔 (nomatch = 0L
).
- 通过
id
和class
计算mean(value)
我经常需要在给定的时间间隔 ('events') 内对时间序列数据进行平均,基本上就像被问到的那样 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
但是由于我的真实数据很大(long format可以达到几百万行),SQL操作需要相当长的时间。
有没有更有效的方法来做这个操作?我偶然发现了 data.table::foverlaps
,它被称为 'overlap join',但我不完全明白这是否是我需要的。
如果有一种有效的方法可以将 en 'event' 列添加到数据中,指示每一行(日期)它属于哪个事件,那么我可以使用 dplyr 进行分组汇总,与SQL 语句。但是我不知道该怎么做...
我们将不胜感激专家的任何建议。
更新
正如评论中所建议的,我已将索引的创建添加到我的 SQL 语句中。不幸的是,这并没有加快解决我在现实世界中遇到的一个大问题的速度。计算仍然需要大约 40 分钟才能达到 运行。
然后我复制粘贴了 David 友善提供的 data.table 解决方案,看到它 运行 在完全相同的真实世界数据集上用了不到 1 秒的时间给我留下了深刻的印象。
我仍然不明白它是如何以及为什么这样做的,但我花一些时间学习 data.table 语法的动力肯定增加了很多。再次感谢!
您最好将任务完全卸载到数据库中。看看 RSQLite package. If your data are really large store them in a database like SQLite and get the db to do the subsetting and grouping this should improve the speed of your task. I've written a couple of posts that may help, one on writing to SQLite and one that includes a section on reading from SQLite.
您可能不想这样做的一个原因是,如果您在大量数据集上重复此操作,因为查询速度的提高将随着将数据写入数据库所花费的时间而丢失。
这是一个可能的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
我觉得逻辑很简单。
- 将
data
中的start
和stop
列设置为重叠。 key
events
数据集的相同列。- 运行
foverlaps
并删除不匹配的间隔 (nomatch = 0L
). - 通过
id
和class
计算mean(value)