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.