确定工作日是否在R中的两个日期之间

时间:2015-08-05 16:00:09

标签: r date

[更新]

在另一个thread中,来自@Frank的答案解决了这个问题。这个问题与另一个问题重复。

[问题]

我正在R编写一个函数来测试工作日是否介于两个日期之间。这就是我所拥有的,但我认为解决方案并不优雅。有更多的数学方法吗?

library(data.table) ## wday is a function in this package
isDayIn <- function(weekday, date1, date2) {
  if (weekday<1 | weekday>7) stop("weekday must be an integer from 1 to 7.")
  date1 <- as.Date(date1)
  date2 <- as.Date(date2)
  output <- weekday %in% unique(wday(seq.Date(date1, date2, by=1)))
  return(output)
}

## 2015-08-02 is a Sunday and 2015-08-03 is a Monday
isDayIn(1, "2015-08-02", "2015-08-03")
> TRUE
isDayIn(7, "2015-08-02", "2015-08-03")
> FALSE

注意:函数wday在星期日开始,在星期六结束,因此星期日将映射到整数1,星期六将映射到整数7。

4 个答案:

答案 0 :(得分:2)

使用var example = momentObject.format("YYYY-MM-DD[T]HH:mm:ss"); 的另一个功能选项:

base R

答案 1 :(得分:1)

我认为您的解决方案很好。但这是一个快速修复:

isDayIn <- function(weekday, date1, date2) {
  if (weekday<1 | weekday>7) stop("weekday must be an integer from 1 to 7.")
  require(lubridate)
  date1 <- as.Date(date1)
  date2 <- as.Date(date2)
  if (as.integer(date2 - date1) >= 7) {
    return(TRUE) # by default
  } else {
    return(weekday %in% wday(seq.Date(date1, date2, by=1)))
  }
}

答案 2 :(得分:0)

已经有很好的解决方案,但它们都没有避免产生一系列日子。我试图找到一个只比较工作日数字(和周数)的解决方案。 它在内部使用星期一作为一周的第一天,但​​参数startWithSunday提供了将星期日设置为第1天的可能性。另一种方法是在%V%U之间切换{ {1}},但这种方法对我来说似乎更直接。

strftime

