【问题标题】:Subset dates with a given weekday and select next date if weekday is missing给定工作日的子集日期,如果缺少工作日,则选择下一个日期
【发布时间】:2018-10-24 12:44:50
【问题描述】:

我可以在 SO 上找到很多关于将子集日期处理到某个工作日的信息(例如 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"

注意 1:对于缺少星期四和星期五的给定一周,我想滚动到星期一。基本上,对于没有找到星期四的星期,在可用日期中获取下一个日期。

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

我相信我可以写一些东西来做我想做的事,但是我很难找到创建简短而优雅的东西。

【问题讨论】:

    标签: r date lubridate


    【解决方案1】:

    findInterval 的替代方案。

    创建一个日期序列 ('tmp'),从 min 'dates' 的一周中的焦点工作日 ('wd') 到 max 'dates'。

    选择与重点工作日('wds')相对应的日期。

    从“日期”(“日期_1_5”)中选择工作日。

    使用findInterval 将“wds”滚动到“dates_1_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"]
    

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

    【讨论】:

    • 嗨@jmuhlenkamp!只是好奇:我的回答是否产生了您想要的结果,还是需要进一步调整?
    • 这很棒。也喜欢basedata.table 解决方案。
    • 感谢您的反馈。很高兴听到它按您想要的方式工作。
    【解决方案2】:

    我打破了您“没有外部依赖项”的条件,但是由于您已经使用 lubridate(这是一个依赖项 ;-)),我将为您提供一个利用 leadlag 的解决方案来自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 元素不存在的极端情况,但我会留给您处理。

    【讨论】:

    • 谢谢,这很有帮助。基本问题,它似乎确实可以正确处理多个跳过。例如,如果我添加dates &lt;- dates[dates != as.Date("2017-11-16")],它只返回第 24 个,但它也应该返回第 17 个。
    • @jmuhlenkamp,是的,这是你发现的一个错误,它不处理开头缺少的日期,因为差异总是 1...
    • @jmuhlenkamp,我真的很抱歉,但现在我想不出任何简洁通用的解决方案。您最好的选择可能是在 for 循环中将其拼写出来,因为您的逻辑分支取决于下一个/上一个值,例如“如果当前事物是星期五,并且它是第一个元素,则返回它”,“如果事物不是星期四并且下一个事物不是星期五,则找到下一个事物是星期一”。你确定这是你需要的吗?这很可能是一个 XY 问题...
    • 谢谢,您的回答仍然很有用。我没有处理我的案例就继续进行,尽管我仍然很感兴趣,因为我希望这已经在 SO 上得到回答,并且可能会在未来的某个时候回到这个问题。请原谅我的无知,XY 问题是什么意思?
    • @jmuhlenkamp,请务必查看其他答案! Henrik 提供了一个解决方案,并且已经很好奇它是否有帮助。一个 XY 问题描述了这样一种情况:你询问问题 X 的解决方案 Y,你认为它是正确的,所以你问的是 Y 而不是你的问题 X;它是better described on Meta
    【解决方案3】:

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

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-12
      • 1970-01-01
      相关资源
      最近更新 更多