【问题标题】:Matching timestamps in two dataframes using dplyr使用 dplyr 匹配两个数据帧中的时间戳
【发布时间】:2020-07-01 20:22:28
【问题描述】:

所以我有一个这样的数据框

DF1

ID    timestamp              value
1     2018-07-31 20:00:00    45555
1     2018-08-01 06:50:00    333
1     2018-08-01 07:00:00    322
2     2018-08-01 13:00:00    1222
2     2018-08-01 22:00:00    1111
3     2018-08-01 11:00:00    0
3     2018-08-02 08:00:00    22222

还有一个这样的

DF2

          startTime             endTime   ID    
2018-08-01 06:45:00 2018-08-01 09:49:00   1
2019-12-02 11:01:00 2019-12-02 11:02:00   2
2018-08-02 07:59:00 2018-08-02 08:50:00   2
2018-08-02 07:59:00 2018-08-02 08:50:00   3

所以我想使用 mutate 创建一个新列,它会改变 DF2,告诉我 startTimeendTime 之间是否有任何时间出现在 DF1 与 ID 匹配。

所以在 DF2 中,如果我们的日期类似于 2018 年 8 月 2 日,时间跨度为 7:59AM - 8:50AM ,如果在此时间间隔内有任何时间戳 (2018-08-02 7:59:00, 2018-08-02 8:00:00, 2018-08-02 8:01:00...) 出现在 DF1 然后是一个变量在 DF2 读取 1,否则读取 0。仅当 ID 匹配时才会出现这种情况。不管多少次,重要的是至少一次。

DF2 的最终版本应该是这样的。

          startTime             endTime   ID   match
2018-08-01 06:45:00 2018-08-01 09:49:00   1    1
2019-12-02 11:01:00 2019-12-02 11:02:00   2    0
2018-08-02 07:59:00 2018-08-02 08:50:00   2    0
2018-08-02 07:59:00 2018-08-02 08:50:00   3    1

理想情况下,我想使用 BETWEEN() 并留在 tidyverse 中。

【问题讨论】:

  • 是正确日期时间类的日期时间列
  • 是的,他们匹配@akrun
  • 我认为这可以在 data.table 中更好地解决,即更有效
  • DF1 是一个 postgresql 数据库 btw;并且这两个类都是 POSIXct 顺便说一句。
  • @akrun mutate 对我来说很有意义,但如果你看到更好的选择,我愿意接受建议

标签: r timestamp tidyverse dplyr


【解决方案1】:

我们可以在 DateTime 列和“ID”列上使用 data.table 非等连接,通过检查行数 (.N) 是否大于 0 来创建 match

library(data.table)
DF2$match <- 0L
setDT(DF2)[DF1,  match := +(.N > 0),on = .(ID, 
        startTime < timestamp, endTime > timestamp), by = .EACHI]
DF2
#             startTime             endTime ID match
#1: 2018-08-01 06:45:00 2018-08-01 09:49:00  1     1
#2: 2019-12-02 11:01:00 2019-12-02 11:02:00  2     0
#3: 2018-08-02 07:59:00 2018-08-02 08:50:00  2     0
#4: 2018-08-02 07:59:00 2018-08-02 08:50:00  3     1

数据

DF1 <- structure(list(ID = c(1L, 1L, 1L, 2L, 2L, 3L, 3L),
 timestamp = structure(c(1533085200, 
1533124200, 1533124800, 1533146400, 1533178800, 1533139200, 1533214800
), class = c("POSIXct", "POSIXt"), tzone = ""), value = c(45555L, 
333L, 322L, 1222L, 1111L, 0L, 22222L)), row.names = c(NA, -7L
), class = "data.frame")

DF2 <- structure(list(startTime = structure(c(1533123900, 1575306060, 
1533214740, 1533214740), class = c("POSIXct", "POSIXt"), tzone = ""), 
    endTime = structure(c(1533134940, 1575306120, 1533217800, 
    1533217800), class = c("POSIXct", "POSIXt"), tzone = ""), 
    ID = c(1L, 2L, 2L, 3L)), row.names = c(NA, -4L), class = "data.frame")

【讨论】:

    【解决方案2】:

    根据您对 tidyverse 解决方案的要求,您也可以先通过“ID”连接两个 dfs,然后使用 lubridate 的 %within%interval (%--%) 函数来查找匹配项:

    library(dplyr)
    library(lubridate)
    
    df_both <- left_join(DF2, DF1, by = "ID") 
    
    df_both %>%
      group_by(ID, startTime, endTime) %>%
      summarize(match = any(timestamp %within% (startTime %--% endTime)),
                .groups = "drop")
    #> # A tibble: 4 x 4
    #>      ID startTime           endTime             match
    #>   <int> <dttm>              <dttm>              <lgl>
    #> 1     1 2018-08-01 06:45:00 2018-08-01 09:49:00 TRUE 
    #> 2     2 2018-08-02 07:59:00 2018-08-02 08:50:00 FALSE
    #> 3     2 2019-12-02 12:01:00 2019-12-02 12:02:00 FALSE
    #> 4     3 2018-08-02 07:59:00 2018-08-02 08:50:00 TRUE
    

    如果您更喜欢 1/0 而不是 TRUE/FALSE,则可以使用 as.integer 来转换“匹配”列。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-20
      • 2017-05-09
      • 2018-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多