R: эффективные способы добавления месяцев к датам?

У меня есть data.table из миллионов строк, и один из столбцов - столбец даты. Я хотел бы добавить 12 месяцев ко всем датам в этом столбце и создать новый столбец. Поэтому я использую пакеты dplyr и lubridate.

library(dplyr)
library(lubridate)
new_data <- data %>% mutate(date12m = date %m+% months(12))

Это работает, однако очень медленно для больших наборов данных. Я что-то упускаю? Как это можно ускорить? Обычно я не ожидаю, что R будет работать более 10 минут для такой простой задачи.

Изменить:

Отмечу, что мое решение уже более эффективно, чем использование as.yearmon. Спасибо полковнику Бовелю за решение

a <- data.frame(date = rep(today(),1000000))


func = function(u) {
  d = as.Date(as.yearmon(u)+1, frac=1) 
  if(day(u)>day(d)) return(d)
  day(d) = day(u)
  d
} 

pt <- proc.time()
a <- a %>% mutate(date12m = func(date))
data.table::timetaken(pt)


pt <- proc.time()
a <- a %>% mutate(date12m = date %m+% 12)
data.table::timetaken(pt)

person xiaodai    schedule 02.02.2015    source источник
comment
Не могли бы вы предоставить небольшую часть ваших данных?   -  person jazzurro    schedule 02.02.2015
comment
надеюсь, что ответ поможет, вам нужно преобразовать нужные столбцы в даты, прежде чем применять мою функцию.   -  person Colonel Beauvel    schedule 02.02.2015
comment
Я согласен, что это должно быть быстрее, но это не совсем простая задача, учитывая, что даты хранятся в секундах с 1 января 1970 года.   -  person hadley    schedule 03.02.2015


Ответы (2)


Просто добавьте 1 с month:

x=seq.Date(from=as.Date("2007-01-01"), to=as.Date("2014-12-12"), by="day")
month(x) = month(x) + 1

#> head(x)
#[1] "2007-02-01" "2007-02-02" "2007-02-03" "2007-02-04" "2007-02-05" "2007-02-06"

Изменить: согласно комментарию @akrun, вот решение, использующее as.yearmon из пакета zoo. Хитрость заключается в том, чтобы сделать быструю проверку, взяв день последней даты следующего месяца:

library(zoo)

func = function(u)
{
    d = as.Date(as.yearmon(u)+1/12, frac=1) 
    if(day(u)>day(d)) return(d)
    day(d) = day(u)
    d
} 

x=as.Date(c("2014-01-31","2015-02-28","2013-03-02"))
#> as.Date(sapply(x, func))
#[1] "2014-02-28" "2015-03-28" "2013-04-02"
person Colonel Beauvel    schedule 02.02.2015
comment
Спасибо, но было бы лучше, если бы вы показали некоторые тесты, так как OP хочет эффективный код. Судя по моим тестам, решение OP эффективно по сравнению с mondate. Хотя твой не проверял :-) - person akrun; 02.02.2015
comment
Я получаю предупреждающее сообщение при применении к тестовому набору данных. In if (day(u) > day(d)) return(d) : the condition has length > 1 and only the first element will be used - person akrun; 02.02.2015
comment
какой у вас набор данных? У меня вообще нет этого сообщения. Это означает, что день (u) - это вектор, которого быть не должно. - person Colonel Beauvel; 02.02.2015
comment
Насколько я понимаю, ваш код можно применять как data %>% mutate(date12m = func(date)) Но, исходя из вашего примера, он снова зациклен на sapply? - person akrun; 02.02.2015
comment
Набор данных set.seed(24); data <- data.frame(date=seq(as.Date('2011-01-01'), length.out=20, by='1 month'), val=rnorm(20)) - person akrun; 02.02.2015
comment
Основываясь на некоторых тестах для данных 1e6 с использованием Col <- function() {data %>% mutate(date12m = func(date))}; xiao <- function() {data %>% mutate(date12m = date %m+% months(12))}, ваша функция занимает больше времени. - person akrun; 02.02.2015
comment
Моя функция не векторизована. Это больше не зацикливается с sapply ... Я использую sapply один раз, чтобы зациклиться на векторе, который я хочу обновить, на 1M. Но да, это довольно медленно для моего набора данных. x=seq.Date(as.Date("2000-01-01"),as.Date("2015-01-01"), by="day") (30 секунд) - person Colonel Beauvel; 02.02.2015
comment
@акрун. Да, я думаю, что мое исходное решение более эффективно: ‹- data.frame(date = rep(today(),1000000)) pt ‹- proc.time() a ‹- a %›% mutate(date12m = func(date) ) data.table::timetaken(pt) pt ‹- proc.time() a ‹- a %›% mutate(date12m = date %m+% 12) data.table::timetaken(pt) - person xiaodai; 04.02.2015

Я также работаю с фреймами больших данных в R, вы можете использовать пакет DescTools, в нем есть функция с именем AddMonths(date,NoOfMonths).

Это работает довольно хорошо для меня.

> a <- ymd("2011-09-9")
> b <- AddMonths(a,1)
> b

[1] "2011-10-09"
person user5196900    schedule 27.06.2016
comment
Работает с фреймами данных. seq не работает с фреймами данных. вам нужно внести некоторые коррективы, прежде чем вы сможете использовать его на фреймах данных. AddMonths довольно прост. - person ; 07.09.2016
comment
как применить к кадру данных. Кажется, что это очень медленно!! - person ayush singhal; 03.05.2017