r - 将连续时间序列数据分块为多个时间段和多个组的非连续时间窗口
问题描述
我有两个数据集:df1
包含代表峰值活动的时间窗口id
。这些是非连续的时间序列,每个 有多个窗口(事件)id
,即每个窗口id
有多个活动高峰期。下面是我编造的一个可重现的例子,但不是真实数据(注意:我根据下面的评论更新了数据)。
df1<-data.frame(start_date=seq(as.POSIXct("2014-09-04 00:00:00"), by = "hour", length.out = 10),
end_date=seq(as.POSIXct("2014-09-04 05:00:00"), by = "hour", length.out = 10),
values=runif(20,10,50),id=rep(seq(from=1,to=5,by=1),2))
df2
是一组连续的活动时间序列id
。我想对(按)date.date
中的每个条目/峰值活动进行子集化。df1
id
date1<-data.frame(date=seq(as.POSIXct("2012-09-04 02:00:00"), by = "hour", length.out = 20), id=1)
date2<-data.frame(date=seq(as.POSIXct("2014-09-03 07:00:00"), by = "hour", length.out = 20),id=2)
date3<-data.frame(date=seq(as.POSIXct("2014-09-04 01:00:00"), by = "hour", length.out = 20),id=3)
df2<-data.frame(date=rbind(date1,date2,date3),values=runif(60,50,90))
df2
目标:仅在start_time
to end_time
in之间(按 id)对连续时间序列进行子集化df1
,并保留values
每个 df 的字段。这里有一个类似的问题,但在那种情况下,时间段是静态的并且是已知的。考虑到每个 id 的多个事件,我正在努力解决如何做到这一点。
解决方案
data.table
具有功能foverlaps
,可以满足您的需要。
foverlaps
代表“快速重叠连接”。该函数采用两个数据帧(在本例中为 data.tables)并返回连接。
两个 data.tables 都需要一个start
和end
列来计算重叠。由于您在 中只有一个日期列df2
,因此我只是创建一个dummy_end
与 中日期相同date.date
的列df2
。
您可以使用选项by.x
和 by.y
来指示start
和end
列。但是,您也可以使用该setkey
语句使用键来执行此操作。的最后两个元素setkey
必须是start
和end
列。使用的优点setkey
是您可以添加其他键(在开始和结束之前)以进一步过滤连接。在本例中,我还将为id
.
[, dummy_end := NULL]
用于删除 dummy_end 列。
library(data.table)
dt1 <- data.table(df1)
dt2 <- data.table(df2)
setnames(dt2,"date.id","id") #change name to "id" for easier comparison
dt2[, dummy_end := date.date] #create dumme end date column
setkey(dt1, id, start_date, end_date)
setkey(dt2, id, date.date, dummy_end)
foverlaps(dt2, dt1, nomatch = NULL)[, dummy_end := NULL]
在性能方面,foverlaps
比dplyr
针对这个特定问题的速度略快(但仍比基础 R 慢)。事实上,您可以在下面看到我重新运行 Paul 的微基准以添加data.table
. 但是,我喜欢简洁明了的data.table
语法。
数据和基准
library(dplyr)
library(microbenchmark)
library(data.table)
df1 <- data.frame(start_date=seq(as.POSIXct("2014-09-04 00:00:00"),
by = "hour", length.out = 10),
end_date=seq(as.POSIXct("2014-09-04 05:00:00"),
by = "hour", length.out = 10),
values=runif(20,10,50),id=rep(seq(from=1,to=5,by=1),2))
date1 <-data.frame(date = seq(as.POSIXct("2012-09-04 02:00:00"),
by = "hour",
length.out = 20), id = 1)
date2 <-data.frame(date = seq(as.POSIXct("2014-09-03 07:00:00"),
by = "hour",
length.out = 20),id = 2)
date3 <-data.frame(date = seq(as.POSIXct("2014-09-04 01:00:00"),
by = "hour", length.out = 20),id = 3)
df2 <-data.frame(date = rbind(date1,date2,date3), values = runif(60,50,90))
dt1 <- data.table(df1)
dt2 <- data.table(df2)
setnames(dt2,"date.id","id") #change name to "id" for easier comparison
dt2[, dummy_end := date.date] #create dumme end date column
setkey(dt1, id, start_date, end_date)
setkey(dt2, id, date.date, dummy_end)
dplyr2 <- function(df1, df2) {
df <- left_join(df1, df2, by = c("id" = "date.id")) %>%
group_by(id) %>%
filter(date.date >= start_date &
date.date <= end_date) %>%
select(start_date,
end_date,
x_values = values.x,
y_values = values.y,
id,
date.date) %>%
ungroup()
}
baseR2 <- function(df1, df2) {
df_bR <- merge(df1, df2, by.x = "id", by.y = "date.id")
df_bR <- subset(
df_bR,
subset = df_bR$date.date >= df_bR$start_date &
df_bR$date.date <= df_bR$end_date,
select = c(start_date, end_date, values.x, values.y, id, date.date)
)
}
data.table2 <- function(dt1, dt2) {
foverlaps(dt2, dt1,nomatch = NULL)[, dummy_end := NULL]
}
microbenchmark(baseR = baseR2(df1, df2),
dplyr = dplyr2(df1, df2),
data.table=data.table2(dt1, dt2),
times = 50)
Unit: milliseconds
expr min lq mean median uq max neval
baseR 1.2328 1.3973 1.632302 1.4713 1.5596 7.0549 50
dplyr 8.2126 8.6865 9.628708 8.8531 9.2621 19.5883 50
data.table 6.6931 7.3884 7.974340 7.9406 8.3973 11.0060 50
推荐阅读
- javascript - 如何将 Firefox 扩展中的数据存储到我的本地系统磁盘中
- xcode - Xcode中是否提供此类工具
- android - 在后台每隔一分钟重复一个功能,颤动
- javascript - 重新排列json生成的表格列顺序
- javascript - 使用 Selenium 从远程页面检索隐藏文本
- python - Python - 有没有办法对包含日期和时间的字符串列表进行排序
- javascript - 用户输入后没有警报输出
- html - 单击整个输入类型时间 HTML5 字段时不仅在小时钟图标上显示时间选择器
- titanium - Appcelerator:如何更改钛合金的 IOS 模拟器?
- postgresql - 如何检查多线串是否真的是多线串?