R 中日期的定制地板/天花板

Customized floor/ceiling for dates in R

提问人:Algebro1000 提问时间:1/10/2023 更新时间:1/10/2023 访问量:69

问:

假设我有一个从到哪里的日期范围。startendstart <- as.Date(2009-11-05), end <- as.Date(2009-12-17)

我想要一个函数,它本质上充当自定义的地板/天花板并返回一个日期间隔,使下限是形式“yyyy-mm-23”的第一个日期,小于或等于“2009-11-05”,上限是第一个大于或等于“2009-12-17”且格式为“yyyy-mm-22”的日期。

在上面的示例中,该函数应返回间隔 ('2009-10-23, 2009-12-22)。

我试过使用 seq。日期并在其上使用长度功能,但这似乎很乏味,我想知道是否有更快的解决方案。

谢谢

r 日期时间 间隔 地板 ceil

评论


答:

2赞 Vinícius Félix 1/10/2023 #1

我不知道有这样的函数,但我会编写如下代码:

法典

custom_bound <- function(date, type, ref_day){
  
  obs_day <- lubridate::day(date)

  if(type == "lower"){aux <- -1}
  
  if(type == "upper"){aux <- 1}      
  
  while(obs_day != ref_day ){
    
    date <- date + days(aux)
    
    obs_day <- lubridate::day(date)
    
  }
  
  return(date)
  
}

输出

> custom_bound(date = as.Date("2009-11-05"),type = "lower",ref_day = 23)
[1] "2009-10-23"

> custom_bound(date = as.Date("2009-12-17"),type = "upper",ref_day = 22)
[1] "2009-12-22"
1赞 Ben 1/10/2023 #2

这可能是另一种尝试的方法。使用创建新的开始日期和结束日期,替换 23 日和 22 日。然后,如果开始日期早于 23 日,则减去一个月。同样,如果结束日期超过 22 日,则增加一个月。lubridate

start <- as.Date("2009-11-05")
end <- as.Date("2009-12-17")

library(lubridate)

my_fun <- function(start, end) {
  new_start <- start
  day(new_start) <- 23
  new_end <- end
  day(new_end) <- 22
  if (day(start) < 23) new_start = new_start %m-% months(1)
  if (day(end) > 22) new_end = new_end %m+% months(1)
  return(interval(new_start, new_end))
}

my_fun(start, end)

输出

[1] 2009-10-23 UTC--2009-12-22 UTC

编辑:在评论中,该月的参考日期可能大于 28,这可能导致日期无效。要考虑这种可能性,一种方法是使用可以处理无效日期(例如,2 月 31 日)的包,然后解析为最接近的日期。clock

start <- as.Date("2009-03-30")
end <- as.Date("2009-12-17")

reference <- 31

library(lubridate)
library(clock)

my_fun <- function(start, end, reference) {
  new_start <- set_day(year_month_day(year(start), month(start)), reference)
  new_end <- set_day(year_month_day(year(end), month(end)), reference)
  if (day(start) < reference) new_start = add_months(new_start, -1) 
  if (day(end) > reference) new_end = add_months(new_end, 1)
  new_start = invalid_resolve(new_start, invalid = "previous") 
  new_end = invalid_resolve(new_end, invalid = "next")
  return(c(new_start, new_end))
}

my_fun(start, end, reference)

输出

[1] "2009-02-28" "2009-12-31"

评论

0赞 Algebro1000 1/10/2023
谢谢,这正是我想要的!漂亮而简单,避免了使用 seq 创建日期序列。日期
0赞 Algebro1000 1/10/2023
如果我们以 31 日为参考日,开始日期是 2009-02-28,该怎么办?我们当然不能设置 day(new_start) <- 31,但我们希望new_start是 2009-02-28?
0赞 Ben 1/10/2023
@Lollo231000 如果可以有一个大于 28 的“参考”日期,则可以使用该包,然后解析无效日期。请参阅上面编辑的答案中的第二个示例。让我知道这是否是你的想法。clock
1赞 Algebro1000 1/11/2023
多么精彩的套餐!非常感谢您的出色解决方案。