【问题标题】:How do I turn a date into a number representing the count of workdays in that month up to that date?如何将日期转换为表示该月截至该日期的工作日数的数字?
【发布时间】:2022-01-31 20:16:28
【问题描述】:

我有一个日期数组。我需要将这些日期转换为它们各自月份的代表工作日。例如,12/14/2021 (mm/dd/yyyy) 必须变成 10,因为它是 2021 年 12 月的第 10 个工作日;同样的,1/31/2022必须变成21,因为它是2022年1月的第21个工作日。如果日期不是工作日,我们可以选择一个识别字符。

我考虑过使用 bizdays 包,但我在这里苦苦挣扎。有人可以帮忙吗?

【问题讨论】:

  • 你也需要考虑假期吗?
  • 艾伦你好!我愿意。如果需要,我有一个假期日期列表。如果没有简单的方法跳过假期,那没关系。工作日的近似值并不完美,但已经足够了。

标签: r date


【解决方案1】:

要真正获得一个月内的工作日,您需要同时考虑周末节假日。这是金融中的一个常见问题,因此存在一些解决方案来尊重特定的交易所。 QuantLib 库有一个相当知名和受人尊敬的库,涵盖了 60 多个交易所,但由于涉及到整个 C++ 库,因此可能很难访问。

我最近只提取了日历,以便在包qlcal 中与 R 一起使用,这可以提供帮助。

对于您的 12 月示例:

> library(qlcal)
> setCalendar("UnitedStates::NYSE")  # New York Stock Exchange
> bd <- getBusinessDays(as.Date("2021-12-01"), as.Date("2021-12-31"))
> data.frame(day=bd, index=seq_along(bd))
          day index
1  2021-12-01     1
2  2021-12-02     2
3  2021-12-03     3
4  2021-12-06     4
5  2021-12-07     5
6  2021-12-08     6
7  2021-12-09     7
8  2021-12-10     8
9  2021-12-13     9
10 2021-12-14    10
11 2021-12-15    11
12 2021-12-16    12
13 2021-12-17    13
14 2021-12-20    14
15 2021-12-21    15
16 2021-12-22    16
17 2021-12-23    17
18 2021-12-27    18
19 2021-12-28    19
20 2021-12-29    20
21 2021-12-30    21
22 2021-12-31    22

一月也是如此

> bd <- getBusinessDays(as.Date("2022-01-01"), as.Date("2022-01-31"))
> data.frame(day=bd, index=seq_along(bd))
          day index
1  2022-01-03     1
2  2022-01-04     2
3  2022-01-05     3
4  2022-01-06     4
5  2022-01-07     5
6  2022-01-10     6
7  2022-01-11     7
8  2022-01-12     8
9  2022-01-13     9
10 2022-01-14    10
11 2022-01-18    11
12 2022-01-19    12
13 2022-01-20    13
14 2022-01-21    14
15 2022-01-24    15
16 2022-01-25    16
17 2022-01-26    17
18 2022-01-27    18
19 2022-01-28    19
20 2022-01-31    20
> 

【讨论】:

    【解决方案2】:

    假设我们可以忘记假期,那么我们可以使用以下函数:

    business_day <- function(date) {
      vapply(date, function(d) {
        vec <- seq(lubridate::floor_date(d, "month"), d, by = "1 day")
        vec <- lubridate::wday(vec)
        length(which(vec > 1 & vec < 7))
      }, numeric(1))
    }
    

    这只是从月初到给定日期的顺序,并计算所有不是星期六或星期日的日子。它是矢量化的,因此它可以在任何给定数量的日期上工作。例如:

    dates <- as.POSIXct(c("2021-07-26", "2022-02-04", "1999-03-21"))
    
    business_day(dates)
    #> [1] 18  4 15
    

    reprex package 创建于 2022-01-31 (v2.0.1)

    【讨论】:

    • 你摇滚!谢谢!我将暂时使用,直到我弄清楚假期。
    • 正确但不考虑假期
    【解决方案3】:

    首先检查日期是否真的是工作日;如果是周末或节假日返回-1(不要带字符串,否则输出为字符)。

    否则,从相应月份的01 开始的seq.Date 到相应的日期,删除以S 开始的weekdays 以及那些在假期定义向量h 中的那些(如果已定义) .

    实际上,R 可以在不使用任何包的情况下做到这一点,而且速度要快得多。

    因此,我们可以像这样定义Vectorized 函数:

    f <- Vectorize(\(d, h=NULL) {
      stopifnot(inherits(d, c('Date')))
      stopifnot(inherits(h, c('Date', 'NULL')))
      if (grepl('S', weekdays(d)) | d %in% h) {
        return(-1L)
      } else {
        s <- seq.Date(as.Date(paste0(substr(d, 1, 7), '-01')), d, 1L)
        return(length(s[!grepl('S', weekdays(s)) & !s %in% h]))
      }
     }, vectorize.args='d')
    
    f(dates)
    # [1] 10 21 20 -1
    f(dates, holydays)
    # [1] 10 21 18 -1
    

    注意:使用 R >= 4.1。

    请注意,在 R 中,您应该始终使用所需的日期格式,并且可能需要先进行转换:

    dates <- as.Date(c("12/14/2021", "01/31/2022"), format='%m/%d/%Y')
    

    如果星期六是工作日,请在grepl 中使用'Su',或者在您的区域设置中使用'Sunday',只需尝试weekdays(dates)


    数据:

    dates <- as.Date(c("2022-12-14", "2022-01-31", "2022-04-28", "2022-12-31"))
    
    holydays <- as.Date(c("2022-01-01", "2022-04-15", "2022-04-18", "2022-05-01",
                          "2022-05-26", "2022-06-06", "2022-08-01", "2022-12-25", 
                          "2022-12-26"))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 2017-07-06
      • 1970-01-01
      • 1970-01-01
      • 2021-07-12
      相关资源
      最近更新 更多