如何解决如何在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 | 部分 | 开始 | 结束 | 间隔 |
---|---|---|---|---|
身份证 | A | 2017-04-12 | 2017-04-21 | 2017-04-12 UTC--2017-04-21 UTC |
身份证 | B | 2017-04-19 | 2017-04-25 | 2017-04-19 UTC--2017-04-25 UTC |
我想将它们组合成这样的重叠和非重叠时期,按“ID”和“部分”分组:
id | 部分 | 开始 | 结束 | 间隔 |
---|---|---|---|---|
身份证 | A | 2017-04-12 | 2017-04-18 | 2017-04-12 UTC--2017-04-18 UTC |
身份证 | A,B | 2017-04-19 | 2017-04-21 | 2017-04-19 UTC--2017-04-21 UTC |
身份证 | B | 2017-04-22 | 2017-04-25 | 2017-04-22 UTC--2017-04-25 UTC |
我试图用重叠的时间段生成中间行,但我无法使用“dplyr”包保留非重叠的时间段:
df_2 <- df %>%
group_by(id) %>%
summarise(drug = paste(drug,collapse = ','),start = max(start),end = min(end),interval = start %--% end)
非常感谢任何想法或解决方案。谢谢!
解决方法
我建议分别创建重叠和非重叠。如果您希望输出行数大于输入行数,这通常是必要的。
对于重叠,我会做类似的事情:
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,"_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,"_2")) %>%
filter(part_1 != part_2,start_2 <= end_1) %>% # parts are different and periods overlap
mutate(start_2 = ifelse(start_1 <= start_2 & start_2 <= end_1,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,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),max_end_2),end = ifelse(is.na(min_start_2),min_start_2)) %>%
select(id,end)
然后您可以将它们与 rbind
结合起来:
output_df = rbind(overlap_df,never_overlapped_df,non_overlapped_df)
请注意,我假设一次最多有一个重叠(例如,part = "A,B,C"
不会发生)。这简化了问题。解决任意数量重叠的更一般情况要复杂得多,需要不同的方法。
请注意,您可能还想将“
,我的第一个答案假设只重叠两个时期。这意味着它可以对每个比较使用单个连接。尝试在两个以上的时间段内重复此过程会导致连接数量增加,从而导致效率低下的混乱。
为了处理加入任意(或未知)数量的重叠,我们需要一种非常不同的方法。因此,我将此作为单独的答案提供。
第 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,order_by = start))
第 3 步:将原始数据与所有时期重叠并汇总
overlapped = all_periods %>%
left_join(df,"_2")) %>%
filter(start_1 <= end_2,start_2 <= end_1) %>%
select(id,part_2,end = end_1) %>%
group_by(id,end) %>%
summarise(part = toString(part_2))
取决于您的确切数据和情况:
- 您可能需要将“
- 您可能希望删除第 1 步中的
distinct
以允许只有一天的时间段。 - 在第 1 步中,如果您希望输出包含所有带有 {{1} }.
- 完成第三步后,您可能需要使用
part = NA
过滤掉任何句点。 - 根据您的输入数据,您可能会观察到具有相同
part = NA
的相邻输出时段。例如。第 1 行:A 部分的结束日期为 2020-01-01,第 2 行:A 部分的开始日期为 2020-01-02。查看part
标记以了解解决此问题的方法。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。