2012-01-09 34 views
122

Como se pregunta, ¿hay una secuencia de control en R similar a C ternary operator? Si es así, cómo lo usas? ¡Gracias!¿Existe el operador ternario en R?

+1

¿Desea algo más poderoso que 'ifelse', o simplemente una forma más compacta? –

+0

@CarlWitthoft En su mayoría, forma más compacta; simplemente una forma de guardar la escritura 'if (x> 1) y = 2 else y = 3'. Escribir 'y =' una vez le tiene cierto atractivo. – eykanal

Respuesta

205

Como es if función en R y devuelve la última evaluación, if-else es equivalente a ?:.

> a <- 1 
> x <- if(a==1) 1 else 2 
> x 
[1] 1 
> x <- if(a==2) 1 else 2 
> x 
[1] 2 

El poder de R es la vectorización. La vectorización del operador ternario es ifelse:

> a <- c(1, 2, 1) 
> x <- ifelse(a==1, 1, 2) 
> x 
[1] 1 2 1 
> x <- ifelse(a==2, 1, 2) 
> x 
[1] 2 1 2 

Es broma, se puede definir c-estilo ?::

`?` <- function(x, y) 
    eval(
      sapply(
        strsplit(
          deparse(substitute(y)),  
          ":" 
     ),  
      function(e) parse(text = e) 
   )[[2 - as.logical(x)]]) 

aquí, usted no necesita tener cuidado sobre soportes:

> 1 ? 2*3 : 4 
[1] 6 
> 0 ? 2*3 : 4 
[1] 4 
> TRUE ? x*2 : 0 
[1] 2 
> FALSE ? x*2 : 0 
[1] 0 

pero se necesitan soportes para la asignación :(

> y <- 1 ? 2*3 : 4 
[1] 6 
> y 
[1] 1 
> y <- (1 ? 2*3 : 4) 
> y 
[1] 6 

Por último, se puede hacer de manera muy similar con c:

`?` <- function(x, y) { 
    xs <- as.list(substitute(x)) 
    if (xs[[1]] == as.name("<-")) x <- eval(xs[[3]]) 
    r <- eval(sapply(strsplit(deparse(substitute(y)), ":"), function(e) parse(text = e))[[2 - as.logical(x)]]) 
    if (xs[[1]] == as.name("<-")) { 
    xs[[3]] <- r 
     eval.parent(as.call(xs)) 
    } else { 
    r 
    } 
}  

Usted puede deshacerse de paréntesis:

> y <- 1 ? 2*3 : 4 
> y 
[1] 6 
> y <- 0 ? 2*3 : 4 
> y 
[1] 4 
> 1 ? 2*3 : 4 
[1] 6 
> 0 ? 2*3 : 4 
[1] 4 

Estos no son para el uso diario, pero tal vez bueno para aprender algunos aspectos internos del lenguaje R.

3

Echaré un vistazo al comando ifelse. Yo lo llamaría aún mejor porque también está vectorizado. Un ejemplo usando el conjunto de datos coches:

> cars$speed > 20 
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 
[13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 
[25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 
[37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE 
[49] TRUE TRUE 

> ifelse(cars$speed > 20, 'fast', 'slow') 
[1] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" 
[11] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" 
[21] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" 
[31] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" 
[41] "slow" "slow" "slow" "fast" "fast" "fast" "fast" "fast" "fast" "fast" 
+4

Hola Paul: ¿quisiste mostrar algo sobre 'ifelse' con tu ejemplo?;) –

+0

No hubo juego de palabras, pero podría ser involuntario;) –

3

Sus enlace apunta a una declaración if.

> x <- 1 
> if(x < 2) print("Less than") else print("Greater than") 
[1] "Less than" 

Si la variable de entrada es un vector, entonces ifelse podría ser más adecuado:

> x <- 1:3 
> ifelse(x<=2, "Less than or equal", "Greater than") 
[1] "Less than or equal" "Less than or equal" "Greater than" 

Para acceder a la página de ayuda para if, necesita integrar la if entre comillas sencillas:

?`if` 

La página de ayuda para ifelse es a t:

`?ifelse` 
+1

Como dijo @kohske, esto también funcionará: 'imprimir (if (x <2)" Menos que "else" Mayor que) ' –

3

No existe explícitamente, pero se puede hacer:

set.seed(21) 
y <- 1:10 
z <- rnorm(10) 

condition1 <- TRUE 
x1 <- if(condition1) y else z 

o

condition2 <- sample(c(TRUE,FALSE),10,TRUE) 
x2 <- ifelse(condition2, y, z) 

La diferencia entre los dos es que condition1 debe ser un vector lógico de longitud 1, mientras que condition2 debe ser un vector lógico de la misma longitud que x, y y z. El primero devolverá y o z (el objeto completo), mientras que el segundo devolverá el elemento correspondiente de y (condition2==TRUE) o (condition2==FALSE).

También tenga en cuenta que ifelse será más lenta que if/else si condition, y y z son todos los vectores de longitud 1.

+0

gracias Joshua , tu respuesta me ayudó mucho, encontré una respuesta de la publicación que mencionaste http://stackoverflow.com/a/8792474/3019570 –

16

Como todo el mundo dice, el uso ifelse, pero se puede definir operadores para que casi tener la sintaxis del operador ternario.

`%?%` <- function(x, y) list(x = x, y = y) 
`%:%` <- function(xy, z) if(xy$x) xy$y else z 

TRUE %?% rnorm(5) %:% month.abb 
## [1] 0.05363141 -0.42434567 -0.20000319 1.31049766 -0.31761248 
FALSE %?% rnorm(5) %:% month.abb 
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" 
# or, more generally 
condition %?% value1 %:% value2 

realmente funciona si define los operadores sin los % signos, por lo que podría tener

`?` <- function(x, y) if(x) y[[1]] else y[[2]] 
`:` <- function(y, z) list(y, z) 

TRUE ? rnorm(5) : month.abb 
## [1] 1.4584104143 0.0007500051 -0.7629123322 0.2433415442 0.0052823403 
FALSE ? rnorm(5) : month.abb 
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" 

(Esto funciona porque la precedencia de : es menor que ?.)

Desafortunadamente , que luego rompe la ayuda existente y los operadores de secuencia.

3

Así como una broma, que puede redefinir el operador ? a (casi) trabajar como el operador ternario (esto es una MALA IDEA):

`?` <- function(x, y) { y <-substitute(y); if(x) eval(y[[2]], parent.frame()) else eval(y[[3]], parent.frame()) } 

x <- 1:3 
length(x) ? (x*2) : 0 
x <- numeric(0) 
length(x) ? (x*2) : 0 

for(i in 1:5) cat(i, (i %% 2) ? "Odd\n" : "Even\n") 

... Pero hay que poner el expresiones entre paréntesis, porque la prioridad por defecto no es como en C.

Sólo recuerde a restaurar la vieja función de ayuda cuando haya terminado de jugar:

rm(`?`) 
0

if obras como ifelse unvectorised si se utiliza de manera siguiente:

`if`(condition, doIfTrue, doIfFalse) 

La ventaja de usar este sobre ifelse es cuando la vectorización es en la forma (es decir, tengo boolean escalar y la lista/cosas vector como un resultado)

ifelse(TRUE, c(1,2), c(3,4)) 
[1] 1 
`if`(TRUE, c(1,2), c(3,4)) 
[1] 1 2