区间的并集和交集
Union and intersection of intervals
我有一组不同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. 将采用这些区间的并集的函数。
对于上面的例子,它将 return 这个 data.frame:
union.df <- data.frame(id=rep("a,b,c",4), start=c(100,400,600,700), end=c(325,550,675,725))
- 一个将与这些间隔相交的函数,仅当所有 ID 在该范围内重叠时才保留该范围。
对于上面的例子,它将 return 这个 data.frame:
intersection.df <- data.frame(id="a,b,c", start=610, end=640)
对于交叉路口,我首先要计算您在每个范围内的间隔数(范围的开头在此代码中标有 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
这有点尴尬,但想法是将数据展开为一系列打开和关闭事件。然后您跟踪一次打开了多少间隔。这假设每个组没有任何重叠间隔。
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
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
GenomicRanges 包提供了一些交叉和重叠功能:
library(GenomicRanges)
source("http://bioconductor.org/biocLite.R")
biocLite("Gviz")
library(Gviz)
创建一个具有相同序列名的 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))),]
我有一组不同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))
union.df <- data.frame(id=rep("a,b,c",4), start=c(100,400,600,700), end=c(325,550,675,725))
- 一个将与这些间隔相交的函数,仅当所有 ID 在该范围内重叠时才保留该范围。 对于上面的例子,它将 return 这个 data.frame:
intersection.df <- data.frame(id="a,b,c", start=610, end=640)
对于交叉路口,我首先要计算您在每个范围内的间隔数(范围的开头在此代码中标有 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
这有点尴尬,但想法是将数据展开为一系列打开和关闭事件。然后您跟踪一次打开了多少间隔。这假设每个组没有任何重叠间隔。
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
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
GenomicRanges 包提供了一些交叉和重叠功能:
library(GenomicRanges)
source("http://bioconductor.org/biocLite.R")
biocLite("Gviz")
library(Gviz)
创建一个具有相同序列名的 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))),]