2012-01-09 39 views
122

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

+1

Bạn có muốn thứ gì đó mạnh hơn 'ifelse' hoặc chỉ là một hình thức nhỏ gọn hơn không? –

+0

@CarlWitthoft Chủ yếu là hình thức nhỏ gọn hơn; chỉ đơn giản là một cách để lưu văn bản 'if (x> 1) y = 2 else y = 3'. Viết 'y =' một lần có sức hấp dẫn nhất định. – eykanal

Trả lời

205

Như if là chức năng trong R và trả về đánh giá mới nhất, if-else 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. Các vector của các nhà điều hành 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 

Just kidding, bạn có thể xác định c kiểu ?::

`?` <- 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 phải chăm sóc về 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 để chuyển nhượng :(

> 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 theo cách rất giống 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ó lẽ tốt cho việc học một số nội ngữ của ngôn ngữ R.

3

Tôi sẽ xem xét lệnh ifelse. Tôi sẽ gọi nó tốt hơn vì nó cũng được vector hóa. Ví dụ sử dụng tập dữ liệu ô tô:

> 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 - bạn có muốn nói điều gì đó về 'ifelse' với ví dụ của bạn không?;) –

+0

Không có ý định chơi chữ, nhưng nó có thể không chủ ý;) –

3

Liên kết của bạn trỏ đến tuyên bố if.

> 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 vector, sau đó ifelse có 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 vào trang trợ giúp cho if, bạn cần phải nhúng các if trong backticks:

?`if` 

Trang trợ giúp cho ifelse là một t:

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

3

Nó không rõ ràng tồn tại, 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

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

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

Cũng lưu ý rằng ifelse sẽ chậm hơn so if/else nếu condition, y, và z đều vectơ với chiều dài 1.

+0

thanks 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 bạn đã đề cập http://stackoverflow.com/a/8792474/3019570 –

16

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

`%?%` <- 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 nhà khai thác mà không có sự % 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" 

(này làm việc vì ưu tiên của : thấp hơn ?.)

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

3

Cũng giống như một trò đùa, bạn có thể xác định lại ? vận hành (hầu như) làm việc như các nhà điều hành ternary (THIS IS A IDEA BAD):

`?` <- 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 phải đặt biểu thức trong ngoặc đơn bởi vì ưu tiên mặc định không giống như trong C.

Chỉ cần nhớ để khôi phục lại chức năng giúp đỡ cũ khi bạn đã hoàn tất chơi:

rm(`?`) 
0

if công trình 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 trên ifelse là khi vectorisation là trong cách (ví dụ: Tôi có boolean vô hướng và danh sách/vector điều như vậy)

ifelse(TRUE, c(1,2), c(3,4)) 
[1] 1 
`if`(TRUE, c(1,2), c(3,4)) 
[1] 1 2 
Các vấn đề liên quan