2012-08-12 30 views
11

En R, ¿cuál es una forma eficiente de extraer los enteros de rangos?Extraer enteros de rangos

Digamos que tengo una matriz de rangos (columna 1 = iniciar, columna2 = fin)

1 5 
3 6 
10 13 

me gustaría almacenar los que abarcan enteros únicas de todos los rangos en la matriz en un objeto:

1 
2 
3 
4 
5 
6 
10 
11 
12 
13 

Esto se aplicaría a una matriz que contiene ~ 4 millones de rangos, por lo que es de esperar que alguien pueda ofrecer una solución que sea bastante eficiente.

Respuesta

5

No sé que es particularmente eficiente, pero si su matriz de rangos es ranges continuación, el siguiente debería funcionar:

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

Uso sequence y 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 

Como esto utiliza solo código vectorizado, esto debería ser bastante rápido, incluso para grandes conjuntos de datos. En mi máquina una matriz de entrada de 1 millón de filas lleva ~ 5 segundos para ejecutar:

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

Creo que el OP quiere que el resultado muestre cada número entero solo una vez. – seancarmody

+0

He comparado el tiempo: ¡mi respuesta es definitivamente más lenta de ejecutar! – seancarmody

+0

@seancarmody Gracias por destacar el requisito de enteros ** únicos **. Editaré mi respuesta. – Andrie

12

Supongamos que tenemos start = 3, final = 7, y que había marcado cada uno como un '1' en una recta numérica a partir de 1

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

la suma acumulada de las aperturas menos la suma acumulada de los extremos, y la diferencia entre los dos, es

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 

y las ubicaciones de los de 1 en el diff son

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

Uso tabular para permitir múltiples inicia/termina en el mismo lugar, y

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

Para la pregunta, esto da

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

Es bastante rápido, por ejemplo de Andrie

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

(esto suena un poco como DNA seque nce análisis, para el cual GenomicRanges podría ser su amigo; usaría las funciones coverage y slice en lecturas, quizás ingrese con readGappedAlignments).

+0

Eso es significativamente más rápido que las otras dos soluciones. Impresionante. – seancarmody

+0

+1 Brilliant ... – Andrie

3

¿No es algo tan simple como:

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 

Editar

Parece como si tuviera el lado equivocado de la vara, pero mi respuesta puede ser modificado para utilizar union, aunque esto es solo un contenedor para 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

Tenga en cuenta que en el OP, 7, 8 y 9 no aparecen en el resultado deseado. La idea es devolver la unión de cada uno de los rangos, no el rango completo desde el más bajo al más alto en toda la matriz. – seancarmody

+0

@seancarmody Ah, ya veo, lo malentendí, entonces tu respuesta es la correcta en la línea de lo que estaba pensando. Eliminaré esto – James

+1

En realidad, encontré una forma de modificarlo. No es muy diferente, pero es otra opción para completar – James

Cuestiones relacionadas