2010-10-12 13 views
12

Tengo dos data.frames cada uno con tres columnas: chrom, start & stop, llamémosles rangesA y rangesB. Para cada fila de rangos A, estoy buscando para encontrar la fila (en el caso) en los rangos B que contiene completamente la fila RangosUna fila, me refiero a rangesAChrom == rangesBChrom, rangesAStart >= rangesBStart and rangesAStop <= rangesBStop.Encontrar superposición en rangos con R

En este momento estoy haciendo lo siguiente, que simplemente no me gusta mucho. Tenga en cuenta que estoy recorriendo las filas de rangos A por otras razones, pero ninguna de esas razones es probable que sea importante, simplemente hace que las cosas sean más legibles dada esta solución en particular.

rangesA:

chrom start stop 
5  100  105 
1  200  250 
9  275  300 

rangesB:

chrom start stop 
    1  200  265 
    5  99  106 
    9  275  290 

para cada fila de rangesA:

matches <- which((rangesB[,'chrom'] == rangesA[row,'chrom']) && 
       (rangesB[,'start'] <= rangesA[row, 'start']) && 
       (rangesB[,'stop'] >= rangesA[row, 'stop'])) 

Calculo Tiene que haber una mejor (y por una mejor, me refiero más rápido en grandes instancias de rangeA y rangeB) forma de hacer esto que en bucle sobre este constructo. ¿Algunas ideas?

Respuesta

13

Esto sería mucho más fácil/más rápido si puede fusionar los dos objetos primero.

ranges <- merge(rangesA,rangesB,by="chrom",suffixes=c("A","B")) 
ranges[with(ranges, startB <= startA & stopB >= stopA),] 
# chrom startA stopA startB stopB 
#1  1 200 250 200 265 
#2  5 100 105  99 106 
2

Para sus datos de ejemplo:

rangesA <- data.frame(
    chrom = c(5, 1, 9), 
    start = c(100, 200, 275), 
    stop = c(105, 250, 300) 
) 
rangesB <- data.frame(
    chrom = c(1, 5, 9), 
    start = c(200, 99, 275), 
    stop = c(265, 106, 290) 
) 

Esto hacerlo con sapply, de manera que cada columna es una fila en rangesA y cada fila es correspondiente fila en rangesB:

> sapply(rangesA$stop, '>=', rangesB$start) & sapply(rangesA$start, '<=', rangesB$stop) 
     [,1] [,2] [,3] 
[1,] FALSE TRUE FALSE 
[2,] TRUE FALSE FALSE 
[3,] FALSE FALSE TRUE 
18

Utilice los paquetes IRanges/GenomicRanges de Bioconductor, que está hecho para tratar estos problemas exactos (y escalas masivas)

source("http://bioconductor.org/biocLite.R") 
biocLite("IRanges") 

Hay algunos contenedores adecuados para rangos en diferentes cromosomas, uno es RangesList

library(IRanges) 
rangesA <- split(IRanges(rangesA$start, rangesA$stop), rangesA$chrom) 
rangesB <- split(IRanges(rangesB$start, rangesB$stop), rangesB$chrom) 
#which rangesB wholly contain at least one rangesA? 
ov <- countOverlaps(rangesB, rangesA, type="within")>0 
+1

Buena puntero en IRanges, se olvidó de eso. No terminé yendo con esto porque no encajaba en mi propia situación por una variedad de razones, pero aún así es bueno saberlo. Mi ejemplo de juguete dejó un par de bits clave (mi propia culpa) que hace que trabajar con IRanges me resulte difícil de descifrar, y la solución merge() proporcionó una aceleración masiva tal como estaba. Además, si bien se escala masivamente también vemos muchos casos muy pequeños y lo que supongo fue que la sobrecarga del S4 estaba disminuyendo en esos casos. – geoffjentry

+1

¿hay alguna manera de contar también superposiciones parciales? – Cina

2

RangesA y RangesB son claramente sintaxis BED, esto se puede hacer fuera de R en la línea de comandos con BEDtools, extremadamente rápido y flexible con una docena de otras opciones para trabajar con intervalos genómicos. A continuación, poner los resultados de nuevo en R.

https://code.google.com/p/bedtools/

9

El paquete data.table tiene una función de foverlaps() que es capaz de fusionarse sobre rangos de intervalo desde V1.9.4:

require(data.table) 
setDT(rangesA) 
setDT(rangesB) 

setkey(rangesB) 
foverlaps(rangesA, rangesB, type="within", nomatch=0L) 
# chrom start stop i.start i.stop 
# 1:  5 99 106  100 105 
# 2:  1 200 265  200 250 
  • setDT() conversos data.frame a data.table por referencia

  • setkey() clasifica el data.table por las columnas proporcionadas (en este caso, todas las columnas, ya que no proporcionó ninguna), y marca esas columnas como ordenadas, que usaremos más adelante para realizar la unión.

  • foverlaps() hace que la superposición se una de manera eficiente. Ver this answer para una explicación detallada y comparación con otros enfoques.

1

Añado la solución dplyr.

library(dplyr) 
inner_join(rangesA, rangesB, by="chrom") %>% 
    filter(start.y < start.x | stop.y > stop.x) 

Salida:

chrom start.x stop.x start.y stop.y 
1  5  100 105  99 106 
2  1  200 250  200 265 
+0

Imagine una situación en la que los rangos A tienen 20k filas, y los rangosB tienen 300k filas -> insanse join enorme -> imposible de caber en la memoria R RAM. Las soluciones con IRanges son mejores –

+0

IRanges no funcionó para el OP. Véase más arriba. – Joe

Cuestiones relacionadas