每月日期的顺序,确保它是同一天,或在无效的情况下的最后一天

eli*_*ing 6 r seq

给定一个初始日期,我想生成一个月度间隔的日期序列,确保每个元素与初始日期或月份的最后一天具有相同的日期,以防同一天产生无效日期.

听起来很标准,对吧?

使用difftime是不可能的.这是帮助文件的difftime说法:

诸如"月"之类的单位是不可能的,因为它们不是恒定的长度.要创建月,季度或年的间隔,请使用seq.Date或seq.POSIXt.

但随后查看seq.POSIXt我的帮助文件,发现:

使用"月份"首先提前一个月而不更改日期:如果这导致该月的无效日期,则向前计入下个月:请参阅示例.

这是帮助文件中的示例.

seq(ISOdate(2000,1,31), by = "month", length.out = 4)
> seq(ISOdate(2000,1,31), by = "month", length.out = 4)
[1] "2000-01-31 12:00:00 GMT" "2000-03-02 12:00:00 GMT" 
"2000-03-31 12:00:00 GMT" "2000-05-01 12:00:00 GMT"
Run Code Online (Sandbox Code Playgroud)

因此,鉴于初始日期是在第31天,这将在2月,4月等产生无效日期.因此,序列最终实际上跳过那些月份,因为它"向前计数"并最终在3月02日结束,而不是二月-29.

如果我从2000-01-31开始,我希望序列如下:

  • 2000-01-31
  • 2000-02- 29
  • 2000年3月31日
  • 2000-04-30
  • ...

它应该正确处理闰年,所以如果初始日期是2015-01-31,那么序列应该是:

  • 2015年1月31日
  • 2015-02- 28
  • 2015年3月31日
  • 2015年4月30日
  • ...

这些只是用来说明问题的例子,我不提前知道初始日期,也不能假设它.初始日期可能在月中(2015-01-15),在这种情况下seq工作正常.但是,正如在示例中那样,在月末使用seq单独使用会产生问题(第29,30和31天)也是如此.我不能假设初始日期是该月的最后一天.

我环顾四周试图寻找解决方案.在SO中的一些问题中(例如这里)有一个"技巧"来获得一个月的最后一天,通过获取下个月的第一天并简单地减去1.并且找到第一天是"容易的",因为它就在第一天.

所以到目前为止我的解决方案是

# Given an initial date for my sequence
initial_date <- as.Date("2015-01-31")

# Find the first day of the month
library(magrittr) # to use pipes and make the code more readable
firs_day_of_month <- initial_date %>% 
    format("%Y-%m") %>% 
    paste0("-01") %>% 
    as.Date()

# Generate a sequence from initial date, using seq  
# This is the sequence that will have incorrect values in months that would
# have invalid dates
given_dat_seq <- seq(initial_date, by = "month", length.out = 4)

# And then generate an auxiliary sequence for the last day of the month
# I do this generating a sequence that starts the first day of the 
# same month as initial date and it goes one month further 
# (lenght 5 instead of 4) and substract 1 to all the elements
last_day_seq <- seq(firs_day_of_month, by = "month", length.out = 5)-1

# And finally, for each pair of elements, I take the min date of both
pmin(given_dat_seq, last_day_seq[2:5])
Run Code Online (Sandbox Code Playgroud)

它有效,但同时又有点愚蠢,愚蠢和错综复杂.所以我不喜欢它.最重要的是,我无法相信在R中没有更简单的方法可以做到这一点.

有人可以指点我一个更简单的解决方案吗?(我想它应该一样简单seq(initial_date, "month", 4),但显然不是).我用Google搜索并在SO和R邮件列表中查看,但除了我上面提到的技巧之外,我找不到解决方案.

jth*_*ner 6

最简单的解决方案是来自lubridate的%m +%,这解决了这个确切的问题.所以:

seq_monthly <- function(from,length.out) {
  return(from %m+% months(c(0:(length.out-1))))
}
Run Code Online (Sandbox Code Playgroud)

输出:

> seq_monthly(as.Date("2015-01-31"),length.out=4)
[1] "2015-01-31" "2015-02-28" "2015-03-31" "2015-04-30"
Run Code Online (Sandbox Code Playgroud)