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)
, tenemosv[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.