Pourquoi les instructions ifelse de R ne peuvent-elles pas renvoyer des vecteurs?

118

J'ai trouvé les déclarations ifelse de R assez pratiques de temps en temps. Par exemple:

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

Mais je suis quelque peu confus par le comportement suivant.

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

Est-ce un choix de conception supérieur à mon salaire?

Christophe DuBois
la source
1
petit design étrange pour ifelse étant donné que simple if else fonctionne.
2sb
4
ifelse est une fonction vectorisée. Ils doivent être utilisés pour différentes tâches.
marbel

Réponses:

99

La documentation pour les ifelseétats:

ifelserenvoie une valeur de la même forme que testcelle remplie d'éléments sélectionnés parmi yesou noselon que l'élément de testest TRUEou FALSE.

Puisque vous passez des valeurs de test de longueur 1, vous obtenez des résultats de longueur 1. Si vous passez des vecteurs de test plus longs, vous obtiendrez des résultats plus longs:

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

Ainsi ifelseest destiné spécifiquement à tester un vecteur de booléens et à renvoyer un vecteur de même longueur, rempli d'éléments tirés du (vecteur) yeset d' noarguments.

C'est une confusion courante, à cause du nom de la fonction, d'utiliser ceci quand vraiment vous voulez juste une if () {} else {}construction normale à la place.

Cuisine Nathan
la source
16
Peut-être que ce que vous vouliez vraiment pour la deuxième série de déclarations était if (TRUE) c(1,2) else c(3,4).
Jonathan Chang
69

Je parie que vous voulez une simple ifdéclaration au lieu de ifelse- dans R, ce ifn'est pas seulement une structure de flux de contrôle, elle peut renvoyer une valeur:

> 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
la source
@Ken, cela fonctionne pour moi, même si j'obtiens ce dont j'ai besoin en permanence, " Warning in if (req(inputval) == "All") { : the condition has length > 1 and only the first element will be used"que dois-je faire pour me débarrasser de cet avertissement?
user5249203
1
@ user5249203, la question et la réponse de Ken se réfèrent au cas où la condition est une valeur unique, c'est-à-dire un vecteur de longueur 1. L'avertissement indique qu'il y req(inputval)a plus d'éléments. Pour obtenir une valeur unique, les fonctions any()ou all()peuvent être utiles.
Uwe
12

Notez que vous pouvez contourner le problème si vous affectez le résultat dans le 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
la source
3
À mon humble avis, cela est encourageant de mal utiliser la ifelse()fonction vectorisée à la place d'un flux de contrôle if ... else ...pour l'affectation. Si la condition est un seul TRUEou une FALSEvaleur, je préférerais écrire a <- if (TRUE) c(1,2) else c(3,4)ouif (TRUE) a <- c(1,2) else a <- c(3,4)
Uwe
1
@Uwe bien que je ne pense pas que la différence de performances lors de l'utilisation ifelseau lieu de if... elsedans le cas d'une seule condition peut vraiment être un problème et ifelsepeut être préférée dans certains cas à l'intérieur du code (simple supposition ici), je ne peux pas être en désaccord avec vous ;-). Je voulais juste montrer une voie avec ifelse.
Cath
9

oui, je pense que ifelse () est vraiment conçu pour lorsque vous avez un grand vecteur de tests et que vous voulez mapper chacun à l'une des deux options. Par exemple, je fais souvent des couleurs pour plot () de cette manière:

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

Si vous avez eu un grand long vecteur de tests mais je voulais paires pour les sorties, vous pouvez utiliser sapply()ou plyr« s llply()ou quelque chose, peut-être.

Brendan OConnor
la source
4

Parfois, l'utilisateur a juste besoin d'une switchinstruction au lieu d'un ifelse. Dans ce cas:

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

(qui est une autre option de syntaxe de la réponse de Ken Williams)

Agenis
la source
4

Voici une approche similaire à celle suggérée par Cath, mais elle peut fonctionner avec des vecteurs pré-assignés existants

Il est basé sur l'utilisation de ce qui get()suit:

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

utilisez `if`, par exemple

> `if`(T,1:3,2:4)
[1] 1 2 3
blueskyddd
la source
C'est la seule réponse ici qui peut réellement fournir la fonctionnalité attendue d'ifelse.
sus_mlm le
2

Dans votre cas, l'utilisation de if_elsefrom dplyraurait été utile: if_elseest plus stricte que ifelse, et génère une erreur pour votre cas:

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

Trouvé sur chaque goutte :

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

Peut reproduire le résultat de votre condition pour renvoyer la longueur souhaitée

SJGD
la source