2011-11-18 66 views
9

(背景信息:ifelse評估表達的兩個,即使只有一個將返回編輯:這是不正確的陳述見湯米的答覆。)是否適用於非矢量化的情況,反之亦然?

是否有一個地方是有道理的使用ifelse任何例子在非矢量化的情況下?我認爲,當我們不關心小的效率增益時,「可讀性」可能是一個有效的答案,但除此之外,如果使用if然後再使用ifelse時,其速度更快/相當於/更好一些else會做這項工作嗎?

同樣,如果我有矢量化的情況,ifelse總是最好的工具來使用?似乎很奇怪,這兩個表達式都被評估過。是否更快地逐一循環並進行正常的if然後else?我猜測只有在評估表達式花了很長時間纔有意義。有沒有其他的選擇不涉及顯式循環?

感謝

回答

14

首先,ifelse確實始終評估兩種表述 - 只有在有測試向量都TRUEFALSE元素。

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

,在我看來:

ifelse應該將在非量化的情況下使用。這是總是慢更容易出錯使用ifelse超過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) 

在量化的情況下,雖然,ifelse可以是一個不錯的選擇 - 但要注意的是結果的長度和屬性可能不是你所期望的(如上所述,我認爲ifelse在這方面被打破)。

下面是一個示例:tst的長度爲5並且有一個類。我期望的結果是長度爲10,沒有類,但這不是什麼情況 - 它得到一個不兼容的類和長度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" 

爲什麼我會期望長度爲10?因爲在R「循環」的大部分功能較短矢量以匹配較長:

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

...但是ifelse僅週期是/否參數以匹配TST參數的長度。

爲什麼我會期望從測試對象中複製類(和其他屬性)到而不是?因爲返回邏輯向量的<不會從它的(通常是數字)參數中複製類和屬性。它不這樣做,因爲它通常是非常錯誤的。

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

最後,「自己動手」可以更有效率嗎?那麼,ifelse似乎不是像if這樣的基元,它需要一些特殊的代碼來處理NA。如果您沒有NA s,那麼自己動手可能會更快。

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

謝謝!我現在沒時間看這個,但今晚晚些時候我會這樣做。感謝您的評論和示例。 –

+0

優秀的建議和例子。謝謝! –

+1

請注意'ifelse'確實執行向量回收 - 如果'yes'或'no'變量的長度與'test'不相等,它們將被回收。這意味着如果所有矢量長度相等,則示例末尾的測試代碼只會得出相同的結果。試試這個輸入數據:'tst < - sample(c(TRUE,FALSE),1e2,replace = TRUE); a < - 1:100; b < - - (1:50);' – Andrie

4

關於第二點,您如何定義「最佳」?我認爲ifelse()是更可讀的解決方案之一,但可能並不總是最快的。具體來說,我發現寫出布爾條件並將它們加在一起可以帶來一些性能優勢。這裏有一個簡單的例子:

> 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 

所以,如果速度是你最關心的問題,布爾方法可能會更好。然而,對於我的大部分目的 - 我發現ifelse()足夠快,很容易理解。您的里程可能會有所不同。

+0

我會羞於承認編寫布爾版本不僅僅因爲它很快,而且因爲它是31337。但是,嚴肅地說,一旦你習慣了它,代碼就像使用if一樣易於閱讀和理解代碼。 –

+0

@Chase好點 –

+0

使用'y3 < - (x <= 0)+ 1'的速度更快,因爲它不包含冗餘測試。 –

相關問題