
[英]Extract overlapping and non-overlapping time periods using R (data.table)
[英]How to separate overlapping time periods into overlapping and non-overlapping periods in R
我正在寻找将重叠和非重叠时期与“lubridate”和“dplyr”包(或任何其他可以建议的)结合起来。 这是一个示例数据框:
vacation_start <- as_date('2017-04-19')
vacation_end <- as_date('2017-04-25')
course_start <- as_date('2017-04-12')
course_end <- as_date('2017-04-21')
course_interval <- interval(course_start, course_end)
vacation_interval <- interval(vacation_start, vacation_end)
df <- data.frame(id = "ID", part = c("A", "B"),
start = c(course_start,vacation_start),
end = c(course_end, vacation_end),
interval = c(course_interval, vacation_interval))
数据框如下所示:
ID | 部分 | 开始 | 结尾 | 间隔 |
---|---|---|---|---|
ID | 一个 | 2017-04-12 | 2017-04-21 | 2017-04-12 UTC--2017-04-21 UTC |
ID | 乙 | 2017-04-19 | 2017-04-25 | 2017-04-19 UTC--2017-04-25 UTC |
我想将它们组合成这样的重叠和非重叠时段,按“ID”和“部分”分组:
ID | 部分 | 开始 | 结尾 | 间隔 |
---|---|---|---|---|
ID | 一个 | 2017-04-12 | 2017-04-18 | 2017-04-12 UTC--2017-04-18 UTC |
ID | 甲,乙 | 2017-04-19 | 2017-04-21 | 2017-04-19 UTC--2017-04-21 UTC |
ID | 乙 | 2017-04-22 | 2017-04-25 | 2017-04-22 UTC--2017-04-25 UTC |
我试图生成具有重叠周期的中间行,但无法使用“dplyr”package 保持非重叠周期:
df_2 <- df %>%
group_by(id) %>%
summarise(drug = paste(drug, collapse = ','),
start = max(start),
end = min(end),
interval = start %--% end)
非常感谢任何想法或解决方案。 谢谢!
我建议分别创建重叠和非重叠。 如果您希望 output 行数大于输入行数,这通常是必要的。
对于重叠,我会做类似的事情:
overlap_df = df %>%
inner_join(df, by = "id", suffix = c("_1","_2")) %>%
filter(part_1 < part_2,
start_1 <= end_2,
start_2 <= end_1) %>%
mutate(part = paste0(part_1,",",part_2), # new part label
start = ifelse(start_1 < start_2, start_2, start_1), # latest start date
end = ifelse(end_1 < end_2, end_1, end_2)) %>% # earliest end date
select(ID, part, start, end)
第一个过滤条件确保每个重叠只有一个订单(例如,只有A,B
而不是B,A
。第二个和第三个过滤条件确保时间段重叠。
对于不重叠,我会区分从不重叠(与另一个时期没有任何重叠的时期)和不重叠(不重叠的时期部分)。
对于从不重叠的我会做类似的事情:
never_overlapped_df = df %>%
left_join(df, by = "id", suffix = c("_1","_2")) %>%
filter(part_1 != part_2) %>%
mutate(overlap = ifelse(start_1 <= end_2 & start_2 <= end_2, 1, 0) %>%
group_by(id, part_1, start_1, end_1) %>%
summarise(num = sum(overlap, na.rm = TRUE)) %>%
filter(is.na(num) | num == 0) %>%
select(id, part = part_1, start = start_1, end = end_1)
这个想法是找到并计算所有重叠,然后只保留没有任何重叠的记录。
对于不重叠的我会做类似的事情:
non_overlapped_df = df %>%
inner_join(df, by = "id", suffix = c("_1","_2")) %>%
filter(part_1 != part_2,
start_1 <= end_2,
start_2 <= end_1) %>% # parts are different and periods overlap
mutate(start_2 = ifelse(start_1 <= start_2 & start_2 <= end_1, start_2, NA),
end_2 = ifelse(start_1 <= end_2 & end_2 <= end_1, end_2, NA)) %>%
# discard start_2 & end_2 that are not within start_1 and end_1
group_by(id, part_1, start_1, end_1) %>%
summarise(min_start_2 = min(start_2, na.rm = TRUE),
max_end_2 = max(end_2, na.rm = TRUE)) %>%
mutate(start = ifelse(is.na(max_end_2), start_1, max_end_2),
end = ifelse(is.na(min_start_2), end_1, min_start_2)) %>%
select(id, part = part_1, start, end)
然后,您可以将它们与rbind
组合在一起:
output_df = rbind(overlap_df, never_overlapped_df, non_overlapped_df)
请注意,我假设一次最多有一个重叠(例如part = "A,B,C"
不会发生)。 这简化了问题。 解决任意数量重叠的更一般情况要复杂得多,并且需要不同的方法。
请注意,您可能还希望将“<=”更改为“<”或从结束日期中减去 1 天,以确保期间不重叠。 这取决于您如何处理时间段的边界条件。
我的第一个答案假设只重叠两个时期。 这意味着它可以对每个比较使用单个连接。 尝试重复此过程超过两个时间段会导致连接数量增加,从而导致效率低下的混乱。
为了处理加入任意(或未知)数量的重叠,我们需要一种非常不同的方法。 因此,我将其作为单独的答案提供。
第 1 步:获取所有可能的开始和结束日期的列表
all_start = df %>%
select(id, start)
all_end = df %>%
select(id, start = end)
all_start_and_end = rbind(all_start, all_end) %>%
distinct()
第 2 步:创建所有可能期间的列表
all_periods = all_start_and_end %>%
group_by(id) %>%
mutate(end = lead(start, 1, order_by = start))
第 3 步:将原始数据与所有期间重叠并汇总
overlapped = all_periods %>%
left_join(df, by = "id", suffix = c("_1","_2")) %>%
filter(start_1 <= end_2,
start_2 <= end_1) %>%
select(id, part_2, start = start_1, end = end_1) %>%
group_by(id, start, end) %>%
summarise(part = toString(part_2))
根据您的确切数据和情况:
distinct
的,以允许只有一天的时间段。part = NA
的所有时间段,则在第 1 步中,您可以添加一个非常早的日期(例如 0000-01-01)和一个非常晚的日期(例如 9999-12-31)。part = NA
的句点。part
的相邻 output 周期。 例如,第 1 行:A 部分的结束日期为 2020-01-01,第 2 行:A 部分的开始日期为 2020-01-02。 查看gaps-and-islands
标签以了解解决此问题的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.