2010-10-12 13 views
12

Ho due data.frames ciascuno con tre colonne: chrom, start & stop, chiamiamoli intervalliA e intervalliB. Per ogni riga di intervalli A, sto cercando di trovare quale (se presente) riga in intervalliB contiene completamente gli intervalliUna riga - con cui intendo rangesAChrom == rangesBChrom, rangesAStart >= rangesBStart and rangesAStop <= rangesBStop.Trovare sovrapposizione in intervalli con R

In questo momento sto facendo quanto segue, che proprio non mi piace molto. Tieni presente che eseguo il looping delle righe di intervalliA per altri motivi, ma nessuno di questi motivi è probabile che sia un grosso problema, ma finisce per rendere le cose più leggibili data questa particolare soluzione.

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 

per ogni riga rangesA:

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

immagino ci deve essere una migliore (e meglio, intendo più veloce su grandi istanze di intervalliA e intervalliB) modo per fare questo rispetto al ciclo su questo costrutto. Qualche idea?

risposta

13

Questo sarebbe molto più facile/veloce se è possibile unire prima i due oggetti.

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

Per i dati di esempio:

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) 
) 

Questo farà con sapply, in modo che ogni colonna è una riga rangesA e ciascuna riga è corrispondente riga 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

Usa i pacchetti IRanges/GenomicRanges di Bioconductor, che è stato progettato per gestire questi problemi esatti (e ridimensiona in modo massiccio)

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

Ci sono alcuni contenitori adeguati per intervalli su diversi cromosomi, uno è 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

Buono puntatore sullo IRanges, dimenticato. Non sono andato a finire con questo dato che non si adattava alla mia situazione per una serie di motivi, ma comunque buono da sapere. Il mio esempio di giocattolo ha lasciato fuori un paio di bit chiave (la mia colpa) che ha reso il lavoro con IRanges difficile da capire per me, e la soluzione di unione() ha fornito un enorme aumento di velocità. Inoltre, mentre scala in modo massiccio, vediamo anche molti casi molto piccoli e quello che sto assumendo era il sovraccarico dell'S4 che stava rallentando in quei casi. – geoffjentry

+1

c'è comunque da contare anche sovrapposizioni parziali? – Cina

2

RangesA e RangesB sono chiaramente sintassi LETTO, questo può essere fatto fuori R nella riga di comando con BEDtools, estremamente veloce e flessibile con una dozzina di altre opzioni per lavorare con intervalli genomici. Poi mettere i risultati nuovamente nel R.

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

9

Il pacchetto data.table ha una funzione foverlaps() capace di fondere su intervalli di intervallo dal 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() convertiti data.frame a data.table per riferimento

  • setkey() ordina i dati.table dalle colonne fornite (in questo caso tutte le colonne, poiché non ne abbiamo fornito nessuno) e contrassegna le colonne come ordinate, che useremo in seguito per eseguire il join.

  • foverlaps() l'unione si sovrappone in modo efficiente. Vedi this answer per una spiegazione dettagliata e un confronto con altri approcci.

1

Aggiungere la soluzione dplyr.

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

uscita:

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

Immaginate una situazione in cui gli intervalliA hanno 20k righe e gli intervalli B hanno 300k righe -> insanse un enorme join -> impossibile da inserire nella memoria RAM R. Le soluzioni con IRanges sono migliori –

+0

Gli IRang non funzionavano per l'OP. Vedi sopra. – Joe

Problemi correlati