2011-11-18 8 views
9

(info Background: ifelse valuta le sia delle espressioni, anche se saranno restituiti solo un'EDIT:. Questa è un'affermazione corretta Vedere la risposta di Tommy.)ifelse è mai appropriato in una situazione non vettoriale e viceversa?

c'è qualche esempio in cui ha senso usare ifelse in una situazione non vettoriale? Penso che la "leggibilità" potrebbe essere una risposta valida quando non ci interessano piccoli guadagni di efficienza, ma oltre a questo, è sempre più veloce/equivalente/migliore-in-alcun-altro modo di usare ifelse quando uno if e poi else farebbe il lavoro?

Analogamente, se ho una situazione vettoriale, è ifelse sempre lo strumento migliore da utilizzare? Sembra strano che entrambe le espressioni siano valutate. È sempre più veloce effettuare il ciclo uno alla volta e fare un normale if e poi else? Immagino che avrebbe senso solo se la valutazione delle espressioni richiedesse molto tempo. C'è qualche altra alternativa che non implichi un ciclo esplicito?

Grazie

risposta

14

In primo luogo, ifelse fa NON valutare sempre le due espressioni - solo se ci sono entrambi TRUE e FALSE elementi nel vettore di prova.

ifelse(TRUE, 'foo', stop('bar')) # "foo" 

E a mio parere:

ifelse dovrebbe non essere utilizzato in una situazione di non-vectorized. È sempre più lento e più soggetto a errori utilizzare ifelse oltre if/else:

# This is fairly common if/else code 
if (length(letters) > 0) letters else LETTERS 

# But this "equivalent" code will yield a very different result - TRY IT! 
ifelse(length(letters) > 0, letters, LETTERS) 

In situazioni vectorized però, ifelse può essere una buona scelta - ma attenzione che la lunghezza e gli attributi del risultato potrebbe non essere quello che ti aspetti (come sopra, e considero ifelse rotto in tal senso).

Ecco un esempio: tst è di lunghezza 5 e ha una classe. Mi aspetto che il risultato sia di lunghezza 10 e non abbia classe, ma non è quello che succede: ottiene una classe incompatibile e una lunghezza 5!

# a logical vector of class 'mybool' 
tst <- structure(1:5 %%2 > 0, class='mybool') 

# produces a numeric vector of class 'mybool'! 
ifelse(tst, 101:110, 201:210) 
#[1] 101 202 103 204 105 
#attr(,"class") 
#[1] "mybool" 

Perché mi aspetto che la lunghezza sia 10? Poiché la maggior parte le funzioni in "ciclo" R il vettore più breve per abbinare i più lunghi:

1:5 + 1:10 # returns a vector of length 10. 

... Ma ifelse solo cicli i sì/nessun argomento in base alla lunghezza dell'argomento tst.

Perché dovrei aspettarmi che la classe (e altri attributi) a non venga copiata dall'oggetto di prova? Perché < che restituisce un vettore logico non copia classe e attributi dai suoi argomenti (in genere numerici). Non lo fa perché in genere sarebbe molto sbagliato.

1:5 < structure(1:10, class='mynum') # returns a logical vector without class 

Infine, può essere più efficiente "fai da te"?Bene, sembra che ifelse non sia un primitivo come if e che abbia bisogno di un codice speciale per gestire NA. Se non hai NA s, può essere più veloce farlo da solo.

tst <- 1:1e7 %%2 == 0 
a <- rep(1, 1e7) 
b <- rep(2, 1e7) 
system.time(r1 <- ifelse(tst, a, b))   # 2.58 sec 

# If we know that a and b are of the same length as tst, and that 
# tst doesn't have NAs, then we can do like this: 
system.time({ r2 <- b; r2[tst] <- a[tst]; r2 }) # 0.46 secs 

identical(r1, r2) # TRUE 
+0

grazie! Non ho tempo di guardarlo ora, ma lo farò più tardi stasera. Grazie per i tuoi commenti ed esempi. –

+0

ottimi consigli ed esempi. Grazie! –

+1

Si noti che 'ifelse' esegue il riciclo vettoriale - se una delle variabili' yes' o 'no' non ha una lunghezza uguale a' test', otterrà il riciclo. Ciò significa che il codice di prova alla fine dell'esempio produrrà risultati identici solo se tutti i vettori hanno la stessa lunghezza. Prova ad esempio questo dato di input: 'tst <- sample (c (TRUE, FALSE), 1e2, replace = TRUE); a <- 1: 100; b <- - (1:50); ' – Andrie

4

Sul secondo punto, come si definisce "migliore"? Penso che ifelse() sia una delle soluzioni più leggibili, ma potrebbe non essere sempre la più veloce. Nello specifico, ho scoperto che scrivere le condizioni booleane e aggiungerle insieme può darti dei vantaggi in termini di prestazioni. Ecco un rapido esempio:

> x <- rnorm(1e6) 
> system.time(y1 <- ifelse(x > 0,1,2)) 
    user system elapsed 
    0.46 0.08 0.53 
> system.time(y2 <- (x > 0) * 1 + (x <= 0) * 2) 
    user system elapsed 
    0.06 0.00 0.06 
> identical(y1, y2) 
[1] TRUE 

Quindi, se la velocità è il tuo più grande preoccupazione, l'approccio booleano può essere migliore. Tuttavia, per la maggior parte dei miei scopi, ho trovato ifelse() abbastanza veloce ed è facile da convincere. Le tue miglia possono variare ovviamente.

+0

Ammetto vergognosamente di scrivere la versione booleana non solo perché è veloce ma perché è 31337. Ma, seriamente, una volta che ci si abitua, il codice è altrettanto facile da leggere e capire come usare il codice se. –

+0

@Chase Punti buoni –

+0

È ancora più veloce usare 'y3 <- (x <= 0) + 1' poiché non include un test ridondante. –

Problemi correlati