Слияние непрерывных диапазонов дат в R - PullRequest
0 голосов
/ 17 декабря 2018

Я хочу объединить наблюдения в непрерывные (без пробелов в днях) диапазоны дат.у каждого пэда может быть несколько диапазонов в результирующем фрейме данных. Я знаю, что это можно сделать с помощью цикла. Но есть ли эффективный способ справиться с этой задачей?Обратите внимание, что временной интервал здесь не перекрывается, а start_date увеличивается.enter image description here

данные здесь (я использую R: dput, вы можете скопировать и назначить свой объект в R):

structure(list(patid = c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
2L, 3L, 3L, 3L), start_date = structure(c(1L, 2L, 3L, 4L, 5L, 
1L, 2L, 3L, 8L, 9L, 6L, 7L, 10L), .Label = c("1/1/2010", "2/1/2010", 
"3/1/2010", "4/1/2010", "5/1/2010", "5/6/2011", "7/1/2012", "8/1/2010", 
"9/1/2010", "9/1/2012"), class = "factor"), end_date = structure(c(1L, 
3L, 4L, 5L, 6L, 1L, 3L, 4L, 8L, 10L, 7L, 9L, 2L), .Label = c("1/31/2010", 
"12/1/2012", "2/28/2010", "3/31/2010", "4/30/2010", "5/31/2010", 
"6/15/2011", "8/31/2010", "8/31/2012", "9/30/2010"), class = "factor")), class = "data.frame", row.names = c(NA, 
-13L))

1 Ответ

0 голосов
/ 17 декабря 2018

A data.table подход (с magrittr для большей читаемости) (надежная версия):

library(data.table)
library(magrittr)

calc_cummax <- function(x) (setattr(cummax(unclass(x)), "class", c("Date", "IDate")))

df_merged <- setDT(df) %>%
  .[, `:=` (cont_start = as.Date(as.character(start_date), "%m/%d/%Y"),
            cont_end = as.Date(as.character(end_date), "%m/%d/%Y"))] %>%
  .[order(patid, start_date),] %>%
  .[, max_until_now := shift(calc_cummax(cont_end)), by = patid] %>%
  .[, lead_max := shift(max_until_now, type = "lead"), by = patid] %>%
  .[is.na(max_until_now), max_until_now := lead_max, by = patid] %>%
  .[(max_until_now + 1L) >= cont_start, gap_between_contracts := 0, by = patid] %>% 
  .[(max_until_now + 1L) < cont_start, gap_between_contracts := 1, by = patid] %>%
  .[is.na(gap_between_contracts), gap_between_contracts := 0] %>% 
  .[, ("fakeidx") := cumsum(gap_between_contracts), by = patid] %>%
  .[, .(cont_start = min(cont_start), cont_end = max(cont_end)), by = .(patid, fakeidx)] %>% 
  .[, ("fakeidx") := NULL]

Вывод в вашем случае:

   patid cont_start   cont_end
1:     1 2010-01-01 2010-05-31
2:     2 2010-01-01 2010-03-31
3:     2 2010-08-01 2010-09-30
4:     3 2011-05-06 2011-06-15
5:     3 2012-07-01 2012-12-01

A tidyverse подход(не надежная, простая версия):

library(tidyverse)

df %>%
  mutate(
    cont_start = as.Date(as.character(start_date), "%m/%d/%Y"),
    cont_end = as.Date(as.character(end_date), "%m/%d/%Y")
  ) %>%
  arrange(patid, cont_start) %>%
  group_by(patid) %>%
  mutate(
    idx = cumsum(coalesce(as.numeric(cont_start != (lag(cont_end) + 1)), 0))
  ) %>%
  group_by(patid, idx) %>%
  summarise(
    cont_start = min(cont_start),
    cont_end = max(cont_end)
  ) %>% select(-idx)

Вывод:

# A tibble: 5 x 3
# Groups:   patid [3]
  patid cont_start cont_end  
  <int> <date>     <date>    
1     1 2010-01-01 2010-05-31
2     2 2010-01-01 2010-03-31
3     2 2010-08-01 2010-09-30
4     3 2011-05-06 2011-06-15
5     3 2012-07-01 2012-12-01

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

В этом случае устойчивость не имеет ничего общего ни с data.table, ни с tidyverse (вы можете использовать функцию calc_cummax также, переписав версию tidyverse, но вам нужно будет загрузить data.table).

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