【问题标题】:Aggregate timeseries to length/N points将时间序列聚合为长度/N 个点
【发布时间】:2021-09-29 17:44:24
【问题描述】:

我有不同长度的时间序列(通常为 1 到 14 天),样本之间的间隔为 15 秒。我需要使用带有一些预定义函数(中值、最小值、最大值等)的聚合为每个组保留所有数据的 N 点。原因 - 我想在绘图上显示它并且太多的点会造成混乱,最好拆分数据并显示中位数,或短时间间隔的最小值/最大值。

问题是,如果我使用 lubridate ceiling_date 函数进行聚合,我的聚合期真的很有限。它仅支持“N hours”或“N mins”格式,甚至不支持“75m”或“1500s”或“1 hours 5 mins”。

但我真正需要的是 - 将我的数据长度除以 N 并以秒为单位计算聚合间隔。假设我的数据长度是 8.68 天 = 8.682460*60 = 749952 秒。假设我想要200分。我的聚合周期应该是 749952/200 = 3749,76 ~ 3750 秒。但我必须用“2小时”来代替它。

这是我的示例数据代码:

library(dplyr)
library(lubridate)

set.seed(900)

data1 <- 
data.frame(
datetime = seq.POSIXt(as.POSIXct("2020-12-26 10:00:00"), as.POSIXct("2020-12-26 10:00:00") + 15*50001, "15 sec"),
Value = sample(1:100, 50002, replace = T),
Instance = "C:"
)

data2 <- data.frame(
  datetime = seq.POSIXt(as.POSIXct("2020-12-26 10:00:00"), as.POSIXct("2020-12-26 10:00:00") + 15*50001, "15 sec"),
  Value = sample(1:100, 50002, replace = T),
  Instance = "D:"
)

data <- rbind (data1, data2) %>% arrange(datetime)

data_lenght <-
  difftime(max(data$datetime), min(data$datetime), units = "secs")

agg_interval <- data_lenght / 200


if (agg_interval > 3600) {
  N_hours <- ceiling(agg_interval / 60 / 60)
  agg_period <- paste0(N_hours, " hours")
} else {
  N_minutes <- ceiling(agg_interval / 60)
  agg_period <- paste0(N_minutes, " mins")
}

agg_data <-
  data %>%  group_by(across(-c(Value, datetime)),  datetime = ceiling_date (datetime, agg_period)) %>%
  summarise (Value = median(Value) , .groups = "drop")

结果:

# A tibble: 212 x 3
   Instance datetime            Value
   <chr>    <dttm>              <dbl>
 1 C:       2020-12-26 10:00:00  85  
 2 C:       2020-12-26 12:00:00  53  
 3 C:       2020-12-26 14:00:00  48.5
 4 C:       2020-12-26 16:00:00  50  
 5 C:       2020-12-26 18:00:00  52  
 6 C:       2020-12-26 20:00:00  50.5
 7 C:       2020-12-26 22:00:00  51  
 8 C:       2020-12-27 00:00:00  48  
 9 C:       2020-12-27 02:00:00  47  
10 C:       2020-12-27 04:00:00  47  
# ... with 202 more rows

另一个问题,稍后在我的代码中,我需要将聚合周期转换为秒。但它是文本:“15 分钟”、“55 分钟”、“3 小时”等。真的很难处理。

有没有比我使用秒作为整数而不是像我这样的文本更简单的方法来进行聚合?

更新:如果我尝试以秒为单位使用间隔:

agg_interval <- round (data_lenght / 200 / 15) * 15

agg_period <- paste0(agg_interval, " secs")

agg_data <-
  data %>%  group_by(across(-c(Value, datetime)),  datetime = ceiling_date (datetime, agg_period)) %>%
  summarise (Value = median(Value) , .groups = "drop")

结果是 - 没有发生聚合:

# A tibble: 50,004 x 3
   Instance datetime            Value
   <chr>    <dttm>              <int>
 1 C:       2020-12-26 10:00:00    85
 2 C:       2020-12-26 10:01:00    19
 3 C:       2020-12-26 10:02:00    43
 4 C:       2020-12-26 10:03:00    83
 5 C:       2020-12-26 10:04:00    67
 6 C:       2020-12-26 10:05:00    28
 7 C:       2020-12-26 10:06:00    54
 8 C:       2020-12-26 10:07:00    28
 9 C:       2020-12-26 10:08:00    99
10 C:       2020-12-26 10:09:00    54
# ... with 49,994 more rows

