2011-11-18 34 views
9

(info Bối cảnh: ifelse đánh giá cả của biểu thức, mặc dù chỉ có một sẽ được trả lại EDIT:. Đây là một tuyên bố không chính xác Xem bài trả lời của Tommy.)là ifelse bao giờ thích hợp trong một tình huống không vectorized và ngược lại?

Có bất kỳ ví dụ nơi nó làm cho cảm giác sử dụng ifelse trong một tình huống không được vector hóa? Tôi nghĩ rằng "khả năng đọc" có thể là câu trả lời hợp lệ khi chúng tôi không quan tâm đến lợi ích hiệu quả nhỏ, nhưng bên cạnh đó, nó nhanh hơn/tương đương/tốt hơn một cách khác để sử dụng ifelse khi một if và sau đó else sẽ thực hiện công việc?

Tương tự, nếu tôi có tình huống vectơ, thì ifelse có phải là công cụ tốt nhất để sử dụng không? Có vẻ kỳ lạ là cả hai biểu thức đều được đánh giá. Có bao giờ nhanh hơn để lặp lại từng cái một và làm một số if bình thường và sau đó else? Tôi đoán nó sẽ có ý nghĩa chỉ khi đánh giá các biểu thức mất một thời gian rất dài. Có bất kỳ lựa chọn thay thế nào khác không liên quan đến vòng lặp rõ ràng không?

Cảm ơn

Trả lời

14

Thứ nhất, ifelse không KHÔNG luôn đánh giá cả hai biểu thức - chỉ khi có cả TRUEFALSE yếu tố trong vector thử nghiệm.

ifelse(TRUE, 'foo', stop('bar')) # "foo" 

Và theo ý kiến ​​của tôi:

ifelse nên không được sử dụng trong một tình huống không vectorized. Đó là luôn chậm hơn và nhiều hơn nữa lỗi dễ bị sử dụng ifelse qua if/else:

# This is fairly common if/else code 
if (length(letters) > 0) letters else LETTERS 

# But this "equivalent" code will yield a very different result - TRY IT! 
ifelse(length(letters) > 0, letters, LETTERS) 

Trong những tình huống vectorized tuy nhiên, ifelse có thể là một lựa chọn tốt - nhưng hãy cẩn thận rằng độ dài và thuộc tính của kết quả có thể không phải là những gì bạn mong đợi (như trên, và tôi xem xét ifelse bị phá vỡ trong đó tôn trọng).

Dưới đây là ví dụ: tst có chiều dài 5 và có một lớp. Tôi mong đợi kết quả là chiều dài 10 và không có lớp, nhưng đó không phải là những gì xảy ra - nó nhận được một lớp không tương thích và chiều dài 5!

# a logical vector of class 'mybool' 
tst <- structure(1:5 %%2 > 0, class='mybool') 

# produces a numeric vector of class 'mybool'! 
ifelse(tst, 101:110, 201:210) 
#[1] 101 202 103 204 105 
#attr(,"class") 
#[1] "mybool" 

Tại sao tôi mong đợi độ dài là 10? Bởi vì hầu hết các chức năng trong R "chu kỳ" vector ngắn hơn để phù hợp với còn:

1:5 + 1:10 # returns a vector of length 10. 

... Nhưng ifelse chỉ chu kỳ sự có/không có đối số để phù hợp với chiều dài của đối số tst.

Tại sao tôi mong đợi lớp học (và các thuộc tính khác) để không phải là được sao chép từ đối tượng thử nghiệm? Bởi vì < trả về một vectơ logic không sao chép lớp và các thuộc tính từ các đối số (thường là số) của nó. Nó không làm điều đó bởi vì nó thường sẽ rất sai.

1:5 < structure(1:10, class='mynum') # returns a logical vector without class 

Cuối cùng, bạn có thể tự làm "hiệu quả hơn" không?Vâng, có vẻ như là ifelse không phải là nguyên thủy như if và cần một số mã đặc biệt để xử lý NA. Nếu bạn không có NA s, bạn có thể tự làm điều đó nhanh hơn.

tst <- 1:1e7 %%2 == 0 
a <- rep(1, 1e7) 
b <- rep(2, 1e7) 
system.time(r1 <- ifelse(tst, a, b))   # 2.58 sec 

# If we know that a and b are of the same length as tst, and that 
# tst doesn't have NAs, then we can do like this: 
system.time({ r2 <- b; r2[tst] <- a[tst]; r2 }) # 0.46 secs 

identical(r1, r2) # TRUE 
+0

cảm ơn bạn! Tôi không có thời gian để nhìn cái này bây giờ, nhưng tối nay tôi sẽ làm thế. Cảm ơn các ý kiến ​​và ví dụ của bạn. –

+0

lời khuyên và ví dụ tuyệt vời. Cảm ơn! –

+1

Lưu ý rằng 'ifelse' thực hiện tái chế vectơ - nếu một trong hai biến' yes' hoặc 'no' có độ dài không bằng nhau đối với' test', chúng sẽ được tái chế. Điều này có nghĩa là mã thử nghiệm của bạn ở cuối ví dụ của bạn sẽ chỉ mang lại kết quả giống nhau nếu tất cả các vector có độ dài bằng nhau. Hãy thử ví dụ dữ liệu đầu vào này: 'tst <- mẫu (c (TRUE, FALSE), 1e2, thay thế = TRUE); một <- 1: 100; b <- - (1:50); ' – Andrie

4

Trên điểm thứ hai, bạn định nghĩa "tốt nhất" như thế nào? Tôi nghĩ rằng ifelse() là một trong những giải pháp dễ đọc hơn, nhưng có thể không phải lúc nào cũng nhanh nhất. Cụ thể, tôi thấy rằng việc viết ra các điều kiện boolean và thêm chúng lại với nhau có thể cung cấp cho bạn một số lợi ích hiệu suất. Dưới đây là ví dụ nhanh:

> x <- rnorm(1e6) 
> system.time(y1 <- ifelse(x > 0,1,2)) 
    user system elapsed 
    0.46 0.08 0.53 
> system.time(y2 <- (x > 0) * 1 + (x <= 0) * 2) 
    user system elapsed 
    0.06 0.00 0.06 
> identical(y1, y2) 
[1] TRUE 

Vì vậy, nếu tốc độ là mối quan tâm lớn nhất của bạn, cách tiếp cận boolean có thể tốt hơn. Tuy nhiên, đối với hầu hết các mục đích của tôi - tôi đã tìm thấy ifelse() đủ nhanh và dễ bị lúng túng. dặm có thể thay đổi rõ ràng.

+0

Tôi sẽ đáng xấu hổ thừa nhận để viết phiên bản boolean không chỉ vì nó nhanh nhưng vì nó là 31337. Nhưng, nghiêm túc, một khi bạn đã quen với nó, mã chỉ là dễ đọc và hiểu như mã sử dụng nếu. –

+0

@Chase Điểm tốt –

+0

Thậm chí còn nhanh hơn khi sử dụng 'y3 <- (x <= 0) + 1' vì nó không bao gồm thử nghiệm dự phòng. –

Các vấn đề liên quan