按月生成日期序列

问题描述 投票:0回答:4

这就是我想要的结果:

library(lubridate)

res <- structure(
  c(
    16160, 16251, 16343, 16435, 16525, 16616, 16708, 
    16800, 16891, 16982, 17074, 17166, 17256, 17347, 17439, 17531, 
    17621, 17712, 17804, 17896, 17986, 18077, 18169, 18261, 18352, 
    18443, 18535, 18627, 18717, 18808, 18900
  ), 
  class = "Date"
)

res
#>  [1] "2014-03-31" "2014-06-30" "2014-09-30" "2014-12-31" "2015-03-31"
#>  [6] "2015-06-30" "2015-09-30" "2015-12-31" "2016-03-31" "2016-06-30"
#> [11] "2016-09-30" "2016-12-31" "2017-03-31" "2017-06-30" "2017-09-30"
#> [16] "2017-12-31" "2018-03-31" "2018-06-30" "2018-09-30" "2018-12-31"
#> [21] "2019-03-31" "2019-06-30" "2019-09-30" "2019-12-31" "2020-03-31"
#> [26] "2020-06-30" "2020-09-30" "2020-12-31" "2021-03-31" "2021-06-30"
#> [31] "2021-09-30"

不过我必须将其生成为序列。

我已经设法使用 while 循环获得结果:

dateA <- min(res)
dateB <- max(res)
by <- months(3)

# initialize a vector to contain all dates:
myres <- dateA

while (TRUE) {
  n <- length(myres)
  next_date <- ceiling_date(myres[n] %m+% by, unit = by) - 1
  
  # if next_date is less than dateB, add it to myres & continue looping:
  if (next_date < dateB) {
    myres[n + 1] <- next_date
    next
  }
  
  # if next_date is equal to or greater than dateB, add it and terminate loop:
  if (next_date >= dateB) {
    myres[n + 1] <- next_date
    break
  }
}

identical(res, myres)
#> [1] TRUE

我希望得到类似的东西:

x <- seq.Date(from = dateA, to = dateB, by = "3 months")
x
#>  [1] "2014-03-31" "2014-07-01" "2014-10-01" "2014-12-31" "2015-03-31"
#>  [6] "2015-07-01" "2015-10-01" "2015-12-31" "2016-03-31" "2016-07-01"
#> [11] "2016-10-01" "2016-12-31" "2017-03-31" "2017-07-01" "2017-10-01"
#> [16] "2017-12-31" "2018-03-31" "2018-07-01" "2018-10-01" "2018-12-31"
#> [21] "2019-03-31" "2019-07-01" "2019-10-01" "2019-12-31" "2020-03-31"
#> [26] "2020-07-01" "2020-10-01" "2020-12-31" "2021-03-31" "2021-07-01"

但这似乎不起作用。

有没有比

while
循环更简单的方法?如果有任何正确方向的指示,我将不胜感激。

r lubridate
4个回答
5
投票

1) Base R 将开始日期和结束日期加一即可获取下个月的开始和结束日期,生成序列并减一即可获取月份的结束日期。没有使用任何封装。

st <- as.Date("2014-03-31")
en <- as.Date("2021-09-30")
seq(st + 1, en + 1, by = "3 months") - 1
##  [1] "2014-03-31" "2014-06-30" "2014-09-30" "2014-12-31" "2015-03-31"
##  [6] "2015-06-30" "2015-09-30" "2015-12-31" "2016-03-31" "2016-06-30"
## [11] "2016-09-30" "2016-12-31" "2017-03-31" "2017-06-30" "2017-09-30"
## [16] "2017-12-31" "2018-03-31" "2018-06-30" "2018-09-30" "2018-12-31"
## [21] "2019-03-31" "2019-06-30" "2019-09-30" "2019-12-31" "2020-03-31"
## [26] "2020-06-30" "2020-09-30" "2020-12-31" "2021-03-31" "2021-06-30"
## [31] "2021-09-30"

2)yearmon也可以创建一个仅包含年/月而没有日期的yearmon序列,然后将

as.Date.yearmon
frac=1
参数一起使用,指示它在转换为日期时使用月末类。

library(zoo)

st <- as.yearmon("2014-03-31")
en <- as.yearmon("2021-09-30")
as.Date(seq(st, en, 3/12), frac = 1)

3) mondate mondate 包理解月末。计算开始和结束之间的月份差异,然后添加适当的序列,如图所示。

library(mondate)

st <- mondate("2014-03-31")
en <- mondate("2021-09-30")
as.Date(mondate(st + seq(0, as.numeric(en - st), 3)))

