Как вычесть месяцы из даты в R?

Я пытаюсь вычесть n месяцев из даты следующим образом:

maturity <- as.Date("2012/12/31")

m <- as.POSIXlt(maturity)

m$mon <- m$mon - 6

но результирующая дата 01-Jul-2012, а не 30-Jun-2012, как я должен был ожидать. Есть ли короткий способ получить такой результат?

заранее спасибо


r
person user648905    schedule 07.03.2011    source источник
comment
Возможный дубликат Добавить месяц к дате   -  person Barker    schedule 05.01.2017
comment
@Barker На этот старый вопрос 2011 года уже есть отличный ответ, обобщающий несколько возможных подходов. Так что не вижу смысла объявлять это дубликатом Q 2013 года. Может быть, должно быть наоборот?   -  person Uwe    schedule 05.01.2017
comment
@UweBlock Другому вопросу тоже много лет, и на него также есть много отличных ответов. Также ответы на другой вопрос касаются крайних случаев, которых нет. Причина, по которой я пометил как дубликат, заключалась в том, что этот вопрос был тем, который я нашел легче всего, и он не решил мою проблему, и только после гораздо более тщательного поиска я нашел другой вопрос, на который действительно был ответ, который мне был нужен (особенно второй). Мета говорит, что менее полный вопрос должен быть дубликатом, даже если он новее.   -  person Barker    schedule 05.01.2017
comment
@Баркер Г. Ответ Гротендика уже включал lubridate и был обновлен.   -  person Uwe    schedule 05.01.2017
comment
@UweBlock хорошо, тогда вы хотите, чтобы я пометил другой вопрос как дубликат? Как снять флажок с этого вопроса?   -  person Barker    schedule 05.01.2017


Ответы (4)


1) дата последовательности. Обратите внимание, что в июне всего 30 дней, поэтому он не может дать 31 июня, вместо этого он дает 1 июля.

seq(as.Date("2012/12/31"), length = 2, by = "-6 months")[2]
## [1] "2012-07-01"

Если бы мы знали, что это конец месяца, мы могли бы сделать это:

seq(as.Date(cut(as.Date("2012/12/31"), "month")), length=2, by="-5 month")[2]-1
## "2012-06-30"

2) год. Кроме того, если бы мы знали, что это конец месяца, мы могли бы использовать класс "yearmon" пакета zoo следующим образом:

library(zoo)
as.Date(as.yearmon(as.Date("2012/12/31")) -.5, frac = 1)
## [1] "2012-06-30"

Это преобразует дату в "yearmon", вычитает 6 месяцев (0,5 года), а затем преобразует ее обратно в "Date", используя frac=1, что означает конец месяца (frac=0 означает начало месяца). Это также имеет то преимущество перед предыдущим решением, что оно автоматически векторизуется, т.е. as.Date(...) мог быть вектором дат.

Обратите внимание, что если класс "Date" используется только как способ представления месяцев, то мы можем полностью избавиться от него и напрямую использовать "yearmon", поскольку он моделирует то, что нам нужно в первую очередь:

as.yearmon("2012-12") - .5
## [1] "Jun 2012"

3) мондата. Третье решение — это пакет mondate, преимущество которого в том, что он возвращает конец месяца 6 месяцев назад, не зная, что наступил конец месяца:

library(mondate)
mondate("2011/12/31") - 6
## mondate: timeunits="months"
## [1] 2011/06/30

Это тоже векторизовано.

4) смазывать. Этот ответ на смазку был изменен в соответствии с изменениями в пакете:

library(lubridate)
as.Date("2012/12/31") %m-% months(6)
## [1] "2012-06-30"

lubridate также векторизован.

5) sqldf/SQLite

library(sqldf)
sqldf("select date('2012-12-31', '-6 months') as date")
##         date
## 1 2012-07-01

или если бы мы знали, что мы в конце месяца:

sqldf("select date('2012-12-31', '+1 day', '-6 months', '-1 day') as date")
##         date
## 1 2012-06-30
person G. Grothendieck    schedule 07.03.2011
comment
Привет, спасибо за ваш ответ, но проблема немного сложнее. Мне нужно рассчитать дату, например, за 6 месяцев до другой (т.е. 30 июня, если дата ввода будет 31 декабря). - person user648905; 12.03.2011
comment
Я не знаю, является ли дата начала концом месяца или нет. То, что я хотел бы получить, - это дата с тем же днем ​​​​начала, если она будет действительной (например, 15 марта, если дата ввода будет 15 сентября), или, в противном случае, с последним днем месяц (т.е. 28 февраля, если дата ввода 31 августа). В своем вопросе я упомянул дату конца месяца, так как для других дат формула, которую я использовал, работала нормально. Должен ли я определить функцию для этого? Спасибо - person user648905; 12.03.2011
comment
См. решение mondate в ответе. - person G. Grothendieck; 12.03.2011
comment
Странный. Если я использую x=2 или 4 для as.Date("2012/12/31") - months(x), я получаю ожидаемые результаты. С x = 1,3,5,6 я получил NA. Любая идея, почему это происходит? - person giordano; 31.03.2015
comment
@giordano это потому, что в некоторых месяцах нет 31 дня. Попробуйте as.Date("2012/12/20") - months(1:12) например. - person David Arenburg; 21.09.2015

вы можете использовать пакет lubridate для этого

library(lubridate)
maturity <- maturity %m-% months(6)

нет причин для изменения поля дня.

вы можете установить поле дня обратно на последний день в этом месяце,

day(maturity) <- days_in_month(maturity)

удачи!!!

person Aneesh Cherian K    schedule 15.02.2018

Технически вы не можете добавить/вычесть 1 месяц ко всем датам (хотя вы можете добавить/вычесть 30 дней ко всем датам, но я полагаю, это не то, что вам нужно). Я думаю, это то, что вы ищете

> lubridate::ceiling_date(as.Date("2020-01-31"), unit = "month")
[1] "2020-02-01"
> lubridate::floor_date(as.Date("2020-01-31"), unit = "month")
[1] "2020-01-01"
person qed    schedule 10.02.2020

lubridate корректно работает с такими вычислениями:

library(lubridate)
as.Date("2000-01-01") - days(1)    # 1999-12-31
as.Date("2000-03-31") - months(1)  # 2000-02-29

но иногда не получается:

as.Date("2000-02-29") - years(1)   # NA, should be 1999-02-28
person J. Doe    schedule 01.07.2020