【问题标题】:Union and intersection of intervals区间的并集和交集
【发布时间】:2015-07-16 18:23:00
【问题描述】:

我有一组不同 id 的区间。例如:

df <- data.frame(id=c(rep("a",4),rep("b",2),rep("c",3)), start=c(100,250,400,600,150,610,275,600,700), end=c(200,300,550,650,275,640,325,675,725))

每个id的区间不重叠,但不同id的区间可能重叠。这是一张图片:

plot(range(df[,c(2,3)]),c(1,nrow(df)),type="n",xlab="",ylab="",yaxt="n")
for ( ii in 1:nrow(df) ) lines(c(df[ii,2],df[ii,3]),rep(nrow(df)-ii+1,2),col=as.numeric(df$id[ii]),lwd=2)
legend("bottomleft",lwd=2,col=seq_along(levels(df$id)),legend=levels(df$id))

我正在寻找的是两个功能: 1. 将这些区间合并的函数。 对于上面的例子,它会返回这个data.frame:

union.df <- data.frame(id=rep("a,b,c",4), start=c(100,400,600,700), end=c(325,550,675,725))
  1. 一个与这些区间相交的函数,仅当该范围的所有 id 重叠时才保留一个范围。 对于上面的例子,它会返回这个data.frame:

intersection.df &lt;- data.frame(id="a,b,c", start=610, end=640)

【问题讨论】:

  • 试试 ?intersect 和 ?union
  • intersectunion 不起作用 - 它们适用于离散集,而不是区间。
  • 您能否澄清一下如何获得“这些区间及其交集的并集”,以及这如何与您的 id 一起使用?假设您在一个人中已经有多个不重叠的间隔,所有间隔的交集将为空。同样,我不明白工会是从哪里来的。

标签: r intervals


【解决方案1】:

对于交叉点,我将首先计算您在每个范围内的间隔数(在此代码中,范围的开头标有ord.dirs$x,范围内的间隔数为ord.dirs$z ):

dirs <- data.frame(x=c(df$start, df$end), y=rep(c(1, -1), each=nrow(df)))
ord.dirs <- dirs[order(dirs$x),]
ord.dirs$z <- cumsum(ord.dirs$y)
ord.dirs <- ord.dirs[!duplicated(ord.dirs$x, fromLast=T),]
ord.dirs
#      x  y z
# 1  100  1 1
# 5  150  1 2
# 10 200 -1 1
# 2  250  1 2
# 14 275 -1 2
# 11 300 -1 1
# 16 325 -1 0
# 3  400  1 1
# 12 550 -1 0
# 8  600  1 2
# 6  610  1 3
# 15 640 -1 2
# 13 650 -1 1
# 17 675 -1 0
# 9  700  1 1
# 18 725 -1 0

现在您只需要获取具有正确间隔数的范围(在本例中为 3):

pos.all <- which(ord.dirs$z == length(unique(df$id)))
data.frame(start=ord.dirs$x[pos.all], end=ord.dirs$x[pos.all+1])
#   start end
# 1   610 640

您可以类似地使用ord.dirs 来获取集合的并集:

zero.pos <- which(ord.dirs$z == 0)
data.frame(start=c(ord.dirs$x[1], ord.dirs$x[head(zero.pos, -1)+1]),
           end=ord.dirs$x[zero.pos])
#   start end
# 1   100 325
# 2   400 550
# 3   600 675
# 4   700 725

【讨论】:

    【解决方案2】:

    这有点尴尬,但想法是将数据展开为一系列打开和关闭事件。然后您跟踪一次打开多少个间隔。这假设每个组没有任何重叠的间隔。

    df <- data.frame(id=c(rep("a",4),rep("b",2),rep("c",3)), start=c(100,250,400,600,150,610,275,600,700), end=c(200,300,550,650,275,640,325,675,725))
    
    
    sets<-function(start, end, group, overlap=length(unique(group))) {
        dd<-rbind(data.frame(pos=start, event=1), data.frame(pos=end, event=-1))
        dd<-aggregate(event~pos, dd, sum)
        dd<-dd[order(dd$pos),]
        dd$open <- cumsum(dd$event)
        r<-rle(dd$open>=overlap)
        ex<-cumsum(r$lengths-1 + rep(1, length(r$lengths))) 
        sx<-ex-r$lengths+1
        cbind(dd$pos[sx[r$values]],dd$pos[ex[r$values]+1])
    
    } 
    
    #union
    with(df, sets(start, end, id,1))
    #     [,1] [,2]
    # [1,]  100  325
    # [2,]  400  550
    # [3,]  600  675
    # [4,]  700  725
    
    #overlap
    with(df, sets(start, end, id,3))
    #      [,1] [,2]
    # [1,]  610  640
    

    【讨论】:

      【解决方案3】:

      intervals 包解决了联合部分的问题:

      require(intervals)
      idf <- Intervals(df[,2:3])
      as.data.frame(interval_union(idf))
      

      对于相交部分,取决于区间的定义方式:

      idl <- lapply(unique(df$id),function(x){var <- as(Intervals(df[df$id==x,2:3]),"Intervals_full");closed(var)[,1]<- FALSE;return(var)})
      idt <- idl[[1]]
      for(i in idl)idt <- interval_intersection(idt,i)
      res <- as.data.frame(idt) 
      res
         V1  V2
      1 610 640
      

      【讨论】:

      • 刚刚编辑了答案以应对 2. 部分。可以使用默认的封闭间隔并删除结果中具有相同条目的行(在这种情况下为 275 275)。一切都取决于间隔是打开还是关闭。
      【解决方案4】:

      GenomicRanges 包提供了一些相交和重叠功能:

      library(GenomicRanges)
      source("http://bioconductor.org/biocLite.R")
      biocLite("Gviz")    
      library(Gviz)
      

      创建一个具有相同 seqnames 的 Grange 对象(这很重要)

      df <- data.frame(id=c(rep("a",4),rep("b",2),rep("c",3)),     start=c(100,250,400,600,150,610,275,600,700), end=c(200,300,550,650,275,640,325,675,725))
      gr <- GRanges(seqnames = rep(1,nrow(df)),IRanges(start = df$start,end =      df$end))
      

      现在您也可以使用 Gviz 包绘制范围。

      d0 <- GenomeAxisTrack()
      d1 <- AnnotationTrack(gr,group = df$id,fill=df$id)
      plotTracks(c(d0,d1))
      

      联合是通过reduce完成的,其中区间被折叠

      as.data.frame(reduce(gr))[,2:3]
      

      相交是通过 findoverlaps 完成的。之后,按与 3 个范围重叠的范围进行过滤。

      OL <- as.data.frame(findOverlaps(gr,type="within"))
      table(OL[,1])
      
      df[as.numeric(names(which(table(OL[,1])==3))),]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-05
        • 1970-01-01
        • 2020-04-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多