Warum können Rs ifelse-Anweisungen keine Vektoren zurückgeben?

118

Ich fand Rs ifelse-Aussagen von Zeit zu Zeit ziemlich praktisch. Beispielsweise:

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

Aber das folgende Verhalten verwirrt mich etwas.

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

Ist dies eine Design-Wahl, die über meiner Gehaltsstufe liegt?

Christopher DuBois
quelle
1
wenig seltsames Design für ifelse angesichts der Tatsache, dass einfach, wenn sonst funktioniert.
2sb
4
ifelse ist eine vektorisierte Funktion. Sie sollten für verschiedene Aufgaben verwendet werden.
Marbel

Antworten:

98

Die Dokumentation für ifelseStaaten:

ifelsegibt einen Wert mit der gleichen Form wie testdas mit Elementen , ausgewählt aus entweder gefüllt yesoder noje nachdem , ob das Element testist , TRUEoder FALSE.

Da Sie Testwerte der Länge 1 bestehen, erhalten Sie Ergebnisse der Länge 1. Wenn Sie längere Testvektoren bestehen, erhalten Sie längere Ergebnisse:

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

So ifelsewird für den speziellen Zweck gedacht einen Vektors von booleans Testen und einen Vektor der gleichen Länge zurückkehrt, gefüllt mit Elementen aus der (Vektor) entnommen yesund noArgumenten.

Aufgrund des Funktionsnamens ist es eine häufige Verwirrung, dies zu verwenden, wenn Sie wirklich nur eine normale if () {} else {}Konstruktion wünschen .

Nathan Kitchen
quelle
16
Vielleicht wollten Sie wirklich für die zweite Reihe von Aussagen if (TRUE) c(1,2) else c(3,4).
Jonathan Chang
69

Ich wette, Sie möchten eine einfache ifAnweisung anstelle von ifelse- in R ifist dies nicht nur eine Kontrollflussstruktur, sondern kann einen Wert zurückgeben:

> 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 Williams
quelle
@ Ken, das funktioniert bei mir, obwohl ich eine ständige Warnung " Warning in if (req(inputval) == "All") { : the condition has length > 1 and only the first element will be used"bekomme, was soll ich tun, um diese Warnung loszuwerden?
user5249203
1
@ user5249203, die Frage und Kens Antwort beziehen sich auf den Fall, in dem die Bedingung ein einzelner Wert ist, dh ein Vektor der Länge 1. Die Warnung zeigt an, dass req(inputval)mehr Elemente vorhanden sind. Um einen einzelnen Wert zu erhalten, können die Funktionen any()oder all()nützlich sein.
Uwe
11

Beachten Sie, dass Sie das Problem umgehen können, wenn Sie das Ergebnis wie folgt zuweisen 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
Cath
quelle
3
IMHO ist dies ermutigend, die vektorisierte ifelse()Funktion anstelle eines Kontrollflusses if ... else ...für die Zuweisung zu missbrauchen . Wenn die Bedingung eine Einzelperson TRUEoder ein FALSEWert ist, würde ich lieber schreiben a <- if (TRUE) c(1,2) else c(3,4)oderif (TRUE) a <- c(1,2) else a <- c(3,4)
Uwe
1
@Uwe, obwohl ich nicht denke, dass der Unterschied in der Leistung bei der Verwendung ifelseanstelle von if... elseim Falle einer einzelnen Bedingung wirklich ein Problem sein ifelsekann und in einigen Fällen innerhalb des Codes bevorzugt werden kann (einfache Vermutung hier), kann ich Ihnen nicht widersprechen ;-). Ich wollte nur einen Weg zeigen mit ifelse.
Cath
9

Ja, ich denke, ifelse () ist wirklich dafür gedacht, wenn Sie einen großen, langen Testvektor haben und jede einer von zwei Optionen zuordnen möchten. Zum Beispiel mache ich oft Farben für plot () auf folgende Weise:

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

Wenn Sie einen großen langen Testvektor hatten, aber Paare für Ausgaben wollten, könnten Sie vielleicht sapply()oder plyr's llply()oder so etwas verwenden.

Brendan OConnor
quelle
4

Manchmal benötigt der Benutzer nur eine switchAnweisung anstelle einer ifelse. In diesem Fall:

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

(Dies ist eine weitere Syntaxoption von Ken Williams 'Antwort.)

agenis
quelle
4

Hier ist ein Ansatz ähnlich dem von Cath vorgeschlagenen, der jedoch mit vorhandenen vorab zugewiesenen Vektoren funktionieren kann

Es basiert auf der Verwendung von get()Ähnlichem wie folgt :

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

benutze `if`, zB

> `if`(T,1:3,2:4)
[1] 1 2 3
blueskyddd
quelle
Dies ist hier die einzige Antwort, die tatsächlich die erwartete Funktionalität von ifelse bereitstellen kann.
sus_mlm
2

In Ihrem Fall wäre die Verwendung von if_elsefrom dplyrhilfreich gewesen: if_elseist strenger als ifelseund wirft einen Fehler für Ihren Fall aus:

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

Gefunden auf everydropr :

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

Kann das Ergebnis Ihrer Bedingung replizieren, um die gewünschte Länge zurückzugeben

SJGD
quelle