Czy operator trójskładnikowy istnieje w R?


175

Jak pojawia się pytanie, czy istnieje sekwencja kontrolna w R podobna do operatora trójskładnikowego C ? Jeśli tak, jak z niego korzystasz? Dzięki!


1
Czy chcesz czegoś mocniejszego niż ifelse, czy po prostu bardziej kompaktowej formy?
Carl Witthoft

@CarlWitthoft Przeważnie bardziej zwarta forma; po prostu sposób na oszczędzenie pisania if (x>1) y=2 else y=3. Pisanie y=raz ma do tego pewien urok.
eykanal

Odpowiedzi:


302

Tak jak iffunkcja w Ri zwraca ostatnią ocenę, jeśli-else jest równoważne ?:.

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

Moc R to wektoryzacja. Wektoryzacja operatora trójskładnikowego to 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

Żartuję, możesz zdefiniować styl c ?::

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

tutaj nie musisz dbać o nawiasy:

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

ale potrzebujesz nawiasów do przypisania :(

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

Wreszcie możesz zrobić bardzo podobny sposób z 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
  }
}       

Możesz pozbyć się nawiasów:

> 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

Nie są one do codziennego użytku, ale mogą być przydatne do nauki niektórych wewnętrznych elementów języka R.


23

Jak wszyscy mówili, użyj ifelse, ale możesz zdefiniować operatory, aby uzyskać prawie trójskładnikową składnię operatorów.

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

W rzeczywistości działa, jeśli zdefiniujesz operatory bez %znaków, więc możesz to zrobić

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

(Działa to, ponieważ pierwszeństwo :jest mniejsze niż ?).

Niestety, to zrywa istniejącą pomoc i operatory sekwencji.


5

Tylko jako żart, to może przedefiniować ?operatorowi (prawie) jak praca operatora potrójnego (jest to zły pomysł):

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

... Ale musisz umieścić wyrażenia w nawiasach, ponieważ domyślny priorytet nie jest taki jak w C.

Pamiętaj tylko, aby przywrócić starą funkcję pomocy, gdy skończysz grać:

rm(`?`)

5

Rzuciłem okiem na ifelsepolecenie. Nazwałbym to jeszcze lepiej, ponieważ jest również zwektoryzowany. Przykład wykorzystujący zbiór danych samochodów:

> 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
Cześć Paul - czy chciałeś coś pokazać ifelseswoim przykładem? ;)
Josh O'Brien

4

Twój link wskazuje na ifoświadczenie.

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

Jeśli zmienną wejściową jest wektor, ifelsemoże być bardziej odpowiednie:

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

Aby uzyskać dostęp do strony pomocy dla if, musisz osadzić iflewe znaczniki:

?`if`

Strona pomocy dla ifelse:

`?ifelse`

1
Jak powiedział @kohske, to też zadziała:print(if (x<2) "Less than" else "Greater than")
Ben Bolker

4

Nie istnieje, ale możesz to zrobić:

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

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

lub

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

Różnica pomiędzy nimi jest to, że condition1musi być logicznie wektor o długości 1, a condition2musi być logicznie wektor tej samej długości x, yoraz z. Pierwsza zwróci albo ylub z(cały obiekt), podczas gdy druga zwróci odpowiedni element y( condition2==TRUE) lub z( condition2==FALSE).

Należy także zwrócić uwagę, że ifelsejest dłuższy niż if/ elsejeśli condition, yi zsą wektorami o długości 1.


dzięki Joshua, twoja odpowiedź bardzo pomogła, znalazłem odpowiedź z posta, o którym wspomniałeś stackoverflow.com/a/8792474/3019570
Mahdi Jadaliha

2

if działa jak niewektorowany ifelse, jeśli jest używany w następujący sposób:

`if`(condition, doIfTrue, doIfFalse)

Zaletą korzystania z tego w porównaniu z ifelse jest sytuacja, w której wektoryzacja jest przeszkodą (tj. Mam w rezultacie skalarną wartość logiczną i listę / wektor)

ifelse(TRUE, c(1,2), c(3,4))
[1] 1
`if`(TRUE, c(1,2), c(3,4))
[1] 1 2
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.