2012-11-20 7 views
17

La grande funzione findInterval() in R utilizza sinistra-chiuso sottointervalli nel suo argomento vec, come mostrato nelle documenti:findInterval() con intervalli di destra chiuso

se i <- findInterval(x,v), abbiamo v[i[j]] <= x[j] < v[i[j] + 1]

Se voglio intervalli secondari chiusi a destra, quali sono le mie opzioni? Il migliore che è venuta in mente è questa:

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

Un altro si lavora anche:

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

Ecco un piccolo test:

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 

ma mi piacerebbe vedere alcun altre soluzioni se ce n'è una migliore. Per "meglio", intendo "in qualche modo più soddisfacente" o "non mi sento un kludge" o forse anche "più efficiente". =)

(Si noti che c'è un argomento rightmost.closed a findInterval(), ma è diverso - si riferisce soltanto alla finale sub-intervallo ed ha un significato diverso)

+0

Cosa ne pensi: 'findInterval (x, c (-Inf, testa (vec, -1)))'? – sgibb

+0

@sgibb che non sembra fare il trucco, ho aggiunto un esempio e il tuo non dà lo stesso risultato. –

+0

Qui sono un po 'confuso, ma 'findInterval (x-1, vec)' fa quello che stai cercando? – thelatemail

risposta

10

EDIT:. maggiore pulizia in tutti navate.

Si potrebbe guardare cut. Per impostazione predefinita, cut rende gli intervalli di apertura sinistra e destra chiusi e può essere modificato utilizzando l'argomento appropriato (right). Per usare il tuo esempio:

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 

Creare ora quattro funzioni che dovrebbero fare la stessa cosa: due dalla OP, uno da Josh O'Brien, e poi cut. Due argomenti a cut sono stati modificati dalle impostazioni predefinite: include.lowest = TRUE creerà un intervallo chiuso su entrambi i lati per l'intervallo più piccolo (a sinistra). labels = FALSE causerà cut per restituire semplicemente i valori interi per i contenitori invece di creare un fattore, che altrimenti farebbe.

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)) 
} 

Tutte le funzioni restituiscono lo stesso risultato? Sì. (Notare l'uso di cutVec per cutFun)

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

Ora un vettore più esigente a bin:

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

Verificare se (uso nota del unname) identica

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

e punto di riferimento:

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 

Da questa corsa, cut sembra essere il più veloce.

+0

Grazie. Mi sembra di ricordare che 'cut' è meno efficiente, e inoltre non consente il trascinamento del margine destro di' vec' come fa 'findInterval' (vedi le righe 15-17 nel tuo output), ma quella parte potrebbe essere lavorata intorno aggiungendo un 'Inf' alla fine. –

+0

@KenWilliams, sì, coprendo l'intero intervallo di 'x' con' breaks' consente di tagliare tutto di 'x' (vedi anche la mia modifica). Per quanto riguarda l'efficienza, beh, il codice esiste già almeno. – BenBarnes

+0

@KenWilliams, se si conta la velocità nel benchmarking dell'efficienza, potrebbe non esserci molta differenza tra 'findInterval' e' cut', dopotutto. Forse il rallentamento di 'cut' di solito deriva dalla conversione in fattore e produzione di etichette? – BenBarnes

-1

Se i tuoi limiti sono intervalli, puoi semplicemente aumentare un po 'l'intervallo giusto: intervallo + c (0,0.1) farebbe: findinterval (valore, intervallo + c (0,0.1))

+0

Questo non funzionerà per una serie di motivi. Fondamentalmente, il riciclaggio farà sì che questo cambi l'intervallo giusto su ogni entrata dispari, non su tutti. Inoltre, questo presuppone che tu sappia che la discretizzazione dei dati è più ampia di 0,1. –

1

forse è possibile utilizzare l'opzione left.open:

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

Sì: questa opzione è stata aggiunta in R versione 3.3.2, rilasciata nell'ottobre 2016. Ora è il modo giusto per farlo. –

Problemi correlati