Подсчет количества частичных совпадений для интервалов дат - PullRequest
2 голосов
/ 25 марта 2019

Мне нужно запустить цикл над кадром данных с ~ 150К строк.Однако цикл должен проверять каждую строку и проверять условие, которое проверяет все остальные строки в наборе данных.Мой код отлично работает для игрушечного набора данных, он выдает правильное значение, но он слишком медленный для моего фактического набора данных.Я дал ему поработать несколько часов, а он так и не закончился.так что я надеюсь, что у кого-то есть лучшая идея, как подойти к этому.

#R version 3.5.1 Windows 64-bit

#Example dataset
my_df <- data.frame("PERSON" = c("A","A","A","B","A","A","B"),
                    "DATE_START" = c("2019-01-15","2019-01-10","2019-01-20","2019-01-19","2018-12-20","2018-03-03","2019-05-01"),
                    "DATE_FINISH" = c("2019-01-30","2019-01-18","2019-02-05","2019-01-23","2019-02-10","2018-04-01","2019-06-06")
                    )
#Each row is a task that the assigned person is working on
my_df
   PERSON   DATE_START DATE_FINISH
1       A   2019-01-15  2019-01-30
2       A   2019-01-10  2019-01-18
3       A   2019-01-20  2019-02-05
4       B   2019-01-19  2019-01-23
5       A   2018-12-20  2019-02-10
6       A   2018-03-03  2018-04-01
7       B   2019-05-01  2019-06-06

Что я хочу знать, это ДЛЯ строки 1, сколько других задач у человека А перекрывается между датами его начала и окончания?(включая строку с включенным)

Итак, я ищу ответ

   PERSON   DATE_START DATE_FINISH  NUMBER_OF_TASKS
1       A   2019-01-15  2019-01-30  4
2       A   2019-01-10  2019-01-18  3
3       A   2019-01-20  2019-02-05  3
4       B   2019-01-19  2019-01-23  1
5       A   2018-12-20  2019-02-10  4
6       A   2018-03-03  2018-04-01  1
7       B   2019-05-01  2019-06-06  1

Так что это в основном говорит о строке 1, у человека А было 4 открытых задания

Я попытался создать элемент списка для каждой строки, которая включает диапазон дат в виде числовых значений, а затем, чтобы проверить, есть ли перекрытие, я использовал оператор% in% для сравнения не перечисленных диапазонов

Я сделал нечто подобное, используяфункция lapply (здесь не показана), но та же проблема, для выполнения которой требуется вечность.

##This is what I currently have

temp_list <- list()
num_open_tasks <- c()
open_work_cc <- c()

##Create a list of length = nrow(my_df)
##Each element in the list is a range of dates coerced to numeric
for(i in 1:nrow(my_df)){
  temp_list[[i]] <- as.numeric(my_df$DATE_START[i]) : 
                    as.numeric(my_df$DATE_FINISH[i])
}


for(i in 1:nrow(my_df)){
  for(j in 1:nrow(my_df)){

##If elements from the temp_list overlap by 5 days, the overlap = 5
##I'm just checking if the overlap is greater than 0 (is there any overlap at all)
##And if the tasks belongs to the same person or not    
open_work_cc[j] <- ifelse(sum(unlist(temp_list[[i]]) %in% 
                              unlist(temp_list[[j]])) > 0 &
                              my_df$PERSON[i] == my_df$PERSON[j] 
                              ,1,0
                           )
open_work_cc_total <- sum(open_work_cc)

  }
  num_open_tasks[i] <- open_work_cc_total

}
my_df <- cbind(my_df, num_open_tasks)

Этот метод возвращает желаемый столбец, заполненный правильными значениями.Но я предполагаю, что есть более элегантный и значительно более быстрый метод, использующий некоторую форму разделения / применения / объединения.Любая помощь приветствуется

1 Ответ

3 голосов
/ 26 марта 2019

foverlaps в data.table, вероятно, будет самым быстрым подходом в R. Я думаю, следующий код делает то, что вы хотите:

library(data.table)
setDT(my_df)

my_df[, DATE_START_N:=as.numeric(as.Date(DATE_START))]
my_df[, DATE_FINISH_N:=as.numeric(as.Date(DATE_FINISH))]

setkey(my_df, PERSON, DATE_START_N,DATE_FINISH_N)

my_df[,NUMBER_OF_TASKS:=foverlaps(my_df,my_df,which=TRUE)[,.N,by=xid]$N]
my_df

Для большей ясности: foverlaps(my_df,my_df,which=TRUE) делаетсамостоятельное соединение в диапазоне дат в пределах PERSON (объединение определяется setkey).Обратите внимание, что аргумент по умолчанию для type интервала соединения для фоверлапсов равен "any", что является частичным совпадением на интервалах: то есть то, что вы хотите здесь.

Указание which=TRUE просто предоставит индексы совпадений по x и y (а не фактические соединенные данные, которые здесь не нужны).Вызов foverlaps возвращает объект класса data.table, который затем немедленно агрегируется для получения количества строк в каждой группе, определяемого как xid (которые являются просто строками my_df), с использованием вызова функции скобки [,.N,by=xid].Эти значения извлекаются в вектор с $N и присваиваются новому столбцу NUMBER_OF_TASKS в my_df.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...