R Оптимальный способ создания временных рядов из дат начала и окончания для групп

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

Вот пример входных данных, группы идентифицируются по id:

structure(list(id = c(723654, 885618, 269861, 1383642, 250276, 
815511, 1506680, 1567855, 667345, 795731), startdate = c("2008-06-29", 
"2008-12-01", "2006-09-27", "2010-02-03", "2006-08-31", "2008-09-10", 
"2010-04-11", "2010-05-15", "2008-04-12", "2008-08-28"), enddate = c("2008-08-13", 
"2009-02-08", "2007-10-12", "2010-09-09", "2007-06-30", "2010-04-27", 
"2010-04-13", "2010-05-16", "2010-04-20", "2010-03-09")), .Names = c("id", 
"startdate", "enddate"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "6", "7", "8", "9", "10", "11"))

Я написал функцию и векторизовал ее. Функция принимает три параметра, хранящиеся в каждой строке, и генерирует временные ряды с идентификаторами групп.

genDateRange<-function(start, end, id){
  dates<-seq(as.Date(start), as.Date(end), by="month")
  return( cbind(month=as.character(dates), id=rep(id, length(dates))))
}

genDataRange<-Vectorize(genDateRange)

Я запускаю функцию следующим образом, чтобы получить фрейм данных. У меня более 6 миллионов строк на выходе, так что это занимает вечность. Мне нужен более быстрый способ.

range<-do.call(rbind,genDataRange(dat$startdate, dat$enddate, dat$id))

Первые десять строк вывода выглядят так:

structure(c("2008-06-29", "2008-07-29", "2008-12-01", "2009-01-01", 
"2009-02-01", "2006-09-27", "2006-10-27", "2006-11-27", "2006-12-27", 
"2007-01-27", "723654", "723654", "885618", "885618", "885618", 
"269861", "269861", "269861", "269861", "269861"), .Dim = c(10L, 
2L), .Dimnames = list(NULL, c("month", "id")))

Я был бы признателен за более быстрый способ сделать это. Я думаю, что слишком сосредоточился на чем-то и упустил гораздо более простое решение.


person PoorLifeChoicesMadeMeWhoIAm    schedule 03.07.2015    source источник
comment
как долго это навсегда? сколько данных у вас есть   -  person rawr    schedule 03.07.2015
comment
ваши первые 10 строк вывода не совсем правильные, должно быть 2 столбца   -  person C8H10N4O2    schedule 03.07.2015
comment
Бит .Dim = c(10L, 2L) создает два столбца.   -  person PoorLifeChoicesMadeMeWhoIAm    schedule 03.07.2015


Ответы (2)


Нет необходимости использовать функцию генератора или rbindlist, потому что data.table легко справится с этим без них.

# start with a data.table and date columns
library(data.table)
dat <- data.table(dat)
dat[,`:=`(startdate = as.Date(startdate), enddate   = as.Date(enddate))]
dat[,num_mons:= length(seq(from=startdate, to=enddate, by='month')),by=1:nrow(dat)]

dat # now your data.table looks like this
#          id  startdate    enddate num_mons
#  1:  723654 2008-06-29 2008-08-13        2
#  2:  885618 2008-12-01 2009-02-08        3
#  3:  269861 2006-09-27 2007-10-12       13
#  4: 1383642 2010-02-03 2010-09-09        8
#  5:  250276 2006-08-31 2007-06-30       10
#  6:  815511 2008-09-10 2010-04-27       20
#  7: 1506680 2010-04-11 2010-04-13        1
#  8: 1567855 2010-05-15 2010-05-16        1
#  9:  667345 2008-04-12 2010-04-20       25
# 10:  795731 2008-08-28 2010-03-09       19

out <- dat[, list(month=seq.Date(startdate, by="month",length.out=num_mons)), by=id]
out 
#          id      month
#   1: 723654 2008-06-29
#   2: 723654 2008-07-29
#   3: 885618 2008-12-01
#   4: 885618 2009-01-01
#   5: 885618 2009-02-01
# ---                  
#  98: 795731 2009-10-28
#  99: 795731 2009-11-28
# 100: 795731 2009-12-28
# 101: 795731 2010-01-28
# 102: 795731 2010-02-28

Этот вопрос связан, но разница заключается в том, что в вашем вопросе мы итерируем, а не дублируем строки в таблице данных.

person C8H10N4O2    schedule 03.07.2015

Для больших наборов данных это

library(data.table)
range <- rbindlist(lapply(genDataRange(dat$startdate, dat$enddate, dat$id),as.data.frame))

должно быть быстрее, чем

range<-do.call(rbind,genDataRange(dat$startdate, dat$enddate, dat$id))
person RHertel    schedule 03.07.2015