Использование foverlaps для сокращения эпизодов

#r #data.table

Вопрос:

Я пытаюсь разобраться в том, как использовать data.table::foverlaps() для создания новых таблиц данных. В одном приложении я хотел бы использовать foverlaps для выявления пробелов, а затем использовать эту информацию для усечения моей исходной таблицы данных.

Предположим, что у меня есть набор данных ( df1 ) из 2 сотрудников ( id ) в компании с диапазонами дат ( start_date и end_date ) за периоды, в течение которых они работают над различными проектами ( proj_id ; либо «A», «B» или «C»).

 library(data.table)
library(lubridate)
df1<-data.table(id = rep(1:2,each=3),
           start_date = ymd(c("1998-04-03","1999-03-08","2000-08-13",
                              "2005-03-03","2007-10-12","2014-02-23")),
           end_date = ymd(c("1999-03-07","2000-08-12","2021-04-23",
                            "2007-09-05","2014-02-22","2019-05-04")),
           proj_id = c("A","B","A","B","C","A"))

> df1
   id start_date   end_date proj_id
1:  1 1998-04-03 1999-03-07       A
2:  1 1999-03-08 2000-08-12       B
3:  1 2000-08-13 2021-04-23       A
4:  2 2005-03-03 2007-09-05       B
5:  2 2007-10-12 2014-02-22       C
6:  2 2014-02-23 2019-05-04       A
 

Теперь у меня есть другой набор данных ( df2 ), который определяет время, из которого я хочу выполнить усечение df1 .

 df2 <- data.table(id = 1:2,
                  start_date = ymd("1998-07-20", "2006-06-12"),
                  end_date = ymd("1998-08-15", "2016-04-08"))

> df2
   id start_date   end_date
1:  1 1998-07-20 1998-08-15
2:  2 2006-06-12 2016-04-08
 

Затем я могу использовать data.table::foverlaps() для идентификации перекрывающихся эпизодов:

 > setkey(df1,id,start_date,end_date)
> foverlaps(df2, df1, type="any", 
            by.x=c("id","start_date","end_date"))
   id start_date   end_date proj_id i.start_date i.end_date
1:  1 1998-04-03 1999-03-07       A   1998-07-20 1998-08-15
2:  2 2005-03-03 2007-09-05       B   2006-06-12 2016-04-08
3:  2 2007-10-12 2014-02-22       C   2006-06-12 2016-04-08
4:  2 2014-02-23 2019-05-04       A   2006-06-12 2016-04-08
 

Теперь я хотел бы использовать эти данные для создания новой версии df1 , в которой я создаю новые эпизоды, сокращая пробелы, указанные выше. Поэтому мой желаемый DT:

   id start_date   end_date proj_id
1:  1 1998-04-03 1998-07-19       A
2:  1 1998-08-16 1999-03-07       A
3:  1 1999-03-08 2000-08-12       B
4:  1 2000-08-13 2021-04-23       A
5:  2 2005-03-03 2006-06-11       B
6:  2 2016-04-09 2019-05-04       A
``` 
 

Ответ №1:

Могут быть альтернативы, которые работают лучше, но это может сработать в зависимости от вашего foverlaps результата.

Предположим, вы создали еще одну таблицу data.table, вызванную df3 с вашим foverlaps результатом:

 df3 <- foverlaps(df2, df1, type = "any", by.x = c("id", "start_date", "end_date"))
 

Затем вы можете повторить каждую строку и добавить 0, 1 или 2 диапазона дат в зависимости от перекрытия (усечение в конце или начале, или весь диапазон заблокирован).

 dt <- data.table(start_date = Date(), end_date = Date(), id = numeric(), proj_id = numeric())

for (i in seq_len(nrow(df3))) {
  if (df3$start_date[i] < df3$i.start_date[i]) {
    dt <- rbind(dt, data.table(start_date = df3$start_date[i], end_date = df3$i.start_date[i] - 1, id = df3$id[i], proj_id = df3$proj_id[i]))
  } 
  if (df3$end_date[i] > df3$i.end_date[i]) {
    dt <- rbind(dt, data.table(start_date = df3$i.end_date[i]   1, end_date = df3$end_date[i], id = df3$id[i], proj_id = df3$proj_id[i]))
  }
}
 

Наконец, вы можете удалить foverlaps результаты из исходного df1 , так как для них были определены новые диапазоны (с использованием fsetdiff ). Затем вы можете добавить новые диапазоны обратно.

 rbind(fsetdiff(df1, df3[,1:4]), dt)[order(id, start_date)]
 

Выход

    id start_date   end_date proj_id
1:  1 1998-04-03 1998-07-19       A
2:  1 1998-08-16 1999-03-07       A
3:  1 1999-03-08 2000-08-12       B
4:  1 2000-08-13 2021-04-23       A
5:  2 2005-03-03 2006-06-11       B
6:  2 2016-04-09 2019-05-04       A