我有一个很大的时间段数据集,由一个“开始”和一个“结束”列定义。有些时期是重叠的。
我想合并(flatten / merge / collapse)所有重叠的时间段,以获得一个“开始”值和一个“结束”值。
一些示例数据:
ID start end
1 A 2013-01-01 2013-01-05
2 A 2013-01-01 2013-01-05
3 A 2013-01-02 2013-01-03
4 A 2013-01-04 2013-01-06
5 A 2013-01-07 2013-01-09
6 A 2013-01-08 2013-01-11
7 A 2013-01-12 2013-01-15
预期结果:
ID start end
1 A 2013-01-01 2013-01-06
2 A 2013-01-07 2013-01-11
3 A 2013-01-12 2013-01-15
我所尝试的:
require(dplyr)
data <- structure(list(ID = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L), class = "factor", .Label = "A"),
start = structure(c(1356998400, 1356998400, 1357084800, 1357257600,
1357516800, 1357603200, 1357948800), tzone = "UTC", class = c("POSIXct",
"POSIXt")), end = structure(c(1357344000, 1357344000, 1357171200,
1357430400, 1357689600, 1357862400, 1358208000), tzone = "UTC", class = c("POSIXct",
"POSIXt"))), .Names = c("ID", "start", "end"), row.names = c(NA,
-7L), class = "data.frame")
remove.overlaps <- function(data){
data2 <- data
for ( i in 1:length(unique(data$start))) {
x3 <- filter(data2, start>=data$start[i] & start<=data$end[i])
x4 <- x3[1,]
x4$end <- max(x3$end)
data2 <- filter(data2, start<data$start[i] | start>data$end[i])
data2 <- rbind(data2,x4)
}
data2 <- na.omit(data2)}
data <- remove.overlaps(data)
6条答案
按热度按时间olhwl3o21#
这里有一个可能的解决方案。这里的基本思想是使用
cummax
函数将滞后的start
日期与最大结束日期“直到现在”进行比较,并创建一个索引,将数据分成组mv1qrgav2#
@大卫Arenburg的回答很棒-但我遇到了一个问题,即较早的间隔在较晚的间隔之后结束-但在
summarise
调用中使用last
导致错误的结束日期。我建议将first(start)
和last(end)
更改为min(start)
和max(end)
此外,正如@Jonno Bourne所提到的,在应用该方法之前,按
start
和任何分组变量进行排序很重要。uklbhaso3#
为了完整起见,the
IRanges
package on Bioconductor有一些整洁的函数,可用于处理日期或日期时间范围。其中一个是reduce()
函数,它合并重叠或相邻的范围。然而,
IRanges
有一个缺点,因为它在整数范围内工作(因此得名),所以使用IRanges
函数的便利性是以来回转换Date
或POSIXct
对象为代价的。另外,
dplyr
似乎不能很好地与IRanges
一起使用(至少从我有限的dplyr
经验来看),所以我使用data.table
:代码变体是
在这两个变体中,使用了
lubridate
包中的as_datetime()
,当将数字转换为POSIXct
对象时,它可以指定原点。看到
IRanges
方法与David's answer方法的基准比较会很有趣。nr7wwzry4#
我认为你可以用dplyr和ivs包很好地解决这个问题,它是为处理 interval vectors 而设计的,就像你在这里所做的一样。它的灵感来自IRanges,但更适合在tidyverse中使用,并且完全通用,因此它可以自动处理日期间隔(无需转换为数字并返回)。
关键是将开始/结束边界组合成单个区间向量列,然后使用
iv_groups()
。这将合并区间向量中的所有重叠区间,并返回合并重叠后剩余的区间。看起来你想按ID来做,所以我也按ID分组了。
由reprex package(v2.0.1)于2022-04-05创建
km0tfn4u5#
看起来我有点迟到了,但我用@zach的代码并使用下面的
data.table
重写了它。我没有做全面的测试,但这似乎比tidy
版本快20%左右。(我无法测试IRange
方法,因为R3.5.1还没有提供该包)此外,fwiw,接受的答案没有捕获一个日期范围完全在另一个日期范围内的边缘情况(例如,
2018-07-07
到2017-07-14
在2018-05-01
到2018-12-01
内)。@Zach的回答确实抓住了这个边缘情况。kkih6yb86#
基准沿着更快的
data.table
解决方案首先,我附和@enmyj和@zach,当一个范围完全在另一个范围内时,公认答案中的解决方案会给出错误的结果。
一种更快的方法,让人想起在公认的答案中提出的方法:
1.按
ID
排序,然后是所有日期(start
和end
组合)。1.开始日期数的累计和减去结束日期数的累计和。
1.找出和为
0
的索引。这些行上的日期是每个重叠日期范围集合的结束日期。下一行的日期是下一个重叠日期范围集合的开始日期。这些索引还可以用于轻松地执行其他列的汇总计算。这只涉及一些向量化的调用,没有分组操作,因此性能非常高。
作为函数:
基准测试
基准测试使用较大的
data.table
。时间:
聚合其他列
flatten
使用的方法还可以轻松地聚合data.table
中的其他列。