生成两年内每个月的最后一天的序列

46

我使用lubridate并认为这将非常容易

ymd("2010-01-31")+months(0:23)

但看看你得到的东西,一切都混乱了!

 [1] "2010-01-31 UTC" "2010-03-03 UTC" "2010-03-31 UTC" "2010-05-01 UTC" "2010-05-31 UTC" "2010-07-01 UTC" "2010-07-31 UTC" "2010-08-31 UTC" "2010-10-01 UTC"
[10] "2010-10-31 UTC" "2010-12-01 UTC" "2010-12-31 UTC" "2011-01-31 UTC" "2011-03-03 UTC" "2011-03-31 UTC" "2011-05-01 UTC" "2011-05-31 UTC" "2011-07-01 UTC"
[19] "2011-07-31 UTC" "2011-08-31 UTC" "2011-10-01 UTC" "2011-10-31 UTC" "2011-12-01 UTC" "2011-12-31 UTC"

然后我了解到`lubridate`可以处理间隔、持续时间和期间等现象。好吧,我意识到一个月实际上是由(365*4+1)/48 = 30.438天定义的天数。于是我试图变得聪明并将其重新编写为

ymd("2010-01-31")+ as.period(months(0:23))

但是这只会产生一个错误。

Error in as.period.default(months(0:23)) : 
  (list) object cannot be coerced to type 'double'
3个回答

101

是的,你找到了正确的方法:从下个月的第一天往回推一天。

以下是使用基础R语言编写的一行代码:

R> seq(as.Date("2010-02-01"), length=24, by="1 month") - 1
 [1] "2010-01-31" "2010-02-28" "2010-03-31" "2010-04-30" "2010-05-31"
 [6] "2010-06-30" "2010-07-31" "2010-08-31" "2010-09-30" "2010-10-31"
[11] "2010-11-30" "2010-12-31" "2011-01-31" "2011-02-28" "2011-03-31"
[16] "2011-04-30" "2011-05-31" "2011-06-30" "2011-07-31" "2011-08-31"
[21] "2011-09-30" "2011-10-31" "2011-11-30" "2011-12-31"
R> 

因此,像这样简单的任务不需要lubridate,尽管它是一个好的包,但它仍然对现有基本函数的重载给我留下了一定的危险感...


23

打一个问题的过程真是神奇,它能够集中创造力。我想我已经找到了答案。我不妨在这里发布,给下一个浪费时间的可怜人一个帮助。

ymd("2010-02-01")+ months(0:23)-days(1)

只需指定下一个月的第一天并生成一个序列,然后从中减去1天即可得到上个月的最后一天。

[1] "2010-01-31 UTC" "2010-02-28 UTC" "2010-03-31 UTC" "2010-04-30 UTC" "2010-05-31 UTC" "2010-06-30 UTC" "2010-07-31 UTC" "2010-08-31 UTC" "2010-09-30 UTC"
[10] "2010-10-31 UTC" "2010-11-30 UTC" "2010-12-31 UTC" "2011-01-31 UTC" "2011-02-28 UTC" "2011-03-31 UTC" "2011-04-30 UTC" "2011-05-31 UTC" "2011-06-30 UTC"
[19] "2011-07-31 UTC" "2011-08-31 UTC" "2011-09-30 UTC" "2011-10-31 UTC" "2011-11-30 UTC" "2011-12-31 UTC"

顺便问一下,我该怎么去掉这些讨厌的"UTC"标识。时区在需要时能够拯救生命,但大部分时间它们都很讨厌。


4
使用strftime(date)函数可以去掉时区信息。因此,strftime('2010-10-31 UTC')会返回2010-10-31 - Ramnath
@SachaEpskamp: Stackoverflow将只允许我在两天后接受我的答案。我想这是相当聪明的。可能还有一种更优雅的解决方法。 - Farrel
@Ramnath strftime给我返回的是前一天的日期,因为在我所在的时区UTC午夜实际上是前一天的晚上7点。我在东部时区。strftime(ymd("2010-02-01")+ months(0:23)-days(1)) [1] "2010-01-30 19:00:00" "2010-02-27 19:00:00" "2010-03-30 20:00:00" 等等。 - Farrel

0

tidyverse已经添加了clock包,此外还有lubridate包,该包具有各种日期算术的优秀功能。你可以有几种回答方式:

library(clock)

# sequence first day of every month and then set day to be last day
seq(as.Date("2010-01-01"), by = "1 month", length.out = 24) |> set_day("last")

date_seq 会生成一些无效的日期,例如 "2010-02-31",但您可以使用 invalid 参数来指定在这些情况下该怎么做。在这种情况下,返回到上一个有效日期。

start <- date_build(2010, 01, 31)
date_seq(start, by = duration_months(1), total_size = 24, invalid = "previous")

或者,您可以按顺序排列月份,然后在最后再添加最后一天:

start <- calendar_narrow(year_month_day(2010, 01, 01), "month") # [1] "2010-01"

seq(start, by = 1, length.out = 24) |>
  set_day("last") |>
  as.Date()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接