每月日期的顺序,确保它是同一天,或者在无效的情况下是一个月的最后一天
sequence of monthly dates making sure it's the same day, or the last day of month in case of invalid
给定一个初始日期,我想生成一个以月为间隔的日期序列,确保每个元素的日期与初始日期或该月的最后一天相同,以防同一天产生无效日期.
听起来很标准,对吧?
无法使用 difftime
。 difftime
的帮助文件是这样说的:
Units such as "months" are not possible as they are not of constant
length. To create intervals of months, quarters or years use seq.Date
or seq.POSIXt.
但后来查看 seq.POSIXt
的帮助文件,我发现:
Using "month" first advances the month without changing the day: if
this results in an invalid day of the month, it is counted forward
into the next month: see the examples.
这是帮助文件中的示例。
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"
因此,鉴于初始日期是第 31 天,这将在二月、四月等产生无效日期。因此,序列最终实际上跳过了这些月份,因为它 "counts forward" 并以3 月 2 日,而不是 2 月 29 日。
如果我从 2000-01-31 开始,我希望顺序如下:
- 2000-01-31
- 2000-02-29
- 2000-03-31
- 2000-04-30
- ...
它应该正确处理闰年,所以如果初始日期是 2015-01-31,序列应该是:
- 2015-01-31
- 2015-02-28
- 2015-03-31
- 2015-04-30
- ...
这些只是为了说明问题的例子,我事先并不知道初始日期,也无法假设任何事情。初始日期很可能在月中 (2015-01-15),在这种情况下 seq
可以正常工作。但也可以像示例中那样,在月末的日期单独使用 seq
会出现问题(第 29、30 和 31 天)。我也不能假设初始日期是该月的最后一天。
我四处寻找解决方案。在 SO 的一些问题中(例如这里)有一个 "trick" 来获取一个月的最后一天,方法是获取下个月的第一天并简单地减去 1。找到第一天是 "easy" 因为这只是第 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])
它有效,但与此同时,它有点愚蠢、古怪和令人费解。所以我不喜欢它。最重要的是,我不敢相信在 R 中没有更简单的方法可以做到这一点。
谁能告诉我一个更简单的解决方案? (我想它应该像 seq(initial_date, "month", 4)
一样简单,但显然不是)。我用谷歌搜索并查看了 SO 和 R 邮件列表中的此处,但除了我上面提到的技巧外,我找不到解决方案。
最简单的解决方案是来自 lubridate 的 %m+%,它正好解决了这个问题。所以:
seq_monthly <- function(from,length.out) {
return(from %m+% months(c(0:(length.out-1))))
}
输出:
> seq_monthly(as.Date("2015-01-31"),length.out=4)
[1] "2015-01-31" "2015-02-28" "2015-03-31" "2015-04-30"
与 lubridate 答案类似,这是一个使用 RcppBDT 的答案(它包装了来自 C++ 的 Boost Date.Time 库)
R> dt <- new(bdtDt, 2010, 1, 31); for (i in 1:5) { dt$addMonths(i); print(dt) }
[1] "2010-02-28"
[1] "2010-04-30"
[1] "2010-07-31"
[1] "2010-11-30"
[1] "2011-04-30"
R> dt <- new(bdtDt, 2000, 1, 31); for (i in 1:5) { dt$addMonths(i); print(dt) }
[1] "2000-02-29"
[1] "2000-04-30"
[1] "2000-07-31"
[1] "2000-11-30"
[1] "2001-04-30"
R>
给定一个初始日期,我想生成一个以月为间隔的日期序列,确保每个元素的日期与初始日期或该月的最后一天相同,以防同一天产生无效日期.
听起来很标准,对吧?
无法使用 difftime
。 difftime
的帮助文件是这样说的:
Units such as "months" are not possible as they are not of constant length. To create intervals of months, quarters or years use seq.Date or seq.POSIXt.
但后来查看 seq.POSIXt
的帮助文件,我发现:
Using "month" first advances the month without changing the day: if this results in an invalid day of the month, it is counted forward into the next month: see the examples.
这是帮助文件中的示例。
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"
因此,鉴于初始日期是第 31 天,这将在二月、四月等产生无效日期。因此,序列最终实际上跳过了这些月份,因为它 "counts forward" 并以3 月 2 日,而不是 2 月 29 日。
如果我从 2000-01-31 开始,我希望顺序如下:
- 2000-01-31
- 2000-02-29
- 2000-03-31
- 2000-04-30
- ...
它应该正确处理闰年,所以如果初始日期是 2015-01-31,序列应该是:
- 2015-01-31
- 2015-02-28
- 2015-03-31
- 2015-04-30
- ...
这些只是为了说明问题的例子,我事先并不知道初始日期,也无法假设任何事情。初始日期很可能在月中 (2015-01-15),在这种情况下 seq
可以正常工作。但也可以像示例中那样,在月末的日期单独使用 seq
会出现问题(第 29、30 和 31 天)。我也不能假设初始日期是该月的最后一天。
我四处寻找解决方案。在 SO 的一些问题中(例如这里)有一个 "trick" 来获取一个月的最后一天,方法是获取下个月的第一天并简单地减去 1。找到第一天是 "easy" 因为这只是第 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])
它有效,但与此同时,它有点愚蠢、古怪和令人费解。所以我不喜欢它。最重要的是,我不敢相信在 R 中没有更简单的方法可以做到这一点。
谁能告诉我一个更简单的解决方案? (我想它应该像 seq(initial_date, "month", 4)
一样简单,但显然不是)。我用谷歌搜索并查看了 SO 和 R 邮件列表中的此处,但除了我上面提到的技巧外,我找不到解决方案。
最简单的解决方案是来自 lubridate 的 %m+%,它正好解决了这个问题。所以:
seq_monthly <- function(from,length.out) {
return(from %m+% months(c(0:(length.out-1))))
}
输出:
> seq_monthly(as.Date("2015-01-31"),length.out=4)
[1] "2015-01-31" "2015-02-28" "2015-03-31" "2015-04-30"
与 lubridate 答案类似,这是一个使用 RcppBDT 的答案(它包装了来自 C++ 的 Boost Date.Time 库)
R> dt <- new(bdtDt, 2010, 1, 31); for (i in 1:5) { dt$addMonths(i); print(dt) }
[1] "2010-02-28"
[1] "2010-04-30"
[1] "2010-07-31"
[1] "2010-11-30"
[1] "2011-04-30"
R> dt <- new(bdtDt, 2000, 1, 31); for (i in 1:5) { dt$addMonths(i); print(dt) }
[1] "2000-02-29"
[1] "2000-04-30"
[1] "2000-07-31"
[1] "2000-11-30"
[1] "2001-04-30"
R>