Perché le istruzioni ifelse di R non possono restituire vettori?


118

Ho trovato le dichiarazioni ifelse di R abbastanza utili di tanto in tanto. Per esempio:

ifelse(TRUE,1,2)
# [1] 1
ifelse(FALSE,1,2)
# [1] 2

Ma sono un po 'confuso dal seguente comportamento.

ifelse(TRUE,c(1,2),c(3,4))
# [1] 1
ifelse(FALSE,c(1,2),c(3,4))
# [1] 3

È una scelta di design superiore al mio livello di pagamento?


1
design poco strano per ifelse dato il fatto che semplice if else funziona.
2sb

4
ifelse è una funzione vettorializzata. Dovrebbero essere usati per compiti diversi.
marbel

Risposte:


99

La documentazione per gli ifelsestati:

ifelserestituisce un valore con la stessa forma di quello testriempito con elementi selezionati da yeso a noseconda che l'elemento di testsia TRUEo FALSE.

Poiché stai superando i valori di test di lunghezza 1, ottieni risultati di lunghezza 1. Se passi vettori di test più lunghi, otterrai risultati più lunghi:

> ifelse(c(TRUE, FALSE), c(1, 2), c(3, 4))
[1] 1 4

Quindi ifelseè inteso allo scopo specifico di testare un vettore di booleani e restituire un vettore della stessa lunghezza, riempito con elementi presi dal (vettore) yese noargomenti.

È una confusione comune, a causa del nome della funzione, usarlo quando in realtà si desidera invece solo una if () {} else {}costruzione normale .


16
Forse quello che volevi veramente per la seconda serie di affermazioni era if (TRUE) c(1,2) else c(3,4).
Jonathan Chang,

69

Scommetto che vuoi una semplice ifdichiarazione invece di ifelse- in R, ifnon è solo una struttura di flusso di controllo, può restituire un valore:

> if(TRUE) c(1,2) else c(3,4)
[1] 1 2
> if(FALSE) c(1,2) else c(3,4)
[1] 3 4

@Ken, questo funziona per me, anche se ottengo quello che mi serve un avvertimento costante, " Warning in if (req(inputval) == "All") { : the condition has length > 1 and only the first element will be used"cosa devo fare per sbarazzarmi di questo avvertimento?
user5249203

1
@ user5249203, la domanda e la risposta di Ken si riferiscono al caso in cui la condizione è un valore singolo, ovvero un vettore di lunghezza 1. L'avvertenza indica che req(inputval)ha più elementi. Per ottenere un singolo valore le funzioni any()o all()potrebbero essere utili.
Uwe

12

Nota che puoi aggirare il problema se assegni il risultato all'interno di ifelse:

ifelse(TRUE, a <- c(1,2), a <- c(3,4))
a
# [1] 1 2

ifelse(FALSE, a <- c(1,2), a <- c(3,4))
a
# [1] 3 4

3
IMHO, questo incoraggia a fare un uso improprio della ifelse()funzione vettorializzata al posto di un flusso di controllo if ... else ...per l'assegnazione. Se la condizione è un singolo TRUEo un FALSEvalore, preferirei scrivere a <- if (TRUE) c(1,2) else c(3,4)oif (TRUE) a <- c(1,2) else a <- c(3,4)
Uwe

1
@ Uwe anche se non penso che la differenza di prestazioni quando si usa ifelseinvece di if... elsein caso di una singola condizione possa davvero essere un problema e ifelsepotrebbe essere preferita in alcuni casi all'interno del codice (semplice ipotesi qui), non posso essere in disaccordo con te ;-). Volevo solo mostrare un modo con ifelse.
Cath

9

sì, penso che ifelse () sia davvero progettato per quando si dispone di un lungo vettore di test e si desidera mappare ciascuno di essi su una delle due opzioni. Ad esempio, faccio spesso i colori per plot () in questo modo:

plot(x,y, col = ifelse(x>2,  'red', 'blue'))

Se avessi un lungo vettore di test ma volessi coppie per gli output, potresti usare sapply()o plyrs llply()o qualcosa del genere.


4

A volte l'utente ha solo bisogno di una switchdichiarazione invece di un file ifelse. In quel caso:

condition <- TRUE
switch(2-condition, c(1, 2), c(3, 4))
#### [1] 1 2

(che è un'altra opzione di sintassi della risposta di Ken Williams)


4

Ecco un approccio simile a quello suggerito da Cath, ma può funzionare con vettori pre-assegnati esistenti

Si basa sull'uso di get()simili così:

a <- c(1,2)
b <- c(3,4)
get(ifelse(TRUE, "a", "b"))
# [1] 1 2

4

usa "if", ad es

> `if`(T,1:3,2:4)
[1] 1 2 3

Questa è l'unica risposta qui che può effettivamente fornire la funzionalità prevista di ifelse.
sus_mlm

2

Nel tuo caso, l'utilizzo di if_elsefrom dplyrsarebbe stato utile: if_elseè più rigoroso di ifelsee genera un errore per il tuo caso:

library(dplyr)
if_else(TRUE,c(1,2),c(3,4))
#> `true` must be length 1 (length of `condition`), not 2

0

Trovato su everydropr :

ifelse(rep(TRUE, length(c(1,2))), c(1,2),c(3,4))
#>[1] 1 2

Può replicare il risultato della tua condizione per restituire la lunghezza desiderata

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.