繁体   English   中英

折叠具有重叠范围的行

[英]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 行的“开始”和第 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

现在我只需要弄清楚如何组合所有真实的,但不确定这是否是 go 的最佳方式

你可以试试这个:

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

这里,通过检查先前的开始是否大于停止然后使用cumsumcumsum 在每组中,计算最小开始和最大停止。

使用base Rmelt / 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连续行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

ivs ivs 的tidy解决方案:

library(dplyr)
library(ivs)

g <- iv_groups(iv(ranges$start, ranges$stop))
data.frame(start = iv_start(g),
           end = iv_end(g))
     start       end
1 65.72000  87.75625
2 89.61625 104.94062

也有效:

ranges %>% 
  group_by(gp = iv_identify_group(iv(start, stop))) %>% 
  summarise(start = min(iv_start(gp)),
            end = max(iv_end(gp))) %>% 
  select(-gp)

lmo 其他方面不错的data.table答案需要一两次调整才能可靠地工作。 由于该编辑队列已满,我将其发布为新答案:

library(data.table)
setDT(ranges)
ranges[
  order(start),
  .(start = start[1], stop = max(stop)),
  by = .(group = cumsum(start > cummax(shift(stop, fill = -Inf))))
]
#>    group    start      stop
#> 1:     1 65.72000  87.75625
#> 2:     2 89.61625 104.94062

如果输入尚未按start排序, order(start)确保它也可以工作。 如果您知道输入已经排序,请跳过此步骤。

就像在Psidom 的dplyr答案中一样,我为stop s 添加了一个cummax 当一个新范围不与它之前的范围重叠时,这一点很重要,但它确实与之前的(更长的)范围重叠。 这是说明这种情况的另一个示例:

library(data.table)

# Example data.table with the following intervals:
#   12–13
#           22––24
#               24–––27
#                  26–––29
#                          30–––––––34
#                          30––32
#                                 33––35
#                                          41–––––48
ranges <- data.table(
  start = c(12, 22, 24, 26, 30, 30, 33, 41),
  stop  = c(13, 24, 27, 29, 34, 32, 35, 48)
)

ranges[
  order(start),
  .(start = start[1], stop = max(stop)),
  by = .(group = cumsum(start > cummax(shift(stop, fill = -Inf))))
]
#>    group start stop
#> 1:     1    12   13
#> 2:     2    22   29
#> 3:     3    30   35
#> 4:     4    41   48

仅考虑前一行的解决方案将分别列出 33-35 区间。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM