组连续工作日日期(不包括周末和节假日)

Gab*_*lla 5 r date

我有关于不同人 ( ID)、他们工作的日期 ( Date) 以及他们每个日期工作了多少小时 ( Hours) 的数据。

周一至周五被视为工作日。对于每个ID,我想对连续工作日进行分组。在决定工作日是否连续时,应省略周末和节假日。

一些例子:

如果一个人在周一、周二和周三工作,然后跳过周四,并在周五再次工作,那么周一至周三被视为一组,周五被视为另一组。

如果一个人每周周四和周五工作,下周周一和周二工作,那么这四天应该在同一组中。因此,当检查若在周末的日子里被省略工作日内是连续的。

如果一周的周一 - 周五和下周的周一 - 周五,那么我会将这两个星期算作连续的工作日。

我还想考虑美国常见的假期(例如新年),这样 12/31/2020 至 1/4/2021 仍将算作连续工作日。

创建分组变量后,我想对每个组的工作时间求和。

示例数据:

df1 = structure(list(ID = c(1, 1, 1, 1, 2, 2, 3, 3, 
3, 3), Date = structure(c(18781, 18782, 18785, 18750, 18687, 
18688, 18626, 18627, 18631, 18634), class = "Date"), Hours = c(8, 
8, 8, 16, 8, 8, 8, 8, 8, 8)), row.names = c(NA, -10L), class = "data.frame")


  ID       Date Hours
1  1 2021-06-03     8
2  1 2021-06-04     8
3  1 2021-06-07     8
4  1 2021-05-03    16
5  2 2021-03-01     8
6  2 2021-03-02     8
7  3 2020-12-30     8
8  3 2020-12-31     8
9  3 2021-01-04     8
10 3 2021-01-07     8

Run Code Online (Sandbox Code Playgroud)

我想象我的输出看起来像这样:

 ID      Date1      Date2 Hours
1 1 2021-06-03 2021-06-07    24
# the weekend, June 5-6, is omitted
# when the group of consecutive working days is created
 
2 1 2021-05-03 2021-05-03    16
3 2 2021-03-01 2021-03-02    16

4 3 2020-12-30 2021-01-04    24
# the public holiday (Jan 1) and the weekend (Jan 2-3) are omitted
   
5 3 2021-01-07 2021-01-07     8

Run Code Online (Sandbox Code Playgroud)

我的首要任务是至少弄清楚连续工作周,假期部分将是额外的奖励。

Hen*_*rik 0

您可以使用RQuantLib::businessDaysBetween. 对于每个 ID ( by = ID),计算每行之间的工作日数,即提供“滞后”( head(Date, -1)) 和“超前”( tail(Date, -1)) 向量作为fromto日期。选择相关的calendar(请参阅中的详细信息?businessDaysBetween

对于每个 ID 和连续工作日 ( by = .(ID, g = cumsum(d != 1L))),选择第一个和最后一个日期 ( from = Date[1], to = Date[.N]) 并对小时数求和 ( sum(Hours))

library(data.table)
library(RQuantLib)
setDT(df1)

df1[ , d := c(1, businessDaysBetween(calendar = "UnitedStates",
                                     from = head(Date, -1), to = tail(Date, -1))),
    by = ID]

df1[ , .(from = Date[1], to = Date[.N], Hours = sum(Hours)),
    by = .(ID, g = cumsum(d != 1L))]

#    ID g       from         to Hours
# 1:  1 0 2021-06-03 2021-06-07    24
# 2:  1 1 2021-05-03 2021-05-03    16
# 3:  2 1 2021-03-01 2021-03-02    16
# 4:  3 1 2020-12-30 2021-01-04    24
# 5:  3 2 2021-01-07 2021-01-07     8
Run Code Online (Sandbox Code Playgroud)

更复杂的解决方案(预businessDaysBetween):

在每个 ID ( ) 内创建完整的日期序列df1[ , .(Date = seq(min(Date), max(Date), by = "1 day")), by = ID]。与 ID 和日期 ( ) 上的原始数据连接df1[..., on =.(ID, Date)。对于原始数据中不存在的日期,即(原始)连续天之间的间隔,小时数将为NA

在每个 ID ( by = ID) 内,根据缺失的小时数 ( rleid(is.na(Hours))) 创建游程长度索引。对于缺少小时 ( d[is.na(Hours)) 的行,即原始时间序列中的间隙,对于每个 ID 和运行 ( by = .(ID, r)),检查all日期是否是周末 ( wday(Date) %in% c(1, 7)) 或 ( |) 公共假期* ( Date %in% as.Date(holidayNYSE(unique(year(Date))))),并创建一个索引变量,九.

对于原始行和周末/节假日间隙 ( !is.na(Hours) | ix),创建连续日期的分组变量 ( g = cumsum(c(TRUE, diff(Date) != 1L)))。对于每个 ID 和连续日期 ( by = .(ID, g)),选择第一个和最后一个日期 ( from = Date[1], to = Date[.N]) 并对小时数求和 ( sum(Hours, na.rm = TRUE))

library(data.table)
library(timeDate)
setDT(df1)

d = df1[df1[ , .(Date = seq(min(Date), max(Date), by = "1 day")), by = ID],
    on = .(ID, Date)]

d[ , r := rleid(is.na(Hours)), by = ID]

d[is.na(Hours), ix := all(
    wday(Date) %in% c(1, 7) |
      Date %in% as.Date(holidayNYSE(unique(year(Date)))))
, by = .(ID, r)]


d[!is.na(Hours) | ix, .(Date, Hours, g = cumsum(c(TRUE, diff(Date) != 1L))),
  by = ID][
  , .(from = Date[1], to = Date[.N],
    Hours = sum(Hours, na.rm = TRUE)),
  by = .(ID, g)] 

#    ID g       from         to Hours
# 1:  1 1 2021-05-03 2021-05-03    16
# 2:  1 2 2021-06-03 2021-06-07    24
# 3:  2 1 2021-03-01 2021-03-02    16
# 4:  3 1 2020-12-30 2021-01-04    24
# 5:  3 2 2021-01-07 2021-01-07     8
Run Code Online (Sandbox Code Playgroud)

*假日的其他定义请参阅timeDate手册。