给定工作日的子集日期和 select 下一个日期(如果缺少工作日)
Subset dates with a given weekday and select next date if weekday is missing
我能够在 SO 上找到很多关于将日期子集化到某个工作日的信息(例如 Get Dates of a Certain Weekday from a Year in R)。但是,我找不到任何实现我想要的回退逻辑的东西。具体来说,如果给定周内不存在给定工作日,我想获取下一个可用日期,不包括周六和周日。
例如,从一个日期向量中,我想 select 所有与星期四相对应的日期。但是,在缺少星期四的几周内,我应该选择下一个工作日的日期。在下面的示例中,这是第二天,即星期五。
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++ 库)。
我有信心我可以写一些东西来做我想做的事,但我很难找到创造简短而优雅的东西的方法。
可能不是最优雅的方式,但我认为它应该有效:)
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
我打破了你 "no external dependencies" 的条件,但由于你已经在使用 lubridate
(这是一个依赖项 ;-)),我将为你提供一个利用 [=13] 的解决方案=] 和 lag
来自 dplyr
。你可以自己写那些,看看源代码,如果它真的是一个困难的条件。
我正在做的是通过计算一种 运行 天的差异来找出 "skips" 在序列中的位置。一旦我们知道跳过的位置,我们就可以滚动到序列中的下一个数据,无论它是什么。现在,很可能这不是星期五,而是星期六。在那种情况下,你将不得不弄清楚你是否还想要下周五,即使中间有一个周四。
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
元素不存在的边缘情况,但我会把它留给您处理。
findInterval
的替代方案。
创建日期序列 ('tmp'),从 min
'dates' 周的焦点工作日 ('wd') 到 max
'dates'.
Select 对应于焦点工作日 ('wds') 的日期。
Select 个工作日,从 'dates' ('dates_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"]
如果需要,只需像上面那样包装一个函数。
我能够在 SO 上找到很多关于将日期子集化到某个工作日的信息(例如 Get Dates of a Certain Weekday from a Year in R)。但是,我找不到任何实现我想要的回退逻辑的东西。具体来说,如果给定周内不存在给定工作日,我想获取下一个可用日期,不包括周六和周日。
例如,从一个日期向量中,我想 select 所有与星期四相对应的日期。但是,在缺少星期四的几周内,我应该选择下一个工作日的日期。在下面的示例中,这是第二天,即星期五。
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++ 库)。
我有信心我可以写一些东西来做我想做的事,但我很难找到创造简短而优雅的东西的方法。
可能不是最优雅的方式,但我认为它应该有效:)
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
我打破了你 "no external dependencies" 的条件,但由于你已经在使用 lubridate
(这是一个依赖项 ;-)),我将为你提供一个利用 [=13] 的解决方案=] 和 lag
来自 dplyr
。你可以自己写那些,看看源代码,如果它真的是一个困难的条件。
我正在做的是通过计算一种 运行 天的差异来找出 "skips" 在序列中的位置。一旦我们知道跳过的位置,我们就可以滚动到序列中的下一个数据,无论它是什么。现在,很可能这不是星期五,而是星期六。在那种情况下,你将不得不弄清楚你是否还想要下周五,即使中间有一个周四。
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
元素不存在的边缘情况,但我会把它留给您处理。
findInterval
的替代方案。
创建日期序列 ('tmp'),从 min
'dates' 周的焦点工作日 ('wd') 到 max
'dates'.
Select 对应于焦点工作日 ('wds') 的日期。
Select 个工作日,从 'dates' ('dates_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"]
如果需要,只需像上面那样包装一个函数。