【问题讨论】:

    标签: r dplyr time-series aggregate lubridate


    【解决方案1】:

    cut.POSIXt 可以这样使用,允许任意秒数。

    secs <- 7200
    as.POSIXt(cut(data$datetime, paste(secs, "secs")) + secs
    

    检查我们有:

    identical(cut(data$datetime, "7200 secs"), cut(data$datetime, "2 hours"))
    ## [1] TRUE
    

    正如您无疑已经注意到的,很遗憾,这适用于 ceiling_date:

    identical(ceiling_date(data$datetime, "2 hours"), 
      ceiling_date(data$datetime, "7200 secs"))
    ## [1] FALSE
    

    示例

    secs <- 3750
    agg_period <- paste(secs, "secs")
    
    agg_data <- data %>%  
        group_by(across(-c(Value, datetime)),  
          datetime = as.POSIXct(cut(datetime, agg_period)) + secs) %>%
        summarise (Value = median(Value) , .groups = "drop")
    
    dim(agg_data)
    ## [1] 402   3
    

    【讨论】:

    • 不幸的是,我需要在聚合期结束时显示聚合点。假设我显示过去一小时的磁盘延迟,而不是未来一小时。
    • 只需在结果中添加秒。已编辑答案以显示这一点。
    • 由于某种原因它不起作用。您可以使用 agg_period
    • 它确实有效。我已经在新添加的示例部分中显示了结果。
    【解决方案2】:

    即使您使用POSIXt,也不需要您使用类似"3 hours" 的排序,您也可以指定length.out=。这是一种方法。

    首先,为每个组创建一个时间范围,然后对所有内容进行分组。

    library(dplyr)
    N <- 200
    newdata1 <- data %>%
      group_by(Instance) %>%
      summarize(datetime = seq(min(datetime), max(datetime), length.out = N)) %>%
      nest_by(.key = "newtimes") %>%
      ungroup()
    newdata2 <- data %>%
      nest_by(Instance, .key = "olddata") %>%
      ungroup()
    
    newdata1
    # # A tibble: 2 x 2
    #   Instance           newtimes
    #   <chr>    <list<tbl_df[,1]>>
    # 1 C:                [200 x 1]
    # 2 D:                [200 x 1]
    newdata2
    # # A tibble: 2 x 2
    #   Instance            olddata
    #   <chr>    <list<tbl_df[,2]>>
    # 1 C:             [50,002 x 2]
    # 2 D:             [50,002 x 2]
    

    现在我们可以approx 进行插值:

    newdata <- left_join(newdata1, newdata2, by = "Instance") %>%
      mutate(newdata = purrr::map2(newtimes, olddata, ~ tibble(newvalue = approx(.y$datetime, .y$Value, xout = .x$datetime)$y))) %>%
      select(-olddata) %>%
      unnest(c(newtimes, newdata))
    
    newdata
    # # A tibble: 400 x 3
    #    Instance datetime            newvalue
    #    <chr>    <dttm>                 <dbl>
    #  1 C:       2020-12-26 10:00:00    85   
    #  2 C:       2020-12-26 11:02:48     9.22
    #  3 C:       2020-12-26 12:05:37    49.2 
    #  4 C:       2020-12-26 13:08:26    50.8 
    #  5 C:       2020-12-26 14:11:15    92.8 
    #  6 C:       2020-12-26 15:14:04    48.7 
    #  7 C:       2020-12-26 16:16:53    70.4 
    #  8 C:       2020-12-26 17:19:42    64.5 
    #  9 C:       2020-12-26 18:22:31    41.7 
    # 10 C:       2020-12-26 19:25:20    73.0 
    # # ... with 390 more rows
    

    【讨论】:

    • 正如你所提到的,我使用了:group_by(across(-c(Value, datetime))。那是因为有时数据中没有 Instance 列,有时有 Counter 列而不是它,等等。 AFAIK 我不能与 nest_by 一起使用。另外,我如何从您的代码中知道聚合周期(以秒为单位)?
    • 你说你想要 200 分,这给了你 200 等间距分,我误解了吗?
    • 您的代码严格使用组的名称:实例。有时我的数据中没有实例,有时有几列。我需要在那里使用“跨越”。
    • Instance 是一个分类的,所以没有办法对其进行插值;也不需要across,这只是您选择的一种方式。如果您打算在示例数据中忽略Instance,那么为什么不(a)不首先添加它,或者(b)明确地说分组变量的数量是可变的?很高兴您找到了解决方案。
    猜你喜欢
    • 1970-01-01
    • 2012-12-09
    • 1970-01-01
    • 2019-07-13
    • 2019-11-18
    • 1970-01-01
    • 2015-10-11
    • 2023-03-07
    • 2019-07-22
    相关资源
    最近更新 更多