r binary-search

findInterval() con intervalos cerrados a la derecha



binary-search (3)

La gran función findInterval() en R usa findInterval() cerrados a la izquierda en su argumento vec , como se muestra en sus documentos:

si i <- findInterval(x,v) , tenemos v[i[j]] <= x[j] < v[i[j] + 1]

Si quiero subintervalos cerrados a la derecha, ¿cuáles son mis opciones? Lo mejor que he encontrado es esto:

findInterval.rightClosed <- function(x, vec, ...) { fi <- findInterval(x, vec, ...) fi - (x==vec[fi]) }

Otro también funciona:

findInterval.rightClosed2 <- function(x, vec, ...) { length(vec) - findInterval(-x, -rev(vec), ...) }

Aquí hay una pequeña prueba:

x <- c(3, 6, 7, 7, 29, 37, 52) vec <- c(2, 5, 6, 35) findInterval(x, vec) # [1] 1 3 3 3 3 4 4 findInterval.rightClosed(x, vec) # [1] 1 2 3 3 3 4 4 findInterval.rightClosed2(x, vec) # [1] 1 2 3 3 3 4 4

Pero me gustaría ver otras soluciones si hay una mejor. Por "mejor", me refiero a "de alguna manera más satisfactorio" o "no se siente como un engaño" o tal vez incluso "más eficiente". =)

(Tenga en cuenta que hay un argumento findInterval() para findInterval() , pero es diferente: solo se refiere al findInterval() final y tiene un significado diferente).


Si sus límites son intervalos, simplemente puede aumentar el intervalo correcto un bit: intervalo + c (0,0.1) haría: findinterval (valor, intervalo + c (0,0.1))


Tal vez puedas usar la opción left.open:

findInterval(x, vec, left.open=T) [1] 1 2 3 3 3 4 4


EDITAR: Limpieza importante en todos los pasillos.

Usted puede mirar en cut . De forma predeterminada, cut hace intervalos abiertos a la izquierda y cerrados a la derecha, y eso se puede cambiar usando el argumento apropiado ( right ). Para usar tu ejemplo:

x <- c(3, 6, 7, 7, 29, 37, 52) vec <- c(2, 5, 6, 35) cutVec <- c(vec, max(x)) # for cut, range of vec should cover all of x

Ahora cree cuatro funciones que deberían hacer lo mismo: dos del OP, una de Josh O''Brien, y luego cut . Se han cambiado dos argumentos para cut de la configuración predeterminada: include.lowest = TRUE creará un intervalo cerrado en ambos lados para el intervalo más pequeño (el más a la izquierda). labels = FALSE hará que el cut devuelva simplemente los valores enteros para los contenedores en lugar de crear un factor, que de lo contrario lo hace.

findInterval.rightClosed <- function(x, vec, ...) { fi <- findInterval(x, vec, ...) fi - (x==vec[fi]) } findInterval.rightClosed2 <- function(x, vec, ...) { length(vec) - findInterval(-x, -rev(vec), ...) } cutFun <- function(x, vec){ cut(x, vec, include.lowest = TRUE, labels = FALSE) } # The body of fiFun is a contribution by Josh O''Brien that got fed to the ether. fiFun <- function(x, vec){ xxFI <- findInterval(x, vec * (1 + .Machine$double.eps)) }

¿Todas las funciones devuelven el mismo resultado? Sip. (note el uso de cutVec para cutFun )

mapply(identical, list(findInterval.rightClosed(x, vec)), list(findInterval.rightClosed2(x, vec), cutFun(x, cutVec), fiFun(x, vec))) # [1] TRUE TRUE TRUE

Ahora un vector más exigente para bin:

x <- rpois(2e6, 10) vec <- c(-Inf, quantile(x, seq(.2, 1, .2)))

Probar si es idéntico (tenga en cuenta el uso de unname )

mapply(identical, list(unname(findInterval.rightClosed(x, vec))), list(findInterval.rightClosed2(x, vec), cutFun(x, vec), fiFun(x, vec))) # [1] TRUE TRUE TRUE

Y punto de referencia:

library(microbenchmark) microbenchmark(findInterval.rightClosed(x, vec), findInterval.rightClosed2(x, vec), cutFun(x, vec), fiFun(x, vec), times = 50) # Unit: milliseconds # expr min lq median uq max # 1 cutFun(x, vec) 35.46261 35.63435 35.81233 36.68036 53.52078 # 2 fiFun(x, vec) 51.30158 51.69391 52.24277 53.69253 67.09433 # 3 findInterval.rightClosed(x, vec) 124.57110 133.99315 142.06567 155.68592 176.43291 # 4 findInterval.rightClosed2(x, vec) 79.81685 82.01025 86.20182 95.65368 108.51624

A partir de esta carrera, el cut parece ser el más rápido.