【问题标题】:Updating Dates and Date Intervals in R在 R 中更新日期和日期间隔
【发布时间】:2023-03-04 14:35:01
【问题描述】:

我什至不确定我是否在标题中准确地描述了问题,但这里是。

假设我有以下data.table/data.frame:

library(data.table)
library(lubridate)


DT <- data.table(begin = c("2019-06-01 09:00:00","2019-06-01 09:00:00", "2019-06-01 09:00:00",
                           "2019-06-01 09:00:00", "2016-06-01 09:00:00","2016-06-01 09:00:00"),
                 end = c("2019-06-03 14:00:00", "2019-06-03 14:00:00", "2019-06-03 14:00:00",
                         "2019-06-02 05:00:00", "2019-06-02 05:00:00", "2016-06-01 23:15:00"),
                 person = c("A", "A","A", "B", "B", "C"))

    begin                 end person
1: 2019-06-01 09:00:00 2019-06-03 14:00:00      A
2: 2019-06-01 09:00:00 2019-06-03 14:00:00      A
3: 2019-06-01 09:00:00 2019-06-03 14:00:00      A
4: 2019-06-01 09:00:00 2019-06-02 05:00:00      B
5: 2016-06-01 09:00:00 2019-06-02 05:00:00      B
6: 2016-06-01 09:00:00 2016-06-01 23:15:00      C

这本质上是一个数据集,汇总了每个人某个时期的开始和结束时间的时间戳。每个人的行数按时间段跨越的天数重复。例如,人员A 具有相同“班次”的三个条目,因为他们的班次跨越三个不同的日期,06-01、06-02 和 06-03。这些条目按班次跨越的日期数重复,但有些班次在同一天开始和结束。

我想要更新上述数据集的开始和结束日期,以便我可以看到每个班次在天级别的开始和结束时间。所以数据集应该是这样的:

    begin                 end                person
1: 2019-06-01 09:00:00 2019-06-02 00:00:00      A
2: 2019-06-02 00:00:00 2019-06-03 00:00:00      A
3: 2019-06-03 00:00:00 2019-06-03 14:00:00      A
4: 2019-06-01 09:00:00 2019-06-02 00:00:00      B
5: 2016-06-02 00:00:00 2019-06-02 05:00:00      B
6: 2016-06-01 09:00:00 2016-06-01 23:15:00      C

任何帮助将不胜感激!

【问题讨论】:

  • 所以您想用00:00 替换每个14:00(例如A)以反映新的一天?您可以使用last,如果时间不等于last 的时间,请将其更改为00:00?但是,为什么要保留所有值?他们整夜工作吗?
  • @NelsonGon,是的,这正是我想要做的。当轮班可能超过一天时,我正在尝试区分每天的工作量。
  • 您的数据不是表明 A 的转变是连续的吗?将跨越 3 个日历日 2019-06-01 09:00:00 到 2019-06-03 14:00:00 的班次解析为三个人为班次,您会得到什么?
  • @NelsonGon,原因是我想知道一天用了多少分钟的劳动力。
  • 用户 B 从 2016 年到 2019 年真的工作了吗?

标签: r dplyr data.table lubridate


【解决方案1】:

假设您对第 5 行的 B 人有错字(从 2019 年开始而不是 2016 年):

library(data.table)
library(lubridate)

> DT <- data.table(begin = c("2019-06-01 09:00:00","2019-06-01 09:00:00", "2019-06-01 09:00:00",
+                            "2019-06-01 09:00:00", "2019-06-01 09:00:00","2016-06-01 09:00:00"),
+                  end = c("2019-06-03 14:00:00", "2019-06-03 14:00:00", "2019-06-03 14:00:00",
+                          "2019-06-02 05:00:00", "2019-06-02 05:00:00", "2016-06-01 23:15:00"),
+                  person = c("A", "A","A", "B", "B", "C"))
>                  
> DT[, `:=`(min=as.numeric(difftime(end,begin, units="mins")), 
+           days=as.numeric(as_date(end)-as_date(begin)+1))][, min_day:=min/days]
> 
> unique(DT)
                 begin                 end person  min days min_day
1: 2019-06-01 09:00:00 2019-06-03 14:00:00      A 3180    3    1060
2: 2019-06-01 09:00:00 2019-06-02 05:00:00      B 1200    2     600
3: 2016-06-01 09:00:00 2016-06-01 23:15:00      C  855    1     855

【讨论】:

  • 您的输出看起来与 OP 中的预期输出完全不同。
  • 没错,但它让他达到了他所说的他想要的 - 每个人每天的分钟数。
  • 不,这让他们每次会话的时间,而不是每天。如果是每天,第一行将是三个,06-0106-0206-03 各一个。
  • 已编辑答案以显示范围内的分钟和日历日。显示范围内每个日历日期的分钟数。是的,它在会话内,但肯定反映了每个日期的分钟数。由于 OP 需要每个日期的分钟数,因此这将绕过重新解析开始/结束日期时间而直接进行所需的计算。
【解决方案2】:

首先,确定日期(我已经确定了从 2016 年开始到 2019 年的第 5 行,似乎不太可能),

DT[, c("begin", "end"):=lapply(.SD, as.POSIXct), .SDcols=c("begin", "end")]

## we get this
DT <- as.data.table(structure(list(begin = structure(c(1559394000, 1559394000, 1559394000, 1559394000, 1559394000, 1464786000), class = c("POSIXct", "POSIXt"), tzone = ""), end = structure(c(1559584800, 1559584800, 1559584800, 1559466000, 1559466000, 1464837300), class = c("POSIXct", "POSIXt"), tzone = ""), person = c("A", "A", "A", "B", "B", "C")), row.names = c(NA, -6L), class = c("data.table", "data.frame")))

其次,我们再创建这个函数

func <- function(st, en) {
  midns <- lubridate::ceiling_date(seq(st, en, by = "day"), unit = "day")
  times <- unique(sort(c(midns[ st < midns & midns < en], st, en)))
  data.table(begin = times[-length(times)], end = times[-1])
}

最后,我们使用它,使用by=.(person) 在输出中保留该列。我使用DT,因为我们不需要(甚至想要)每个班次/天的重复:

unique(DT)[, rbindlist(Map(func, begin, end)), by = .(person)]
#    person               begin                 end
#    <char>              <POSc>              <POSc>
# 1:      A 2019-06-01 09:00:00 2019-06-02 00:00:00
# 2:      A 2019-06-02 00:00:00 2019-06-03 00:00:00
# 3:      A 2019-06-03 00:00:00 2019-06-03 14:00:00
# 4:      B 2019-06-01 09:00:00 2019-06-02 00:00:00
# 5:      B 2019-06-02 00:00:00 2019-06-02 05:00:00
# 6:      C 2016-06-01 09:00:00 2016-06-01 23:15:00

【讨论】: