У меня есть структурированный фрейм данных (но на самом деле он содержит ~ 400 тыс. Строк):
library(data.table)
df <- fread(" id start end
174095 2018-12-19 2018-12-31
227156 2018-12-19 2018-12-31
210610 2018-04-13 2018-09-27
27677 2018-04-12 2018-04-26
370474 2017-07-13 2017-08-19
303693 2017-02-20 2017-04-09
74744 2016-10-03 2016-11-05
174095 2018-12-01 2018-12-20
27677 2018-03-01 2018-05-29
111111 2018-01-01 2018-01-31
111111 2018-11-11 2018-12-31")
(отредактировано, спасибо Уве)
Для каждой строки я хочу подсчитать, сколько строк в кадре данных имеют тот же идентификатор, что и текущая строка, и начальный-конечный период, который перекрывает период в текущей строке. Например, для первой строки результат будет равен 2, поскольку есть другая строка с id = 174095, и ее конец больше, чем начало первой строки.
Я пытался сделать это с помощью dplyr, например:
df = df %>% rowwise() %>% mutate(count = sum(id == df$id & ((start >= df$start & start <= df$end) | (end >= df$start & end <= df$end))))
Но это очень медленно. Я попробовал, и через два часа он все еще работал.
Я также пытался использовать mapply, но это также отнимает слишком много времени:
df$count = mapply(function(id, start, end) {
return(sum(df$id == id & (between(df$start, start, end) | between(df$end, start, end))) }, id, start, end)
Есть ли эффективный разумный способ сделать это?
Большое спасибо
РЕДАКТИРОВАТЬ 2019-03-06
@ Предлагаемое Уве решение:
df[, overlapping.rows := df[.SD, on = .(id, start <= end, end >= start), .N, by = .EACHI]$N][]
отлично работает для примера data.frame выше. Но оказалось, что пример не был достаточно иллюстративным, или я не совсем понял, может быть:)
Я добавил третью запись для идентификатора 174095 и изменил две другие:
df <- fread("id start end
174095 2018-12-19 2018-12-31
227156 2018-12-19 2018-12-31
210610 2018-04-13 2018-09-27
27677 2018-04-12 2018-04-26
370474 2017-07-13 2017-08-19
303693 2017-02-20 2017-04-09
74744 2016-10-03 2016-11-05
174095 2018-12-01 2018-12-18
27677 2018-03-01 2018-05-29
111111 2018-01-01 2018-01-31
111111 2018-11-11 2018-12-31
174095 2018-11-30 2018-12-25")
Теперь у идентификатора 174095 есть два интервала, которые не перекрываются между ними (строки 1 и 2), и другой интервал, который перекрывает два других (строка 3):
id start end
1: 174095 2018-12-19 2018-12-31
2: 174095 2018-12-01 2018-12-18
3: 174095 2018-11-30 2018-12-25
Итак, результат должен быть:
id start end overlapping.rows
1: 174095 2018-12-19 2018-12-31 2
2: 174095 2018-12-01 2018-12-18 2
3: 174095 2018-11-30 2018-12-25 3
Но на самом деле это так:
id start end overlapping.rows
1: 174095 2018-12-19 2018-12-31 3
2: 174095 2018-12-01 2018-12-18 3
3: 174095 2018-11-30 2018-12-25 3
Если я не ошибаюсь, это происходит потому, что окончательное соединение выполняется только по "id", поэтому все строки с одинаковым идентификатором имеют одинаковый результат.
Мое решение состоит в выполнении окончательного слияния также с помощью «start» и «end»:
df[tmp, on = .(id, start, end), overlapping.rows := N]
По какой-то причине (я хотел бы выяснить ...), при самосоединении даты начала заканчиваются в столбце "конец" и наоборот, поэтому мне пришлось добавить эту строку сразу после нее:
setnames(tmp, c("id", "end", "start", "N"))
Теперь результат:
id start end overlapping.rows
1: 174095 2018-12-19 2018-12-31 2
2: 227156 2018-12-19 2018-12-31 1
3: 210610 2018-04-13 2018-09-27 1
4: 27677 2018-04-12 2018-04-26 2
5: 370474 2017-07-13 2017-08-19 1
6: 303693 2017-02-20 2017-04-09 1
7: 74744 2016-10-03 2016-11-05 1
8: 174095 2018-12-01 2018-12-18 2
9: 27677 2018-03-01 2018-05-29 2
10: 111111 2018-01-01 2018-01-31 1
11: 111111 2018-11-11 2018-12-31 1
12: 174095 2018-11-30 2018-12-25 3
Это именно то, что я ожидал!