2012-08-12 19 views
11

W R, jaki jest skuteczny sposób wyodrębniania liczb całkowitych z zakresów?Wyodrębnij liczby całkowite z zakresów

Powiedzmy mam matrycę zakresach (kolumna1 = start, column2 = koniec)

1 5 
3 6 
10 13 

Chciałbym zapisać obejmujące unikalne całkowitymi wszystkich zakresach w matrycy do obiektu:

1 
2 
3 
4 
5 
6 
10 
11 
12 
13 

Zostanie to zastosowane do macierzy zawierającej ~ 4 miliony zakresów, więc mam nadzieję, że ktoś może zaoferować rozwiązanie, które jest dość wydajne.

Odpowiedz

5

nie wiem, że jest on szczególnie skuteczny, ale jeśli matryca zakresów jest ranges następnie następujące powinny działać:

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

Zastosowanie sequence i 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 

Ponieważ wykorzystuje to tylko kod wektorowy, powinno to być dość szybkie, nawet w przypadku dużych zestawów danych. Na moim komputerze macierz wejściowa 1 milion wierszy trwa ~ 5 sekund, aby uruchomić:

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

Myślę, że PO chce, aby wynik zawierał jedną liczbę całkowitą tylko raz. – seancarmody

+0

Porównałem czas: moja odpowiedź jest zdecydowanie wolniejsza! – seancarmody

+0

@seancarmody Dziękujemy za wyróżnienie wymogu ** unikalnych ** liczb całkowitych. Będę edytować moją odpowiedź. – Andrie

12

Załóżmy, że start = 3, koniec = 7, i którą każdy oznaczony jako „1” na osi liczbowej począwszy od 1

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

skumulowana suma rozpoczęciem minus skumulowanej sumy końcach, a różnica między nimi jest

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 

i lokalizacje 1 w diff są

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

Zastosowanie tabularyzować aby umożliwić wielu startów/kończy się w tym samym miejscu, a

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

Na pytanie, co daje

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

Jest to dość szybko, na przykład Andrie za

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

(Brzmi to trochę jak seque DNA nce analysis, dla której GenomicRanges może być twoim przyjacielem; używałbyś funkcji coverage i slice w odczytach, być może wprowadzając przy pomocy readGappedAlignments).

+0

To znacznie szybciej niż w przypadku pozostałych dwóch rozwiązań. Imponujący. – seancarmody

+0

+1 Brilliant ... – Andrie

3

Czy nie jest to coś tak prostego jak:

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 

Edytuj

Wygląda jak mam zły koniec kija, ale moja odpowiedź może zostać zmodyfikowane w celu zastosowania union, choć jest to tylko opakowanie dla 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

Należy zauważyć, że w OP, 7, 8 i 9 nie pojawiają się pożądane wyniki. Chodzi o to, aby przywrócić zjednoczenie każdego z zakresów, nie cały zakres od najniższego do najwyższego w całej macierzy. – seancarmody

+0

@seancarmody Ah, rozumiem, źle zrozumiałem, wtedy twoja odpowiedź jest poprawna na linii tego, o czym myślałem. Usuwam to – James

+1

Właściwie znalazłem sposób na modyfikację. Nie różni się znacznie, ale kolejna opcja kompletności – James

Powiązane problemy