#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». И я считаю, что есть тихое количество из них, имеющих только один ряд.