Does the ternary operator exist in R?

后端 未结 7 1209
日久生厌
日久生厌 2020-12-22 16:52

As the question asks, is there a control sequence in R similar to C\'s ternary operator? If so, how do you use it? Thanks!

相关标签:
7条回答
  • 2020-12-22 17:15

    if works like unvectorised ifelse if used in following manner:

    `if`(condition, doIfTrue, doIfFalse)
    

    The advantage of using this over ifelse is when the vectorisation is in the way (i.e I have scalar boolean and list/vector things as a result)

    ifelse(TRUE, c(1,2), c(3,4))
    [1] 1
    `if`(TRUE, c(1,2), c(3,4))
    [1] 1 2
    
    0 讨论(0)
  • 2020-12-22 17:16

    Like everyone else said, use ifelse, but you can define operators so that you nearly have the ternary operator syntax.

    `%?%` <- 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
    

    It actually works if you define the operators without the % signs, so you could have

    `?` <- 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"
    

    (This works because the precedence of : is lower than ?.)

    Unfortunately, that then breaks the existing help and sequence operators.

    0 讨论(0)
  • 2020-12-22 17:16

    I would take a look at the ifelse command. I would call it even better because it is also vectorized. An example using the cars dataset:

    > 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"
    
    0 讨论(0)
  • 2020-12-22 17:21

    As if is function in R and returns the latest evaluation, if-else is equivalent to ?:.

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

    The power of R is vectorization. The vectorization of the ternary operator is 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
    

    Just kidding, you can define c-style ?::

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

    here, you don't need to take care about brackets:

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

    but you need brackets for assignment :(

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

    Finally, you can do very similar way with 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
      }
    }       
    

    You can get rid of brackets:

    > 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
    

    These are not for daily use, but maybe good for learning some internals of R language.

    0 讨论(0)
  • 2020-12-22 17:21

    Your link points to an if statement.

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

    If your input variable is a vector, then ifelse might be more suitable:

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

    To access the help page for if, you need to embed the if in backticks:

    ?`if`
    

    The help page for ifelse is at:

    `?ifelse`
    
    0 讨论(0)
  • 2020-12-22 17:37

    It doesn't explicitly exist, but you can do:

    set.seed(21)
    y <- 1:10
    z <- rnorm(10)
    
    condition1 <- TRUE
    x1 <- if(condition1) y else z
    

    or

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

    The difference between the two is that condition1 must be a logical vector of length 1, while condition2 must be a logical vector the same length as x, y, and z. The first will return either y or z (the entire object), while the second will return the corresponding element of y (condition2==TRUE) or z (condition2==FALSE).

    Also note that ifelse will be slower than if / else if condition, y, and z are all vectors with length 1.

    0 讨论(0)
提交回复
热议问题