2012-01-09 9 views
122

Come domanda, c'è una sequenza di controllo in R simile a C ternary operator? Se sì, come lo usi? Grazie!L'operatore ternario esiste in R?

+1

Vuoi qualcosa di più potente di 'ifelse', o solo una forma più compatta? –

+0

@CarlWitthoft Forma molto più compatta; semplicemente un modo per salvare scrivere 'if (x> 1) y = 2 else y = 3'. Scrivere 'y =' una volta ha un certo fascino. – eykanal

risposta

205

Come if è la funzione in R e restituisce l'ultima valutazione, if-else è equivalente a ?:.

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

La potenza di R è la vettorizzazione. La vettorializzazione del 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, è possibile definire c-style ?::

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

qui, non c'è bisogno di prendere a cuore le staffe:

> 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 è necessario staffe per l'assegnazione :(

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

Infine, si può fare 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 
    } 
}  

Si può sbarazzarsi di staffe:

> 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 buono per l'apprendimento di alcuni interni del linguaggio R.

3

Vorrei dare un'occhiata al comando ifelse. Lo chiamerei ancora meglio perché è anche vettorializzato. Un esempio utilizzando il set di dati auto:

> 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 su 'ifelse' con il tuo esempio?;) –

+0

Non era previsto il gioco di parole, ma potrebbe essere non intenzionale;) –

3

tuo link punta a un if dichiarazione.

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

Se la variabile di ingresso è un vettore, allora ifelse potrebbe 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 il if in backticks:

?`if` 

La pagina di aiuto di ifelse è un t:

`?ifelse` 
+1

Come ha detto @kohske, questo funzionerà anche: 'stampa (se (x <2)" Meno di "altro" Maggiore di ")' –

3

Non esiste in modo esplicito, ma si può 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 condition1 deve essere un vettore logica di lunghezza 1, mentre condition2 deve essere un vettore logico della stessa lunghezza di x, e z. Il primo restituirà y o z (l'intero oggetto), mentre il secondo restituirà l'elemento corrispondente di (condition2==TRUE) o z (condition2==FALSE).

Si noti inoltre che ifelse sarà più lento rispetto if/else se condition, y, e z sono tutti i vettori di lunghezza 1.

+0

grazie Joshua , la tua risposta mi ha aiutato molto, ho trovato risposta dal post che hai citato http://stackoverflow.com/a/8792474/3019570 –

16

Come tutti gli altri, ha detto, l'uso ifelse, ma è possibile definire gli operatori in modo che è quasi avere 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 

effettivamente funziona se si definisce gli operatori senza i % segni, così 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 ?.)

Purtroppo , che quindi interrompe gli operatori di aiuto e sequenza esistenti.

3

Proprio come uno scherzo, si può ridefinire il ? all'operatore di (quasi) funzionare 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 è necessario mettere il espressioni tra parentesi, perché la precedenza di default non è come in C.

Basta ricordarsi di ripristinare la funzione di aiuto vecchio quando hai finito di giocare:

rm(`?`) 
0

if opere come IfElse unvectorised se utilizzati in modo seguente:

`if`(condition, doIfTrue, doIfFalse) 

Il vantaggio di utilizzare questo più IfElse è quando il vettorizzazione è nel modo (cioè ho booleano scalare e cose vettoriali come risultato l'elenco /)

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