【问题标题】:Filter between multiple date ranges在多个日期范围之间过滤
【发布时间】:2020-10-01 14:42:01
【问题描述】:

我有一个大型数据框,两周内每秒进行四次测量。因此数据框非常大。
我还有两个带有开始日期和结束日期的向量,它们定义了数据框中的某些时间范围,并且我必须将其过滤掉。
我想要做的是提取开始日期和结束日期之间的数据。
我的数据看起来像这样。

library(lubridate)
library(dplyr)

df <- data.frame(datetime = seq(ymd_hms("2020/01/01 00:00:00"),
                                by = "sec",
                                length.out = 3600),
                 var = rnorm(3600))

我的开始/结束向量看起来像这样。这里我只添加了两个开始/结束组合。但是实际的向量包含更多的值。

start = c(ymd_hms("2020/01/01 00:1:00"), ymd_hms("2020/01/01 00:30:00"))
end = c(ymd_hms("2020/01/01 00:1:04"), ymd_hms("2020/01/01 00:30:04"))

我试图过滤它,使用

filtered <- df %>%
  filter(datetime >= start & datetime <= end)

head(filtered)

             datetime        var
1 2020-01-01 00:01:00 -0.2245330
2 2020-01-01 00:01:02  0.5926424
3 2020-01-01 00:01:04 -0.3824533
4 2020-01-01 00:30:01 -0.7202059
5 2020-01-01 00:30:03 -0.5775794

但它似乎对数据进行了下采样,因为过滤后的数据帧在第一个时间间隔只有三个测量值,而不是预期的五个值。

如果我只过滤第一个开始和结束日期,我会得到五个值。

filtered2 <- df %>%
  filter(datetime >= start[1] & datetime <= end[1])

head(filtered2)

             datetime         var
1 2020-01-01 00:01:00 -0.22453305
2 2020-01-01 00:01:01  1.13452854
3 2020-01-01 00:01:02  0.59264239
4 2020-01-01 00:01:03 -0.03700048
5 2020-01-01 00:01:04 -0.38245332

我卡住的地方是:
为什么第一次过滤有效,但没有返回预期的完整日期范围?
以及如何过滤完整的数据?

我已经尝试过filter(between(datetime, start, end)。这给了我预期的结果,但仅限于第一个日期范围。 dplyr::between好像不接受vecorts。

非常欢迎任何帮助。

更新
@ekoam 正确指出 data.table::between 也可以。但是作为dplyr::between,它不喜欢向量。

【问题讨论】:

  • 目标到底是什么?对于使用向量进行过滤,您的向量应该与df 具有相同的长度。
  • @arg0naut91 我有一个很长的时间序列(两周内的毫秒数),我必须根据某些开始和结束日期对其进行分析。这些日期将来会发生变化,所以我想将它们保存在向量或列表中,并以这种方式过滤我的数据。

标签: r datetime dplyr filtering lubridate


【解决方案1】:

从这个关于如何Efficient way to filter one data frame by ranges in another 的问题中得到一些启发,我想出了以下解决方案。

一个非常慢的数据集非常大:
它采用我上面提供的数据并使用rowwise()

filtered3 <- df %>% 
  rowwise() %>%
  filter(any(datetime >= start & datetime <= end))

正如我所提到的,我的数据中有超过 300 万行,这非常慢。

另一个选项,也来自上面链接的答案,包括使用 data.table 包,它有一个inrange 函数。这个工作得更快。

library(data.table)
range <- data.table(start = start, end = end)
filtered4 <- setDT(df)[datetime %inrange% range]

【讨论】:

    【解决方案2】:

    正确的语法是

    df %>% filter(dplyr::between(datetime, start[[1L]], end[[1L]]) | dplyr::between(datetime, start[[2L]], end[[2L]]))
    

    更新

    我做了以下检查:

    res1 <- df %>% filter(data.table::between(datetime, start[[1L]], end[[1L]]) | data.table::between(datetime, start[[2L]], end[[2L]]))
    
    res2 <- df %>% filter(dplyr::between(datetime, start[[1L]], end[[1L]]) | dplyr::between(datetime, start[[2L]], end[[2L]]))
    
    all(res1 == res2)
    

    输出

    > all(res1 == res2)
    [1] TRUE
    

    因此,如果您还使用dplyr 1.0.2data.table 1.13.0,则可以使用dplyr::betweendata.table::between 执行任务。

    【讨论】:

    • 谢谢@ekoam。您的答案适用于示例数据。我应该澄清一下,我的实际数据集是一个相当长的数据集,在两周内进行毫秒测量,因此我的开始和结束向量也各有两个以上的值。使用 OR 语句会很快变得很长。
    猜你喜欢
    • 2019-05-29
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 2020-02-26
    • 2022-11-04
    • 2015-04-10
    • 1970-01-01
    • 2022-01-03
    相关资源
    最近更新 更多