首页 > 解决方案 > 将连续时间序列数据分块为多个时间段和多个组的非连续时间窗口

问题描述

我有两个数据集: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中的每个条目/峰值活动进行子集化。df1id

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_timeto end_timein之间(按 id)对连续时间序列进行子集化df1,并保留values每个 df 的字段。这里有一个类似的问题,但在那种情况下,时间段是静态的并且是已知的。考虑到每个 id 的多个事件,我正在努力解决如何做到这一点。

标签: rgroup-bytime-seriesmany-to-manysubset

解决方案


data.table具有功能foverlaps,可以满足您的需要。

foverlaps代表“快速重叠连接”。该函数采用两个数据帧(在本例中为 data.tables)并返回连接。

两个 data.tables 都需要一个startend列来计算重叠。由于您在 中只有一个日期列df2,因此我只是创建一个dummy_end与 中日期相同date.date的列df2

您可以使用选项by.xby.y来指示startend列。但是,您也可以使用该setkey语句使用键来执行此操作。的最后两个元素setkey必须是startend列。使用的优点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]

在性能方面,foverlapsdplyr针对这个特定问题的速度略快(但仍比基础 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

推荐阅读