¿Por qué las declaraciones ifelse de R no pueden devolver vectores?

118

He encontrado que las declaraciones ifelse de R son bastante útiles de vez en cuando. Por ejemplo:

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

Pero estoy algo confundido por el siguiente comportamiento.

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

¿Es esta una opción de diseño que está por encima de mi pago?

Christopher DuBois
fuente
1
Un pequeño diseño extraño para ifelse dado el hecho de que simple if else funciona.
2sb
4
ifelse es una función vectorizada. Deben usarse para diferentes tareas.
marbel

Respuestas:

99

La documentación de los ifelseestados:

ifelsedevuelve un valor con la misma forma que el testque se rellena con elementos seleccionados de yeso nodependiendo de si el elemento de testes TRUEo FALSE.

Dado que pasa valores de prueba de longitud 1, obtiene resultados de longitud 1. Si pasa vectores de prueba más largos, obtendrá resultados más largos:

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

Así que ifelseestá destinado al propósito específico de probar un vector de valores booleanos y devolver un vector de la misma longitud, lleno de elementos tomados del (vector) yesy noargumentos.

Es una confusión común, debido al nombre de la función, usar esto cuando realmente desea una if () {} else {}construcción normal .

Cocina Nathan
fuente
16
Quizás lo que realmente deseaba para el segundo conjunto de declaraciones era if (TRUE) c(1,2) else c(3,4).
Jonathan Chang
69

Apuesto a que desea una ifdeclaración simple en lugar de ifelse: en R, ifno es solo una estructura de flujo de control, puede devolver un 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
fuente
@Ken, esto funciona para mí, aunque obtengo lo que necesito, una advertencia constante, " Warning in if (req(inputval) == "All") { : the condition has length > 1 and only the first element will be used"¿qué debo hacer para deshacerme de esta advertencia?
user5249203
1
@ user5249203, la pregunta y la respuesta de Ken se refieren al caso donde la condición es un valor único, es decir, un vector de longitud 1. La advertencia indica que req(inputval)tiene más elementos. Para obtener un solo valor las funciones any()o all()podrían ser útiles.
Uwe
12

Tenga en cuenta que puede evitar el problema si asigna el 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
fuente
3
En mi humilde opinión, es alentador hacer un mal uso de la ifelse()función vectorizada en lugar de un flujo de control if ... else ...para la asignación. Si la condición es única TRUEo FALSEvalor, preferiría escribir a <- if (TRUE) c(1,2) else c(3,4)oif (TRUE) a <- c(1,2) else a <- c(3,4)
Uwe
1
@Uwe, aunque no creo que la diferencia en el rendimiento cuando se usa en ifelselugar de if... elseen caso de una sola condición realmente puede ser un problema y ifelsepuede ser preferible en algunos casos dentro del código (una simple suposición aquí), no puedo estar en desacuerdo con usted ;-). Solo quería mostrar un camino con ifelse.
Cath
9

sí, creo que ifelse () está realmente diseñado para cuando tienes un gran vector de pruebas y quieres mapear cada una a una de dos opciones. Por ejemplo, a menudo hago colores para plot () de esta manera:

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

Si usted tenía una larga grande del vector de pruebas, pero quería pares de salidas, se puede usar sapply()o plyr's llply()o algo, tal vez.

Brendan O'Connor
fuente
4

A veces, el usuario solo necesita una switchdeclaración en lugar de un ifelse. En ese caso:

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

(que es otra opción de sintaxis de la respuesta de Ken Williams)

agenis
fuente
4

Aquí hay un enfoque similar al sugerido por Cath, pero puede funcionar con vectores preasignados existentes

Se basa en el uso de me get()gusta así:

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

use `si`, por ejemplo

> `if`(T,1:3,2:4)
[1] 1 2 3
blueskyddd
fuente
Esta es la única respuesta aquí que realmente puede proporcionar la funcionalidad esperada de ifelse.
sus_mlm
2

En su caso, usar if_elsefrom dplyrhabría sido útil: if_elsees más estricto que ifelse, y arroja un error para su caso:

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

Encontrado en everydropr :

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

Puede replicar el resultado de su condición para devolver la longitud deseada.

SJGD
fuente