2012-08-12 18 views
11

In R, was ist eine effiziente Möglichkeit, die ganzen Zahlen aus den Bereichen zu extrahieren?Ganzzahlen aus Bereichen extrahieren

Lassen Sie uns sagen, ich habe eine Matrix von Bereichen (column1 = beginnen, column2 = end)

1 5 
3 6 
10 13 

Ich mag würde die umfassende einzigartige ganze Zahlen aller Bereiche in der Matrix in ein Objekt speichern:

1 
2 
3 
4 
5 
6 
10 
11 
12 
13 

Dies würde auf eine Matrix mit ~ 4 Millionen Bereiche angewendet werden, so dass hoffentlich jemand eine Lösung anbieten kann, die etwas effizient ist.

Antwort

5

Ich weiß nicht, dass es besonders effizient ist, aber wenn Ihre Matrix von Bereichen ist ranges dann sollten folgende Arbeiten:

unique(unlist(apply(ranges, 1, function(x) x[1]:x[2]))) 
5

Verwenden sequence und rep:

x <- matrix(c(1, 5, 3, 6, 10, 13), ncol=2, byrow=TRUE) 

ranges <- function(x){ 
    len <- x[, 2] - x[, 1] + 1 
    #allocate space 
    a <- b <- vector("numeric", sum(len)) 
    a <- rep(x[, 1], len) 
    b <- sequence(len)-1 
    unique(a+b) 
} 

ranges(x) 
[1] 1 2 3 4 5 6 10 11 12 13 

Da hier nur vektorisierter Code verwendet wird, sollte dieser auch für große Datenmengen relativ schnell sein. Auf meinem Rechner nimmt eine Eingangsmatrix von 1 Million Zeilen ~ 5 Sekunden laufen:

set.seed(1) 
xx <- sample(1e6, 1e6) 
xx <- matrix(c(xx, xx+sample(1:100, 1e6, replace=TRUE)), ncol=2) 
str(xx) 
int [1:1000000, 1:2] 265509 372124 572853 908206 201682 898386 944670 660794 629110 61786 ... 

system.time(zz <- ranges(xx)) 
user system elapsed 
    4.33 0.78 5.22 

str(zz) 
num [1:51470518] 265509 265510 265511 265512 265513 ... 
+0

Ich denke, dass das OP will das Ergebnis, um jede ganze Zahl nur einmal zu kennzeichnen. – seancarmody

+0

Ich habe das Timing verglichen: Meine Antwort ist definitiv langsamer zu laufen! – seancarmody

+0

@seancarmody Vielen Dank für die Hervorhebung der Anforderung für ** einzigartige ** Ganzzahlen. Ich werde meine Antwort bearbeiten. – Andrie

12

Angenommen, Sie beginnen mußten = 3, Ende = 7, und Sie haben jeweils als ‚1‘ auf einer Reihe Linie markiert

starts:  0 0 1 0 0 0 0 0 0 ... 
ends + 1: 0 0 0 0 0 0 0 1 0 ... 

die kumulative Summe der Starts minus die kumulative Summe der Enden beginnend bei 1, und die Differenz zwischen den beiden ist

cumsum(starts): 0 0 1 1 1 1 1 1 1 ... 
cumsum(ends + 1): 0 0 0 0 0 0 0 1 1 ... 
diff:    0 0 1 1 1 1 1 0 0 

und die Positionen der 1en in der diff sind

which(diff > 0): 3 4 5 6 7 

Verwenden tabellieren für mehrere Anläufe/endet an der gleichen Stelle zu ermöglichen, und

range2 <- function(ranges) 
{ 
    max <- max(ranges) 
    starts <- tabulate(ranges[,1], max) 
    ends <- tabulate(ranges[,2] + 1L, max) 
    which(cumsum(starts) - cumsum(ends) > 0L) 
} 

Für die Frage, gibt dieser

> eg <- matrix(c(1, 3, 10, 5, 6, 13), 3) 
> range2(eg) 
[1] 1 2 3 4 5 6 10 11 12 13 

Es ist ziemlich schnell, für Andrie dem Beispiel

> system.time(runs <- range2(xx)) 
    user system elapsed 
    0.108 0.000 0.111 

(das klingt ein bisschen wie DNA Seque nce Analyse, für die GenomicRanges Ihr Freund sein könnte; Sie würden die Funktionen coverage und slice auf Lesevorgänge verwenden, vielleicht Eingabe mit readGappedAlignments).

+0

Das ist deutlich schneller als die anderen beiden Lösungen. Beeindruckend. – seancarmody

+0

+1 Brilliant ... – Andrie

3

Ist es nicht etwas so einfaches wie:

x <- matrix(c(1, 5, 3, 6, 10, 13), ncol=2, byrow=TRUE) 
do.call(":",as.list(range(x))) 
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 

bearbeiten

Sieht aus wie ich das falsche Ende des Stockes bekam, aber meine Antwort geändert werden kann union zu verwenden, obwohl dies nur ein Wrapper für unique:

Reduce("union",apply(x,1,function(y) do.call(":",as.list(y)))) 
[1] 1 2 3 4 5 6 10 11 12 13 
+0

Beachten Sie, dass im OP, 7, 8 und 9 nicht im gewünschten Ergebnis erscheinen. Die Idee ist, die Vereinigung von jedem der Bereiche nicht den gesamten Bereich von dem niedrigsten zu dem höchsten in der gesamten Matrix zurückzugeben. – seancarmody

+0

@seancarmody Ah, ich verstehe, ich habe es falsch verstanden, dann ist deine Antwort die richtige in der Art, was ich dachte. Ich werde das löschen – James

+1

Eigentlich habe ich eine Möglichkeit gefunden, es zu ändern. Nicht sehr verschieden, aber eine weitere Option für die Vollständigkeit – James