这个小任务看起来像很多代码,但大部分只是准备;实际工作是在两个isDayIn1 <- function(weekday, date1, date2, startWithSunday = FALSE) { if (weekday < 1 | weekday > 7) stop("weekday must be an integer from 1 to 7.") if(startWithSunday) { weekday <- max(weekday - 1, 1) } dates <- sort(as.Date(c(date1, date2))) if (dates[2] - dates[1] >= 7) return(TRUE) weeks <- strftime(dates, "%V") days <- strftime(dates, "%u") if (weeks[1] == weeks[2]) { # Dates are in the same week. return(weekday >= days[1] & weekday <= days[2]) } else { # Different weeks. return(weekday >= days[1] | weekday <= days[2]) } } 语句中完成的。诀窍是区分日期在不同周内的情况,因为这会影响我们应该做的比较。

为了检查return是否正常工作,我编写了这个小包装函数:

isDayIn1

这是第一批测试。请注意,niceTests <- function(weekday, date1, date2, startWithSunday = FALSE) { date1 <- as.Date(date1) date2 <- as.Date(date2) fmt <- "%a, %y-%m-%d (week %V)" if (startWithSunday) { fmt <- "%a, %y-%m-%d (week %U)" } print(sprintf("Date1: %s, Date2: %s, Diff.: %d. Range contains day #%d: %s", strftime(date1, fmt), strftime(date2, fmt), abs(date2 - date1), weekday, as.character(isDayIn1(weekday, date1, date2, startWithSunday)) )) } 默认为startWithSunday,因此工作日FALSE表示星期一。

1

输出:

niceTests(7, "2015-08-02", "2015-08-03") # from question (Sunday in Su-Mo)
niceTests(6, "2015-08-02", "2015-08-03") # from question (Saturday in Su-Mo)
niceTests(1, "2015-08-02", "2015-08-09") # Full week or more.
niceTests(1, "2015-08-02", "2015-08-10") # Full week or more.

niceTests(1, "2015-08-05", "2015-08-07") # Same week. (Wednesday - Friday)
niceTests(2, "2015-08-05", "2015-08-07") # Same week.
niceTests(3, "2015-08-05", "2015-08-07") # Same week.
niceTests(4, "2015-08-05", "2015-08-07") # Same week.
niceTests(5, "2015-08-05", "2015-08-07") # Same week.
niceTests(6, "2015-08-05", "2015-08-07") # Same week.
niceTests(7, "2015-08-05", "2015-08-07") # Same week.

niceTests(1, "2015-08-08", "2015-08-11") # Across weeks. (Saturday - Tuesday)
niceTests(2, "2015-08-08", "2015-08-11") # Across weeks.
niceTests(3, "2015-08-08", "2015-08-11") # Across weeks.
niceTests(4, "2015-08-08", "2015-08-11") # Across weeks.
niceTests(5, "2015-08-08", "2015-08-11") # Across weeks.
niceTests(6, "2015-08-08", "2015-08-11") # Across weeks.
niceTests(7, "2015-08-08", "2015-08-11") # Across weeks.

最后,[1] "Date1: Sun, 15-08-02 (week 31), Date2: Mon, 15-08-03 (week 32), Diff.: 1. Range contains day #7: TRUE" [1] "Date1: Sun, 15-08-02 (week 31), Date2: Mon, 15-08-03 (week 32), Diff.: 1. Range contains day #6: FALSE" [1] "Date1: Sun, 15-08-02 (week 31), Date2: Sun, 15-08-09 (week 32), Diff.: 7. Range contains day #1: TRUE" [1] "Date1: Sun, 15-08-02 (week 31), Date2: Mon, 15-08-10 (week 33), Diff.: 8. Range contains day #1: TRUE" [1] "Date1: Wed, 15-08-05 (week 32), Date2: Fri, 15-08-07 (week 32), Diff.: 2. Range contains day #1: FALSE" [1] "Date1: Wed, 15-08-05 (week 32), Date2: Fri, 15-08-07 (week 32), Diff.: 2. Range contains day #2: FALSE" [1] "Date1: Wed, 15-08-05 (week 32), Date2: Fri, 15-08-07 (week 32), Diff.: 2. Range contains day #3: TRUE" [1] "Date1: Wed, 15-08-05 (week 32), Date2: Fri, 15-08-07 (week 32), Diff.: 2. Range contains day #4: TRUE" [1] "Date1: Wed, 15-08-05 (week 32), Date2: Fri, 15-08-07 (week 32), Diff.: 2. Range contains day #5: TRUE" [1] "Date1: Wed, 15-08-05 (week 32), Date2: Fri, 15-08-07 (week 32), Diff.: 2. Range contains day #6: FALSE" [1] "Date1: Wed, 15-08-05 (week 32), Date2: Fri, 15-08-07 (week 32), Diff.: 2. Range contains day #7: FALSE" [1] "Date1: Sat, 15-08-08 (week 32), Date2: Tue, 15-08-11 (week 33), Diff.: 3. Range contains day #1: TRUE" [1] "Date1: Sat, 15-08-08 (week 32), Date2: Tue, 15-08-11 (week 33), Diff.: 3. Range contains day #2: TRUE" [1] "Date1: Sat, 15-08-08 (week 32), Date2: Tue, 15-08-11 (week 33), Diff.: 3. Range contains day #3: FALSE" [1] "Date1: Sat, 15-08-08 (week 32), Date2: Tue, 15-08-11 (week 33), Diff.: 3. Range contains day #4: FALSE" [1] "Date1: Sat, 15-08-08 (week 32), Date2: Tue, 15-08-11 (week 33), Diff.: 3. Range contains day #5: FALSE" [1] "Date1: Sat, 15-08-08 (week 32), Date2: Tue, 15-08-11 (week 33), Diff.: 3. Range contains day #6: TRUE" [1] "Date1: Sat, 15-08-08 (week 32), Date2: Tue, 15-08-11 (week 33), Diff.: 3. Range contains day #7: TRUE" 的测试,其中第1天是星期日:

startWidthSunday = TRUE

输出:

print("Now: Start with Sunday!")

niceTests(1, "2015-08-02", "2015-08-03", startWithSunday = TRUE) # from question (Sunday in Su-Mo)
niceTests(7, "2015-08-02", "2015-08-03", startWithSunday = TRUE) # from question (Saturday in Su-Mo)
niceTests(1, "2015-08-02", "2015-08-09", startWithSunday = TRUE) # Full week or more.
niceTests(1, "2015-08-02", "2015-08-10", startWithSunday = TRUE) # Full week or more.

niceTests(1, "2015-08-05", "2015-08-07", startWithSunday = TRUE) # Same week. (Wednesday - Friday)
niceTests(2, "2015-08-05", "2015-08-07", startWithSunday = TRUE) # Same week.
niceTests(3, "2015-08-05", "2015-08-07", startWithSunday = TRUE) # Same week.
niceTests(4, "2015-08-05", "2015-08-07", startWithSunday = TRUE) # Same week.
niceTests(5, "2015-08-05", "2015-08-07", startWithSunday = TRUE) # Same week.
niceTests(6, "2015-08-05", "2015-08-07", startWithSunday = TRUE) # Same week.
niceTests(7, "2015-08-05", "2015-08-07", startWithSunday = TRUE) # Same week.

niceTests(1, "2015-08-08", "2015-08-11", startWithSunday = TRUE) # Across weeks. (Saturday - Tuesday)
niceTests(2, "2015-08-08", "2015-08-11", startWithSunday = TRUE) # Across weeks.
niceTests(3, "2015-08-08", "2015-08-11", startWithSunday = TRUE) # Across weeks.
niceTests(4, "2015-08-08", "2015-08-11", startWithSunday = TRUE) # Across weeks.
niceTests(5, "2015-08-08", "2015-08-11", startWithSunday = TRUE) # Across weeks.
niceTests(6, "2015-08-08", "2015-08-11", startWithSunday = TRUE) # Across weeks.
niceTests(7, "2015-08-08", "2015-08-11", startWithSunday = TRUE) # Across weeks.

答案 3 :(得分:0)

我通过@CL编写了答案的矢量化版本,该版本也更为通用:

#' Check if a weekday is within an interval
#' 
#' @param wday Day of week (integer 1-7)
#' @param from Date. Can be a vector.
#' @param to Date. Same length as `from` and must be greater than `from`.
#' @param week_start 1 = Monday. 7 = Sunday
#' 
wday_in_interval = function(wday, from, to, week_start = 1) {
  if (wday < 1 | weekday > 7) 
    stop("wday must be an integer from 1 to 7.")
  if (week_start)
    wday = 1 + (((wday - 2) + week_start ) %% 7)  # Translate wday to week_start = 1 (ISO standard)
  if (any(from > to, na.rm = TRUE))
    stop("`from` must come before `to`")
  
  # If the interval is greater than a week, it trivially contains any weekday
  over_a_week = difftime(from, to, units = "days") >= 7
  
  # Check if weekday is both smaller/greater than "from" and "to"
  days_from = as.numeric(strftime(from, "%u"))
  days_to = as.numeric(strftime(to, "%u"))
  contains_weekday = ifelse(
    strftime(from, "%V") == strftime(to, "%V"),  # Dates are in the same week?
    yes = wday >= days_from & wday <= days_to,
    no = wday >= days_from | wday <= days_to  # 
  )
  
  return(over_a_week | contains_weekday)
}

例如,假设我们要检测一个与周末重叠的时间序列中的间隔。我们在周六和周日运行wday_in_interval

library(dplyr)
tibble::tibble(
  timestamp = seq(as.POSIXct("2020-09-03 0:00"), as.POSIXct("2020-09-8 12: 00"), length.out = 10),
  overlaps_saturday = wday_in_interval(6, from = lag(timestamp), to = timestamp),
  overlaps_sunday = wday_in_interval(7, from = lag(timestamp), to = timestamp),
  overlaps_weekend = overlaps_saturday | overlaps_sunday
)

结果:

# A tibble: 10 x 4
   timestamp           overlaps_saturday overlaps_sunday overlaps_weekend
   <dttm>              <lgl>             <lgl>           <lgl>           
 1 2020-09-03 00:00:00 NA                NA              NA              
 2 2020-09-03 14:40:00 FALSE             FALSE           FALSE           
 3 2020-09-04 05:20:00 FALSE             FALSE           FALSE           
 4 2020-09-04 20:00:00 FALSE             FALSE           FALSE           
 5 2020-09-05 10:40:00 TRUE              FALSE           TRUE            
 6 2020-09-06 01:20:00 TRUE              TRUE            TRUE            
 7 2020-09-06 16:00:00 FALSE             TRUE            TRUE            
 8 2020-09-07 06:40:00 FALSE             TRUE            TRUE            
 9 2020-09-07 21:20:00 FALSE             FALSE           FALSE           
10 2020-09-08 12:00:00 FALSE             FALSE           FALSE  

在我的中型笔记本电脑上,它在约3秒内处理了250.000行。