【问题标题】:Identify 3rd Friday in a sequence of Dates在一系列日期中识别第三个星期五
【发布时间】:2016-10-07 21:27:15
【问题描述】:

下面的代码有一个日期列表,如果日期是该月的最后一天,对于数据集中的所有日期(不在日历上),它会将日期标记为该月的最后一天。

data = data.frame(day  = seq(as.Date("2014-01-01"),as.Date("2016-05-10"),"day"), weekday = weekdays ( seq(as.Date("2014-01-01"),as.Date("2016-05-10"),"day")))
excludeDays <- c("Saturday", "Sunday")
data <- subset(data, !weekdays(data$day) %in% excludeDays) #exclude weekend
setDT(data)
data[, LastDayInMonth := day == max(day), by = list(year(day),month(day)  )]
head(data,100)

我还需要在数据中的任何星期五添加一个标记 (TRUE/FALSE) 的列,该数据是该月的第三个星期五。有什么想法吗?

例如 2014-01-17、2014-02-21、......2014-05-16 等......是第三个星期五。

谢谢。

【问题讨论】:

  • 可以直接用data.table()函数创建data.table。

标签: r date data.table lubridate


【解决方案1】:

要在月中开始数据时覆盖极端情况,您可以试试这个:

occ <- 3L   # 3rd occurrence of selected weekday
data[, ThirdFridayInMonth := weekday == "Friday" &
       as.integer(day - lubridate::floor_date(day, "month")) %/% 7L + 1L == occ]

这也适用于该月的其他工作日,例如,在每个第一个星期一标记。

编辑:按要求解释。

基本思想是每个月的第一个星期五必须是每个月的前 7 天之一,第二个星期五必须是第 8 到 14 天之一,依此类推。因此,lubridate::floor_date(day, "month") 计算 day 所在月份的第一天。现在,您构建了一个需要转换为 integerdifftime 对象的差异。如果day 恰好是一个月的第一天,则为0。现在您使用整数除法%/%,它在该月的前 7 天返回 0,在第二个 7 天返回 1,依此类推,然后通过加 1 进行调整。

编辑 2:改进的代码

在编写说明时,我意识到代码可以进一步改进。

我们可以直接使用月份中的某一天,这样我们就不用计算日期的差异和随后的整数转换:

data[, ThirdFridayInMonth := weekday == "Friday" &
       (mday(day) - 1) %/% 7L + 1L == occ]

在这里,我使用mdaydata.table 包的一部分)而不是as.integer(format(day, "%d"))

【讨论】:

  • 你能解释一下 as.integer(day - lubridate::floor_date(day, "month")) %/% 7L + 1L == occ] 在做什么吗?
  • @user3022875 感谢您的提问。我在答案中添加了解释和改进的代码。
【解决方案2】:
data[, numWeek := 1:.N, by = .(year(day), month(day), weekday)]

data[numWeek == 3 & weekday == "Friday"]

或添加为列:

data[, is3rdFriday := numWeek == 3 & weekday == "Friday"]

如果您要在日历中查找第三个星期五,而不是数据中的第三个星期五,那么您可以更简单:

data[, is3rdFriday := (mday(day) - 1) %/% 7 + 1 == 3 & weekday == "Friday"]

【讨论】:

  • 这可行,但是当数据在第二周开始时存在一个极端情况。那么第三个星期五算作第二个星期五,不会被标记
  • 要覆盖角落案例,您可以这样做data[, ThirdFridayInMonth := weekday == "Friday" &amp; as.integer(day - lubridate::floor_date(day, "month")) %/% 7L + 1L == 3L]
  • @user3022875 不清楚 OP 这就是你想要的 - 添加
【解决方案3】:

另一种方法:

data[, is3Friday:=cumsum(weekday=="Friday"), by=substring(day, 1, 7)]
data[, is3Friday:=ifelse(weekday=="Friday" & is3Friday==3, TRUE, FALSE)]

data[is3Friday==TRUE]
           day weekday LastDayInMonth is3Friday
 1: 2014-01-17  Friday          FALSE      TRUE
 2: 2014-02-21  Friday          FALSE      TRUE
 3: 2014-03-21  Friday          FALSE      TRUE
 4: 2014-04-18  Friday          FALSE      TRUE
 5: 2014-05-16  Friday          FALSE      TRUE
 6: 2014-06-20  Friday          FALSE      TRUE

【讨论】:

    【解决方案4】:

    试试这个 - 没有 data.table:

    library(lubridate)
    library(dplyr)
    data <- data.frame(day  = seq(as.Date("2014-01-01"),
                                  as.Date("2016-05-10"),"day"),
                      weekday = weekdays(seq(as.Date("2014-01-01"),
                                             as.Date("2016-05-10"),"day")))
    data$year_month <- floor_date(data$day, unit = 'month')
    fri3 <- filter(data, weekday == 'Friday') %>%
      group_by(year_month) %>% slice(3)
    
    data$Third_Fri <- data$day %in% fri3$day
    

    您可以稍后删除 year_month

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-04
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多