代码之家  ›  专栏  ›  技术社区  ›  jmuhlenkamp

将日期子集为给定的工作日,如果缺少工作日,请选择下一个日期

  •  1
  • jmuhlenkamp  · 技术社区  · 6 年前

    我能找到很多关于如何处理某个工作日的子集日期的信息(例如。 Get Dates of a Certain Weekday from a Year in R ). 但是,我找不到任何实现我想要的回退逻辑的方法。具体来说,如果给定的工作日在给定的一周中不存在,我想抓取下一个可用的日期,不包括周六和周日。

    例如,从一个日期向量中,我想选择与星期四对应的所有日期。然而,如果星期四不见了,我应该选择下一个工作日的日期。在下面的例子中,这是第二天,星期五。

    library(lubridate)
    
    # Create some dates
    dates <- seq.Date(as.Date("2017-11-16"), as.Date("2017-11-24"), by = 1)
    
    # Remove Thursday, November 23
    dates <- dates[dates != as.Date("2017-11-23")]
    
    # Get all Thursdays in dates
    dates[wday(dates) == 5]
    # [1] "2017-11-16"
    
    # Desired Output:
    # Because Thursday 2017-11-23 is missing in a week,
    # we roll over and select Friday 2017-11-24 instead  
    # [1] "2017-11-16" "2017-11-24"
    

    注2:我希望在没有任何外部依赖的情况下完成这项工作,除了常见的R包,如lubridate等(例如,不依赖于c++库)。

    我有信心我可以写一些东西做我想做的,但我有困难,创造一些简短和优雅的东西。

    3 回复  |  直到 6 年前
        1
  •  1
  •   Henrik plannapus    6 年前

    另一种选择 findInterval .

    min “日期”,至 max “日期”。

    选择与焦点工作日(“wds”)对应的日期。

    使用 findInterval公司 将“wds”滚动到“dates\u 1\u 5”中最近的可用工作日。

    f <- function(wd, dates){
      tmp <- seq(as.Date(paste(format(min(dates), "%Y-%W"), wd, sep = "-"),
                         format = "%Y-%W-%u"),
                 max(dates), by = 1)
    
      wds <- tmp[as.integer(format(tmp, "%u")) == wd]
    
      dates_1_5 <- dates[as.integer(format(dates, "%u")) %in% 1:5]
    
      dates_1_5[findInterval(wds, dates_1_5, left.open = TRUE) + 1]
    }
    

    一些例子:

    d <- seq.Date(as.Date("2017-11-16"), as.Date("2017-11-24"), by = 1)
    
    dates <- d[d != as.Date("2017-11-23")]
    f(wd = 4, dates)
    # [1] "2017-11-16" "2017-11-24"
    
    dates <- d[d != as.Date("2017-11-16")]
    f(wd = 4, dates)
    # [1] "2017-11-17" "2017-11-23"
    
    dates <- d[!(d %in% as.Date(c("2017-11-16", "2017-11-17", "2017-11-21", "2017-11-23")))]
    f(wd = 2, dates)
    # [1] "2017-11-20" "2017-11-22"
    

    data.table 滚动连接:

    library(data.table)
    
    wd <- 2
    # using 'dates' from above
    
    d1 <- data.table(dates)
    d2 <- data.table(dates = seq(as.Date(paste(format(min(dates), "%Y-%W"), wd, sep = "-"),
                                         format = "%Y-%W-%u"),
                                 max(dates), by = 1))
    
    d1[wday(dates) %in% 2:6][d2[wday(dates) == wd + 1],
                             on = "dates", .(x.dates), roll = -Inf]
    

    …或非等联接:

    d1[wday(dates) %in% 2:6][d2[wday(dates) == wd + 1],
                             on = .(dates >= dates), .(x.dates), mult = "first"]
    

    如果需要,只需像上面那样包装一个函数。

        2
  •  0
  •   Oliver Baumann    6 年前

    我打破了你的“没有外部依赖”的条件,但是你已经使用了 lubridate (这是一个依赖项;-),我会给你提供一个 lead lag dplyr

    我要做的是通过计算一种运行日差来找出“跳过”在序列中的位置。一旦我们知道了跳转的位置,我们就转到序列中的下一个数据,不管是什么。现在,很可能这不是星期五,而是星期六。在这种情况下,你必须弄清楚你是否还想要下个星期五,即使中间有一个星期四。

    library(dplyr)
    
    rollover_to_next <- function(dateseq, the_day = 5) {
      day_diffs <- lead(wday(dateseq) - lag(wday(dateseq))) %% 7
      skips <- which(day_diffs > 1) 
    
      sort(c(dateseq[wday(dateseq) == the_day], dateseq[skips + 1]))
    }
    
    dates <- seq.Date(as.Date("2017-11-16"), as.Date("2017-11-24"), by = 1)
    dates <- dates[dates != as.Date("2017-11-23")]
    
    rollover_to_next(dates)
    

    输出:

    [1] "2017-11-16" "2017-11-24"
    

    你可能得考虑一下 idx + 1 元素不存在,但我会让你来处理。

        3
  •  0
  •   TinglTanglBob    6 年前

    可能不是最优雅的方式,但我认为它应该工作:)

    library(lubridate)
    
    
    dates <- seq.Date(as.Date("2017-11-16"), as.Date("2017-11-30"), by = 1) #your dates
    dates <- dates[dates != as.Date("2017-11-23")] # thursday
    dates <- dates[dates != as.Date("2017-11-24")] # friday
    dates <- dates[dates != as.Date("2017-11-25")] # satureday
    dates <- dates[dates != as.Date("2017-11-26")] # sunday
    dates <- dates[dates != as.Date("2017-11-27")] # monday
    dates <- dates[dates != as.Date("2017-11-28")] # tuesday
    #dates <- dates[dates != as.Date("2017-11-29")] # wednesday
    
    dates_shall_be <- seq.Date(min(dates)-wday(min(dates))+1, max(dates), by = 1) # create a shall-be list of days within your date-range
    # min(dates)-wday(min(dates))+1 shiftback mindate to get missing thursdays in week one
    
    thuesdays_shall = dates_shall_be[wday(dates_shall_be) == 5] # get all thuesdays that should be in there
    
    for(i in 1:6) # run threw all possible followup days till wednesday next week 
    {
      thuesdays_shall[!thuesdays_shall %in% dates] = thuesdays_shall[!thuesdays_shall %in% dates] + 1 # if date is not present in your data add another day to it
    }
    
    thuesdays_shall[!thuesdays_shall %in% dates] = NA # if date is still not present in the data after 6 shifts, this thursday + the whole followup days till next thursday are missing and NA is taken
    thuesdays_shall