есть ли способ интегрировать перекрывающиеся интервалы дат в r?

#r

Вопрос:

Я пытаюсь интегрировать/объединить несколько перекрывающихся интервалов. И ниже приведен пример исходного набора данных:

 df <- data.frame(id=c(1, 1, 1, 1),
                 degree=c(2,2,2,2),
                 start_date=c(as.Date("2010-01-20"),
                              as.Date("2010-01-25"),
                              as.Date("2010-03-20"),
                              as.Date("2010-03-25")),
                 stop_date= c(as.Date("2010-01-30"),
                              as.Date("2010-02-10"),
                              as.Date("2010-04-10"),
                              as.Date("2010-04-30")))
 

И вот результат, которого я ожидаю:

 df_result <- data.frame(id=c(1, 1),
                 degree=c(2,2),
                 start_date=c(as.Date("2010-01-20"),
                              as.Date("2010-03-20")),
                 stop_date= c(as.Date("2010-02-10"),
                              as.Date("2010-04-30")))
 

Спасибо!

Ответ №1:

Вы можете попробовать

 df %>%
  arrange(start_date) %>%
  mutate(idx = stop_date > lead(start_date)) %>%
  mutate(stop_date = as.Date(ifelse(idx == TRUE, lead(stop_date), stop_date))) %>%
  filter(idx == TRUE) %>% select(-idx)

  id degree start_date  stop_date
1  1      2 2010-01-20 2010-02-10
2  1      2 2010-03-20 2010-04-30
 

Комментарии:

1. Спасибо вам за решение! Это показывает ошибку, что мне нужно указать источник

2. @rachel3765 Можете ли вы сказать мне, в какой строке возникает эта ошибка?

3. Это в четвертом ряду

4. @rachel3765 Мне очень жаль. Вы должны использовать zoo::as.Date .

Ответ №2:

Сгруппируйте данные по идентификатору и степени, а затем создайте вектор всех дат между каждым началом и остановкой, получите среди них уникальные значения и отсортируйте их dates . Затем создайте группирующую переменную g , которая сохраняет соседние даты в одной группе, и сведите каждую группу в одну строку.

 library(dplyr)
df %>%
  group_by(id, degree) %>%
  group_modify(~ with(., {
    dates <- sort(unique(do.call("c", Map(seq, start_date, stop_date, 1))))
    data.frame(dates, g = cumsum(c(TRUE, as.numeric(diff(dates)) != 1)))
  })) %>%
  group_by(g, .add = TRUE) %>%
  summarize(dates_start = min(dates), dates_stop = max(dates), .groups = "drop")
 

дающий:

 # A tibble: 2 x 5
     id degree     g dates_start dates_stop
  <dbl>  <dbl> <int> <date>      <date>    
1     1      2     1 2010-01-20  2010-02-10
2     1      2     2 2010-03-20  2010-04-30
 

Комментарии:

1. Привет, большое вам спасибо за решение! Но когда я применяю код к своему исходному набору данных, он показывает ошибку: Ошибка в h(simpleError(msg, вызов)) : ошибка при оценке аргумента » x «при выборе метода для функции «сортировка»: ошибка при оценке аргумента » x «при выборе метода для функции «уникальный»: ошибка при оценке аргумента «args» при выборе метода для функции «do.call»: неправильный вход в » по » аргументу. Мне интересно, имеете ли вы какое-либо представление об этом. Еще раз спасибо вам!

2. Я попробовал это, и это показывает ошибку как: Ошибка в seq.int(0, to0 — от, по) : неверный знак в аргументе «по»

3. Моя версия R 4.1.0. Возможно, мне придется сначала обновить ее. И код работал для упрощенного примера, но не работает в моем исходном большем наборе данных.

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

Ответ №3:

Вычислите различия в запаздывающих датах и перестройте фрейм данных для подмножества и отрицательных подмножеств, где они отрицательны.

 s <- na.omit(with(df, c(FALSE, c(start_date[-1], NA) - stop_date) < 0))
res <- setNames(with(df, data.frame(id[s], degree[s], start_date[!s], stop_date[s])), 
                names(df))
res
#   id degree start_date  stop_date
# 1  1      2 2010-01-20 2010-02-10
# 2  1      2 2010-03-20 2010-04-30

stopifnot(identical(res, df_result))
 

Это также работает с несколькими идентификаторами, а также идентификаторами с одной строкой, используя by .

 do.call(rbind, by(df2, df2$id, (x) {
  if (nrow(x) > 1) {
    s <- na.omit(with(x, c(FALSE, c(start_date[-1], NA) - stop_date) < 0))
    setNames(with(x, data.frame(id[s], degree[s], start_date[!s], stop_date[s])), 
             names(x))
  } else {
    x
  }
}))

#     id degree start_date  stop_date
# 1.1  1      2 2010-01-20 2010-02-10
# 1.2  1      2 2010-03-20 2010-04-30
# 2.1  2      2 2010-01-20 2010-02-10
# 2.2  2      2 2010-03-20 2010-04-30
# 3    3      2 2010-03-25 2010-04-30
 

Данные:

 df2 <- structure(list(id = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L), degree = c(2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), start_date = structure(c(14629, 
14634, 14688, 14693, 14629, 14634, 14688, 14693, 14693), class = "Date"), 
    stop_date = structure(c(14639, 14650, 14709, 14729, 14639, 
    14650, 14709, 14729, 14729), class = "Date")), row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9"), class = "data.frame")
 

Комментарии:

1. Большое вам спасибо за решение! Но система показывает ошибку: Ошибка в именах(объект)

2. @rachel3765 Что произойдет, если вы оставите переименование, т. Е. Просто сделаете with(df, data.frame(id[s], degree[s], start_date[!s], stop_date[s])) ?

3. это показывает: Ошибка в data.frame(patno[s], toxdeg[s], start_day[!s], stop_day[s]) : аргументы подразумевают различное количество строк: 4, 5. Итак, проблема в том, что количество дат начала и окончания различно? И кстати, я использую второй метод, который вы применили.

4. @rachel3765 Я думаю, что может быть особый случай, если вы разделитесь по идентификатору, например, только на одну строку. Что дает соответствующий чек, any(lengths(split(df2$id, df2$id)) == 1) ?

5. Да, он возвращает «TRUE». И я считаю, что есть тихое количество из них, имеющих только один ряд.