4) lubridate 查找两个日期之间的月份数,然后添加月份序列。在这种情况下,它会理解我们想要月末。

library(lubridate)

st <- ymd("2014-03-31")
en <- ymd("2021-09-30")
st %m+% months(seq(0, round(interval(st, en) / months(1)), 3))

3
投票

假设您只是从极端开始,

res <- range(res)
res
# [1] "2014-03-31" "2021-09-30"
do.call(seq.Date, c(as.list(lubridate::ceiling_date(res, unit = "month")), by = "3 months")) - 1L
#  [1] "2014-03-31" "2014-06-30" "2014-09-30" "2014-12-31" "2015-03-31" "2015-06-30" "2015-09-30" "2015-12-31" "2016-03-31"
# [10] "2016-06-30" "2016-09-30" "2016-12-31" "2017-03-31" "2017-06-30" "2017-09-30" "2017-12-31" "2018-03-31" "2018-06-30"
# [19] "2018-09-30" "2018-12-31" "2019-03-31" "2019-06-30" "2019-09-30" "2019-12-31" "2020-03-31" "2020-06-30" "2020-09-30"
# [28] "2020-12-31" "2021-03-31" "2021-06-30" "2021-09-30"

如果您对

do.call
部分不太满意,那么

res <- lubridate::ceiling_date(range(res), unit = "month")
res
# [1] "2014-04-01" "2021-10-01"
seq.Date(res[1], res[2], by = "3 months")
#  [1] "2014-04-01" "2014-07-01" "2014-10-01" "2015-01-01" "2015-04-01" "2015-07-01" "2015-10-01" "2016-01-01" "2016-04-01"
# [10] "2016-07-01" "2016-10-01" "2017-01-01" "2017-04-01" "2017-07-01" "2017-10-01" "2018-01-01" "2018-04-01" "2018-07-01"
# [19] "2018-10-01" "2019-01-01" "2019-04-01" "2019-07-01" "2019-10-01" "2020-01-01" "2020-04-01" "2020-07-01" "2020-10-01"
# [28] "2021-01-01" "2021-04-01" "2021-07-01" "2021-10-01"

2
投票

我认为,在生成序列时,通过从等式中取出月份中的某一天部分,可以最优雅地解决这个问题。使用 clock 包,您可以生成月份精度序列,然后轻松将日期设置为该月的最后一天。

library(clock)

start <- as.Date("2014-03-31")
end <- as.Date("2021-09-30")

start <- calendar_narrow(as_year_month_day(start), "month")
end <- calendar_narrow(as_year_month_day(end), "month")

# Month precision dates
start
#> <year_month_day<month>[1]>
#> [1] "2014-03"
end
#> <year_month_day<month>[1]>
#> [1] "2021-09"

seq(start, end, by = 3) |>
  set_day("last") |>
  as.Date()
#>  [1] "2014-03-31" "2014-06-30" "2014-09-30" "2014-12-31" "2015-03-31"
#>  [6] "2015-06-30" "2015-09-30" "2015-12-31" "2016-03-31" "2016-06-30"
#> [11] "2016-09-30" "2016-12-31" "2017-03-31" "2017-06-30" "2017-09-30"
#> [16] "2017-12-31" "2018-03-31" "2018-06-30" "2018-09-30" "2018-12-31"
#> [21] "2019-03-31" "2019-06-30" "2019-09-30" "2019-12-31" "2020-03-31"
#> [26] "2020-06-30" "2020-09-30" "2020-12-31" "2021-03-31" "2021-06-30"
#> [31] "2021-09-30"

reprex 包于 2022 年 8 月 15 日创建(v2.0.1)


0
投票

将连续 2 个月的第一天的日期序列并计算差值。

days_in <- function(a) {
ym <- format(a, "%Y-%m")
first_of_m <- as.Date(paste0(ym, -1))
diff(seq(first_of_m, by='months', length=2))
}

在闰年测试结果:

> date <- as.Date("2020-01-10")
> Reduce(rbind, lapply(seq(as.Date("2020-01-02"), by="month",length=12),
+                      \(X) c(format(X, "%B"), days_in(X))))
     [,1]        [,2]
init "Januar"    "31"
     "Februar"   "29"
     "März"      "31"
     "April"     "30"
     "Mai"       "31"
     "Juni"      "30"
     "Juli"      "31"
     "August"    "31"
     "September" "30"
     "Oktober"   "31"
     "November"  "30"
     "Dezember"  "31"
© www.soinside.com 2019 - 2024. All rights reserved.