Por que as instruções ifelse de R não retornam vetores?

118

Eu descobri que as declarações ifelse de R são muito úteis de vez em quando. Por exemplo:

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

Mas estou um tanto confuso com o seguinte comportamento.

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

Esta é uma escolha de design que está acima do meu salário?

Christopher DuBois
fonte
1
design pouco estranho para ifelse, dado o fato de que o if else funciona simples.
2sb
4
ifelse é uma função vetorizada. Eles devem ser usados ​​para tarefas diferentes.
marbel

Respostas:

99

A documentação para os ifelseestados:

ifelseretorna um valor com a mesma forma testque é preenchido com elementos selecionados de yesou, nodependendo se o elemento de testé TRUEou FALSE.

Como você está passando nos valores de teste de comprimento 1, está obtendo resultados de comprimento 1. Se você passar em vetores de teste mais longos, obterá resultados mais longos:

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

Assim, ifelsedestina-se ao propósito específico de testar um vetor de booleanos e retornar um vetor de mesmo comprimento, preenchido com elementos retirados do (vetor) yese noargumentos.

É uma confusão comum, por causa do nome da função, usá-la quando na verdade você quer apenas uma if () {} else {}construção normal .

Nathan Kitchen
fonte
16
Talvez o que você realmente desejasse para o segundo conjunto de afirmações fosse if (TRUE) c(1,2) else c(3,4).
Jonathan Chang
69

Aposto que você quer uma ifdeclaração simples em vez de ifelse- em R, ifnão é apenas uma estrutura de fluxo de controle, ela pode retornar um valor:

> 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
fonte
@Ken, isso funciona para mim, embora eu receba o que preciso de um aviso constante, " Warning in if (req(inputval) == "All") { : the condition has length > 1 and only the first element will be used"o que devo fazer para me livrar desse aviso?
user5249203
1
@ user5249203, a pergunta e a resposta de Ken referem-se ao caso em que a condição é um valor único, ou seja, um vetor de comprimento 1. O aviso indica que req(inputval)tem mais elementos. Para obter um único valor, as funções any()ou all()podem ser úteis.
Uwe
12

Observe que você pode contornar o problema se atribuir o resultado dentro de 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
fonte
3
IMHO, isso é encorajador o uso indevido da ifelse()função vetorizada no lugar de um fluxo de controle if ... else ...para atribuição. Se a condição for um único valor TRUEou FALSE, prefiro escrever a <- if (TRUE) c(1,2) else c(3,4)ouif (TRUE) a <- c(1,2) else a <- c(3,4)
Uwe
1
@Uwe embora eu não ache a diferença no desempenho ao usar em ifelsevez de if... elseno caso de uma única condição pode realmente ser um problema e ifelsepode ser preferida em alguns casos dentro do código (suponho simples aqui), não posso discordar de você ;-). Eu só queria mostrar um jeito com ifelse.
Cath
9

sim, acho que ifelse () é realmente projetado para quando você tem um grande vetor de testes e deseja mapear cada um para uma de duas opções. Por exemplo, costumo fazer cores para plot () desta forma:

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

Se você tivesse um grande e longo vetor de testes, mas desejasse pares de saídas, poderia usar sapply()ou plyr's llply()ou algo assim, talvez.

Brendan OConnor
fonte
4

Às vezes, o usuário só precisa de uma switchdeclaração em vez de um ifelse. Nesse caso:

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

(que é outra opção de sintaxe da resposta de Ken Williams)

agenis
fonte
4

Aqui está uma abordagem semelhante à sugerida por Cath, mas pode funcionar com vetores pré-atribuídos existentes

Baseia-se em usar o seguinte get():

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

use `if`, por exemplo

> `if`(T,1:3,2:4)
[1] 1 2 3
blueskyddd
fonte
Esta é a única resposta aqui que pode realmente fornecer a funcionalidade esperada do ifelse.
sus_mlm
2

No seu caso, usar if_elsede dplyrteria sido útil: if_elseé mais restrito do que ifelsee gera um erro para o seu caso:

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

Encontrado em everydropr :

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

Pode replicar o resultado da sua condição para retornar o comprimento desejado

SJGD
fonte