2011-11-18 13 views
9

(información de fondo: ifelse evalúa tanto de las expresiones, a pesar de que sólo una será devuelto EDIT:. Esta es una declaración incorrecta Véase la respuesta de Tommy.)es siempre apropiado en una situación no vectorizada y viceversa?

¿Hay algún ejemplo donde tiene sentido utilizar ifelse en una situación no vectorizada? Creo que "legibilidad" podría ser una respuesta válida cuando no se preocupan por las ganancias de eficiencia pequeñas, pero aparte de eso, es que cada vez más rápido/equivalente/mejor-en-alguna-otra-manera de utilizar ifelse cuando un if y luego else haría el trabajo?

Del mismo modo, si tengo una situación vectorizada, es ifelse siempre la mejor herramienta a utilizar? Parece extraño que ambas expresiones sean evaluadas. Es cada vez más rápido para recorrer uno por uno y hacer una normal de if y luego else? Supongo que tendría sentido solo si la evaluación de las expresiones tomó mucho tiempo. ¿Hay alguna otra alternativa que no implique un ciclo explícito?

Gracias

Respuesta

14

En primer lugar, hace ifelseNO siempre evalúan ambas expresiones - sólo si hay dos TRUE y FALSE elementos en el vector de prueba.

ifelse(TRUE, 'foo', stop('bar')) # "foo" 

Y en mi opinión:

ifelse debe no usarse en una situación de no vectorizado. Es siempre más lenta y más propenso a errores utilizar ifelse sobre if/else:

# This is fairly common if/else code 
if (length(letters) > 0) letters else LETTERS 

# But this "equivalent" code will yield a very different result - TRY IT! 
ifelse(length(letters) > 0, letters, LETTERS) 

En situaciones vectorizados embargo, ifelse puede ser una buena opción - pero ten en cuenta que la longitud y los atributos del resultado podría no sea lo que esperas (como arriba, y considero que ifelse está roto en ese sentido).

Aquí está un ejemplo: tst es de longitud 5 y tiene una clase. Esperaría que el resultado fuera de longitud 10 y no tuviera clase, pero eso no es lo que sucede: ¡tiene una clase incompatible y longitud 5!

# a logical vector of class 'mybool' 
tst <- structure(1:5 %%2 > 0, class='mybool') 

# produces a numeric vector of class 'mybool'! 
ifelse(tst, 101:110, 201:210) 
#[1] 101 202 103 204 105 
#attr(,"class") 
#[1] "mybool" 

¿Por qué esperar que la longitud sea 10? Debido a que la mayoría de las funciones de "ciclo" R el vector más corto para que coincida con los más largos:

1:5 + 1:10 # returns a vector of length 10. 

... Pero sólo ifelse ciclos de los sí/no hay argumentos para que coincida con la longitud del argumento TST.

¿Por qué esperaría que se copiara la clase (y otros atributos) a no del objeto de prueba? Porque < que devuelve un vector lógico no copia la clase y los atributos de sus argumentos (típicamente numéricos). No hace eso porque normalmente estaría muy mal.

1:5 < structure(1:10, class='mynum') # returns a logical vector without class 

Por último, ¿puede ser más eficiente "hacerlo usted mismo"?Bueno, parece que ifelse no es una primitiva como if, y necesita algún código especial para manejar NA. Si no tiene NA s, puede ser más rápido hacerlo usted mismo.

tst <- 1:1e7 %%2 == 0 
a <- rep(1, 1e7) 
b <- rep(2, 1e7) 
system.time(r1 <- ifelse(tst, a, b))   # 2.58 sec 

# If we know that a and b are of the same length as tst, and that 
# tst doesn't have NAs, then we can do like this: 
system.time({ r2 <- b; r2[tst] <- a[tst]; r2 }) # 0.46 secs 

identical(r1, r2) # TRUE 
+0

gracias! No tengo tiempo para analizar esto ahora, pero lo haré más tarde esta noche. Gracias por sus comentarios y ejemplos. –

+0

excelentes consejos y ejemplos. ¡Gracias! –

+1

Tenga en cuenta que 'ifelse' sí realiza el reciclaje de vectores: si cualquiera de las variables' yes' o 'no' tienen una longitud desigual a' test', se reciclarán. Esto significa que su código de prueba al final de su ejemplo solo arrojará resultados idénticos si todos los vectores tienen la misma longitud. Pruebe, por ejemplo, estos datos de entrada: 'tst <- sample (c (TRUE, FALSE), 1e2, replace = TRUE); a <- 1: 100; b <- - (1:50); ' – Andrie

4

En su segundo punto, ¿cómo define "mejor"? Creo que ifelse() es una de las soluciones más legibles, pero puede no ser siempre la más rápida. Específicamente, descubrí que escribir condiciones booleanas y sumarlas puede brindarle algunos beneficios de rendimiento. Aquí hay un ejemplo rápido:

> x <- rnorm(1e6) 
> system.time(y1 <- ifelse(x > 0,1,2)) 
    user system elapsed 
    0.46 0.08 0.53 
> system.time(y2 <- (x > 0) * 1 + (x <= 0) * 2) 
    user system elapsed 
    0.06 0.00 0.06 
> identical(y1, y2) 
[1] TRUE 

Por lo tanto, si la velocidad es su mayor preocupación, el enfoque booleano puede ser mejor. Sin embargo, para la mayoría de mis propósitos, he encontrado ifelse() lo suficientemente rápido y es fácil de asimilar. Sus millas pueden variar obviamente.

+0

Reconozco vergonzosamente que escribí la versión booleana no solo porque es rápida sino porque es 31337. Pero, en serio, una vez que te acostumbras, el código es tan fácil de leer y entender como el código usando if. –

+0

@Chase Puntos buenos –

+0

Es incluso más rápido utilizar 'y3 <- (x <= 0) + 1' ya que no incluye una prueba redundante. –

Cuestiones relacionadas