L'operatore ternario esiste in R?


175

Come si pone la domanda, esiste una sequenza di controllo in R simile all'operatore ternario di C ? In tal caso, come lo usi? Grazie!


1
Vuoi qualcosa di più potente di ifelse, o semplicemente una forma più compatta?
Carl Witthoft,

@CarlWitthoft Forma per lo più compatta; semplicemente un modo per salvare la scrittura if (x>1) y=2 else y=3. Scrivere y=una volta ha un certo fascino.
eykanal,

Risposte:


302

Poiché la iffunzione è attiva Re restituisce la valutazione più recente, if-else equivale a ?:.

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

Il potere di R è la vettorializzazione. La vettorializzazione dell'operatore ternario è 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

Sto scherzando, puoi definire lo stile c ?::

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

qui, non è necessario preoccuparsi delle parentesi:

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

ma hai bisogno di parentesi per il compito :(

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

Infine, puoi fare un modo molto simile 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
  }
}       

Puoi sbarazzarti delle parentesi:

> 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

Questi non sono per l'uso quotidiano, ma forse sono utili per l'apprendimento di alcuni interni della lingua R.


23

Come tutti gli altri hanno detto, usa ifelse, ma puoi definire gli operatori in modo da avere quasi la sintassi dell'operatore 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

In realtà funziona se si definiscono gli operatori senza i %segni, quindi si potrebbe avere

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

(Questo funziona perché la precedenza di :è inferiore a ?.)

Sfortunatamente, ciò interrompe quindi gli operatori di aiuto e sequenza esistenti.


5

Proprio come uno scherzo, puoi ridefinire l' ?operatore affinché (quasi) lavori come l'operatore ternario (QUESTA È UNA CATTIVA 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")

... Ma devi mettere le espressioni tra parentesi perché la precedenza predefinita non è come in C.

Ricorda solo di ripristinare la vecchia funzione di aiuto quando hai finito di giocare:

rm(`?`)

5

Vorrei dare un'occhiata al ifelsecomando. Lo definirei ancora migliore perché è anche vettorializzato. Un esempio usando il set di dati delle automobili:

> 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
Ciao Paul, intendevi mostrare qualcosa ifelsecon il tuo esempio? ;)
Josh O'Brien il

4

Il tuo link punta a una ifdichiarazione.

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

Se la tua variabile di input è un vettore, ifelsepotrebbe essere più adatto:

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

Per accedere alla pagina di aiuto per if, è necessario incorporare i ifbacktick:

?`if`

La pagina di aiuto per ifelseè a:

`?ifelse`

1
Come ha detto @kohske, anche questo funzionerà:print(if (x<2) "Less than" else "Greater than")
Ben Bolker il

4

Non esiste esplicitamente, ma puoi fare:

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 differenza tra i due è che condition1deve essere un vettore logico di lunghezza 1, mentre condition2deve essere un vettore logico della stessa lunghezza x, ye z. Il primo restituirà uno yo z(l'intero oggetto), mentre il secondo restituirà l'elemento corrispondente di y( condition2==TRUE) o z( condition2==FALSE).

Inoltre nota che ifelsesarà più lento di if/ elsese condition, ye zsono tutti vettori con lunghezza 1.


grazie Joshua, la risposta ha aiutato molto, ho trovato risposta da post che menzionato stackoverflow.com/a/8792474/3019570
Mahdi Jadaliha

2

if funziona come ifelse non vettoriale se usato nel modo seguente:

`if`(condition, doIfTrue, doIfFalse)

Il vantaggio di usare questo su ifelse è quando la vettorializzazione è in mezzo (cioè ho un booleano scalare e di conseguenza elenco / cose vettoriali)

ifelse(TRUE, c(1,2), c(3,4))
[1] 1
`if`(TRUE, c(1,2), c(3,4))
[1] 1 2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.