Liệu toán tử ternary tồn tại trong R?


175

Như câu hỏi đặt ra, có một chuỗi điều khiển trong R tương tự như toán tử ternary của C không? Nếu vậy, làm thế nào để bạn sử dụng nó? Cảm ơn!


1
Bạn có muốn một cái gì đó mạnh mẽ hơn ifelse, hoặc chỉ là một hình thức nhỏ gọn hơn?
Carl Witthoft

@CarlWitthoft Hình thức nhỏ gọn hơn; chỉ đơn giản là một cách để tiết kiệm bằng văn bản if (x>1) y=2 else y=3. Viết y=một lần có một sức hấp dẫn nhất định với nó.
Eykanal

Câu trả lời:


302

ifchức năng trong Rvà trả về đánh giá mới nhất, if-other tương đương với ?:.

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

Sức mạnh của R là vector hóa. Các vector hóa của toán tử ternary là 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

Đùa thôi, bạn có thể định nghĩa kiểu c ?::

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

Ở đây, bạn không cần quan tâm đến dấu ngoặc:

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

nhưng bạn cần dấu ngoặc để gán :(

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

Cuối cùng, bạn có thể làm cách tương tự với 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
  }
}       

Bạn có thể thoát khỏi dấu ngoặc:

> 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

Đây không phải là để sử dụng hàng ngày, nhưng có thể tốt cho việc học một số nội bộ của ngôn ngữ R.


23

Giống như mọi người khác đã nói, hãy sử dụng ifelse, nhưng bạn có thể xác định các toán tử để bạn gần như có cú pháp toán tử ternary.

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

Nó thực sự hoạt động nếu bạn xác định các toán tử mà không có %dấu hiệu, vì vậy bạn có thể có

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

(Điều này hoạt động vì mức độ ưu tiên :thấp hơn ?.)

Thật không may, sau đó phá vỡ các toán tử trợ giúp và trình tự hiện có.


5

Cũng giống như một trò chơi khăm, bạn có thể xác định lại ?toán tử để (gần như) hoạt động giống như toán tử ternary (ĐÂY LÀ MỘT BAD 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")

... Nhưng bạn cần đặt các biểu thức trong ngoặc đơn vì quyền ưu tiên mặc định không giống như trong C.

Chỉ cần nhớ khôi phục chức năng trợ giúp cũ khi bạn chơi xong:

rm(`?`)

5

Tôi sẽ xem ifelselệnh. Tôi sẽ gọi nó thậm chí còn tốt hơn bởi vì nó cũng được vector hóa. Một ví dụ sử dụng bộ dữ liệu xe hơi:

> 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
Xin chào Paul - ý của bạn là thể hiện điều gì đó ifelsevới ví dụ của bạn? ;)
Josh O'Brien

4

Liên kết của bạn trỏ đến một iftuyên bố.

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

Nếu biến đầu vào của bạn là một vectơ, thì ifelsecó thể phù hợp hơn:

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

Để truy cập trang trợ giúp if, bạn cần nhúng các ifbackticks:

?`if`

Trang trợ giúp dành cho ifelse:

`?ifelse`

1
Như @kohske đã nói, điều này cũng sẽ hoạt động:print(if (x<2) "Less than" else "Greater than")
Ben Bolker

4

Nó không tồn tại rõ ràng, nhưng bạn có thể làm:

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

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

hoặc là

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

Sự khác biệt giữa hai là condition1phải là một vector logic có độ dài 1, trong khi condition2phải là một vector logic cùng độ dài như x, y, và z. Đầu tiên sẽ trở lại một trong hai yhoặc z(toàn bộ đối tượng), trong khi thứ hai sẽ trở lại các yếu tố tương ứng của y( condition2==TRUE) hoặc z( condition2==FALSE).

Cũng lưu ý rằng ifelsesẽ chậm hơn so if/ elsenếu condition, yzđều là những vectơ với chiều dài 1.


cảm ơn Joshua, câu trả lời của bạn đã giúp ích rất nhiều, tôi đã tìm thấy câu trả lời từ bài đăng mà bạn đề cập đến stackoverflow.com/a/8792474/3019570
Mahdi Jadaliha

2

if hoạt động như ifelse unvectorised nếu được sử dụng theo cách sau:

`if`(condition, doIfTrue, doIfFalse)

Ưu điểm của việc sử dụng này so với ifelse là khi kết quả vector hóa (nghĩa là tôi có kết quả boolean vô hướng và liệt kê / vectơ)

ifelse(TRUE, c(1,2), c(3,4))
[1] 1
`if`(TRUE, c(1,2), c(3,4))
[1] 1 2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.