折叠具有重叠范围的行
Collapse rows with overlapping ranges
我有一个 data.frame 开始和结束时间:
ranges<- data.frame(start = c(65.72000,65.72187, 65.94312,73.75625,89.61625),stop = c(79.72187,79.72375,79.94312,87.75625,104.94062))
> ranges
start stop
1 65.72000 79.72187
2 65.72187 79.72375
3 65.94312 79.94312
4 73.75625 87.75625
5 89.61625 104.94062
在此示例中,第 2 行和第 3 行中的范围完全在第 1 行 'start' 和第 4 行停止之间的范围内。因此,重叠范围 1-4 应折叠为一个范围:
> ranges
start stop
1 65.72000 87.75625
5 89.61625 104.94062
我试过这个:
mdat <- outer(ranges$start, ranges$stop, function(x,y) y > x)
mdat[upper.tri(mdat)|col(mdat)==row(mdat)] <- NA
mdat
现在我只需要弄清楚如何将所有真实的组合起来,但不确定这是否是最好的方法
你可以试试这个:
library(dplyr)
ranges %>%
arrange(start) %>%
group_by(g = cumsum(cummax(lag(stop, default = first(stop))) < start)) %>%
summarise(start = first(start), stop = max(stop))
# A tibble: 2 × 3
# g start stop
# <int> <dbl> <dbl>
#1 0 65.72000 87.75625
#2 1 89.61625 104.94062
这是一个data.table
解决方案
library(data.table)
setDT(ranges)
ranges[, .(start=min(start), stop=max(stop)),
by=.(group=cumsum(c(1, tail(start, -1) > head(stop, -1))))]
group start stop
1: 1 65.72000 87.75625
2: 2 89.61625 104.94062
在这里,通过检查前一个开始是否大于停止然后使用cumsum
来构造组。在每个组内,计算开始的最小值和停止的最大值。
有了 base R
和 melt / unstack
,让我们再添加几个日期,使问题更有趣和通用:
ranges<- data.frame(start = c(65.72000,65.72187, 65.94312,73.75625,89.61625,105.1,104.99),stop = c(79.72187,79.72375,79.94312,87.75625,104.94062,110.22,108.01))
ranges
# start stop
#1 65.72000 79.72187
#2 65.72187 79.72375
#3 65.94312 79.94312
#4 73.75625 87.75625
#5 89.61625 104.94062
#6 105.10000 110.22000
#7 104.99000 108.01000
library(reshape2)
ranges <- melt(ranges)
ranges <- ranges[order(ranges$value),]
ranges
# variable value
#1 start 65.72000
#2 start 65.72187
#3 start 65.94312
#4 start 73.75625
#8 stop 79.72187
#9 stop 79.72375
#10 stop 79.94312
#11 stop 87.75625
#5 start 89.61625
#12 stop 104.94062
#7 start 104.99000
#6 start 105.10000
#14 stop 108.01000
#13 stop 110.22000
现在从上面可以看出,(有一个合理的假设,我们有一个所有值中最小的起始值和一个所有值中最大的停止值),问题简化为寻找模式 stop
后跟连续行中的 start
,这将是除了第一行和最后一行之外我们唯一感兴趣的点(找到重叠范围)。以下代码实现了这一点:
indices <- intersect(which(ranges$variable=='start')-1, which(ranges$variable=='stop'))
unstack(ranges[c(1, sort(c(indices, indices+1)), nrow(ranges)),], value~variable)
# start stop
#1 65.72000 87.75625
#2 89.61625 104.94062
#3 104.99000 110.22000
我有一个 data.frame 开始和结束时间:
ranges<- data.frame(start = c(65.72000,65.72187, 65.94312,73.75625,89.61625),stop = c(79.72187,79.72375,79.94312,87.75625,104.94062))
> ranges
start stop
1 65.72000 79.72187
2 65.72187 79.72375
3 65.94312 79.94312
4 73.75625 87.75625
5 89.61625 104.94062
在此示例中,第 2 行和第 3 行中的范围完全在第 1 行 'start' 和第 4 行停止之间的范围内。因此,重叠范围 1-4 应折叠为一个范围:
> ranges
start stop
1 65.72000 87.75625
5 89.61625 104.94062
我试过这个:
mdat <- outer(ranges$start, ranges$stop, function(x,y) y > x)
mdat[upper.tri(mdat)|col(mdat)==row(mdat)] <- NA
mdat
现在我只需要弄清楚如何将所有真实的组合起来,但不确定这是否是最好的方法
你可以试试这个:
library(dplyr)
ranges %>%
arrange(start) %>%
group_by(g = cumsum(cummax(lag(stop, default = first(stop))) < start)) %>%
summarise(start = first(start), stop = max(stop))
# A tibble: 2 × 3
# g start stop
# <int> <dbl> <dbl>
#1 0 65.72000 87.75625
#2 1 89.61625 104.94062
这是一个data.table
解决方案
library(data.table)
setDT(ranges)
ranges[, .(start=min(start), stop=max(stop)),
by=.(group=cumsum(c(1, tail(start, -1) > head(stop, -1))))]
group start stop
1: 1 65.72000 87.75625
2: 2 89.61625 104.94062
在这里,通过检查前一个开始是否大于停止然后使用cumsum
来构造组。在每个组内,计算开始的最小值和停止的最大值。
有了 base R
和 melt / unstack
,让我们再添加几个日期,使问题更有趣和通用:
ranges<- data.frame(start = c(65.72000,65.72187, 65.94312,73.75625,89.61625,105.1,104.99),stop = c(79.72187,79.72375,79.94312,87.75625,104.94062,110.22,108.01))
ranges
# start stop
#1 65.72000 79.72187
#2 65.72187 79.72375
#3 65.94312 79.94312
#4 73.75625 87.75625
#5 89.61625 104.94062
#6 105.10000 110.22000
#7 104.99000 108.01000
library(reshape2)
ranges <- melt(ranges)
ranges <- ranges[order(ranges$value),]
ranges
# variable value
#1 start 65.72000
#2 start 65.72187
#3 start 65.94312
#4 start 73.75625
#8 stop 79.72187
#9 stop 79.72375
#10 stop 79.94312
#11 stop 87.75625
#5 start 89.61625
#12 stop 104.94062
#7 start 104.99000
#6 start 105.10000
#14 stop 108.01000
#13 stop 110.22000
现在从上面可以看出,(有一个合理的假设,我们有一个所有值中最小的起始值和一个所有值中最大的停止值),问题简化为寻找模式 stop
后跟连续行中的 start
,这将是除了第一行和最后一行之外我们唯一感兴趣的点(找到重叠范围)。以下代码实现了这一点:
indices <- intersect(which(ranges$variable=='start')-1, which(ranges$variable=='stop'))
unstack(ranges[c(1, sort(c(indices, indices+1)), nrow(ranges)),], value~variable)
# start stop
#1 65.72000 87.75625
#2 89.61625 104.94062
#3 104.99000 110.22000