【问题标题】:Collapse rows with overlapping ranges折叠具有重叠范围的行
【发布时间】:2017-06-04 12:04:52
【问题描述】:

我有一个带有开始和结束时间的 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

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

【问题讨论】:

  • 看起来不太容易。你已经看过 dplyr 了吗?将是我第一次尝试解决。
  • 我试过这个:mdat &lt;- outer(ranges$start, ranges$stop, function(x,y) y &gt; x) mdat[upper.tri(mdat)|col(mdat)==row(mdat)] &lt;- NA mdat 现在我只需要弄清楚如何组合所有真实的,但不确定这是否是最好的方法
  • 您将什么定义为重叠?
  • 我猜如果下一行的开始在上一行的范围内,那么我想保留下一行的“停止”。因此,如果下一行的起点是 73.75625(止损在 87.75625),而前一行的范围是 65.94312 到 79.94312,那么我希望有一个范围作为两者的组合,基本上是包含两者的范围: 65.94312 到 87.75625

标签: r subset rows overlap overlapping


【解决方案1】:

你可以试试这个:

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

【讨论】:

  • 这完美地解决了我的问题,但我还不太明白如何。感谢你的小提琴技巧,而我再次检查代码试图辨别它的魔力。
  • @Jemus42 代码首先按start 列对行进行排序。 lag(stop) 添加一个假想列,其中包含先前的 stop 值。 cummax 将保留以前的 stop 值的最高值,以便您可以与 start 列进行比较。如果start 值大于前面stop 值的最大值,则您在一个新组中。 cumsum 将累积 TRUEs,以便您拥有每个组的标识符 (g)。 summarise 将按组 id 聚合并获取请求和间隔结束。非常聪明的解决方案@Psidom :)
【解决方案2】:

这是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

在这里,通过检查前一个 start 是否大于 stop 然后使用 cumsum 来构造组。在每组内,计算开始的最小值和停止的最大值。

【讨论】:

    【解决方案3】:

    对于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
    

    【讨论】:

      猜你喜欢
      • 2012-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2021-12-09
      • 2016-12-24
      相关资源
      最近更新 更多