Вот решение, которое использует только ядро R. Сначала мы покажем входные данные, чтобы сохранить все это самодостаточным:
DF <- data.frame(ID = c(1, 1, 2, 2),
ATTRIBUTE = c("A", "B", "B", "B"),
START = c("01-01-2000", "05-11-2001", "01-02-2002", "01-06-2008"),
END = c("15-03-2010", "06-02-2002", "08-05-2008", "01-07-2008"))
Теперь, когда у нас есть входные данные, решение следует: yr
определяется как функция, которая извлекает год.Суть расчетов - это утверждение, следующее за определением yr
.Для каждой строки DF
анонимная функция создает фрейм данных с интервалами лет в столбце 1 и ATTRIBUTE
и ID
в столбцах 2 и 3. Например, фрейм данных, соответствующий первой строке DF
это 11 строка data.frame(YEAR = 2000:2010, ATTRIBUTE = 1, ID = "A")
, а фрейм данных, соответствующий второй строке DF
, это две строки data.frame(YEAR = 2001:2002, ATTRIBUTE = 1, ID = "B")
.lapply
создает список таких фреймов данных, по одному для каждой строки DF
, поэтому в приведенном выше примере ввода он создает список из 4 компонентов.Используя do.call
мы rbind
компоненты этого списка, то есть отдельные кадры данных, создавая один большой кадр данных.Мы удаляем дубликаты строк (используя unique
) из этого большого фрейма данных, удаляем столбец ID
(третий столбец) и запускаем table
для результата:
yr <- function(d) as.numeric(sub(".*-", "", d))
out <- table(unique(do.call(rbind, lapply(1:nrow(DF), function(r) with(DF[r, ],
data.frame(YEAR = seq(yr(START), yr(END)), ATTRIBUTE, ID)))))[, -3])
В результате получается следующая таблица:
> out
ATTRIBUTE
YEAR A B
2000 1 0
2001 1 1
2002 1 2
2003 1 1
2004 1 1
2005 1 1
2006 1 1
2007 1 1
2008 1 1
2009 1 0
2010 1 0
РЕДАКТИРОВАТЬ:
Позже постер указал, что память может быть проблемой, поэтому вот решение sqldf, которое обрабатывает ключевые большие промежуточные результаты в sqlite вне R (dbname = tempfile()
говорит ему об этом), поэтому любое ограничение памяти R не повлияет на это.Он использует тот же вход и ту же функцию yr
, показанную выше, и возвращает тот же результат, tab
такой же, как out
выше.Также попробуйте это без dbname = tempfile()
на случай, если оно действительно уместится в памяти.
library(sqldf)
DF2 <- transform(DF, START = yr(START), END = yr(END))
years <- data.frame(year = min(DF2$START):max(DF2$END))
tab.df <- sqldf("select year, ATTRIBUTE, count(*) as count from
(select distinct year, ATTRIBUTE, ID
from years, DF2
where year between START and END)
group by year, ATTRIBUTE", dbname = tempfile())
tab <- xtabs(count ~., tab.df)