2013-04-25 17 views
6

我正數和負數更高效的策略,其中()或匹配()

vec<-c(seq(-100,-1), rep(0,20), seq(1,100)) 

的矢量比的例子大,並且呈現出隨機的一組值的向量。我必須重複查找矢量中負數的數量......我發現這是相當低效的。因爲我只需要找到負數的數量,並且向量被排序,我只需要知道前0或正數的索引(實際隨機向量中可能沒有0)。

目前我使用這個代碼,以查找長度

length(which(vec<0)) 

但是這迫使R鍵完成整個載體,但由於它的排序,也沒有必要。

我可以用

match(0, vec) 

,但我的矢量並不總是有0

所以我的問題是,是否有某種匹配的()函數,它適用的條件,而不是尋找一個特定的值?還是有更有效的方式來運行我的哪些()代碼?

謝謝

回答

15

到目前爲止提供的解決方案都意味着創建一個logical(length(vec))並對此進行全面或部分掃描。正如你注意到的那樣,矢量是排序的。我們可以通過二進制搜索來利用這一點。我開始認爲我會超級聰明,並且以更快的速度在C中實現它,但是在調試算法的索引(這是棘手的部分!)時遇到了麻煩。所以我寫了它在R:

f3 <- function(x) { 
    imin <- 1L 
    imax <- length(x) 
    while (imax >= imin) { 
     imid <- as.integer(imin + (imax - imin)/2) 
     if (x[imid] >= 0) 
      imax <- imid - 1L 
     else 
      imin <- imid + 1L 
    } 
    imax 
} 

爲了與其他建議

f0 <- function(v) length(which(v < 0)) 
f1 <- function(v) sum(v < 0) 
f2 <- function(v) which.min(v < 0) - 1L 

和樂趣

library(compiler) 
f3.c <- cmpfun(f3) 

通往

> vec <- c(seq(-100,-1,length.out=1e6), rep(0,20), seq(1,100,length.out=1e6)) 
> identical(f0(vec), f1(vec)) 
[1] TRUE 
> identical(f0(vec), f2(vec)) 
[1] TRUE 
> identical(f0(vec), f3(vec)) 
[1] TRUE 
> identical(f0(vec), f3.c(vec)) 
[1] TRUE 
> microbenchmark(f0(vec), f1(vec), f2(vec), f3(vec), f3.c(vec)) 
Unit: microseconds 
     expr  min  lq  median   uq  max neval 
    f0(vec) 15274.275 15347.870 15406.1430 15605.8470 19890.903 100 
    f1(vec) 15513.807 15575.229 15651.2970 17064.8830 18326.293 100 
    f2(vec) 21473.814 21558.989 21679.3210 22733.1710 27435.889 100 
    f3(vec) 51.715 56.050 75.4495 78.5295 100.730 100 
f3.c(vec) 11.612 17.147 28.5570 31.3160 49.781 100 

可能有一些比較棘手的邊緣情況下,我有wr翁!移動到C,我沒

library(inline) 
f4 <- cfunction(c(x = "numeric"), " 
    int imin = 0, imax = Rf_length(x) - 1, imid; 
    while (imax >= imin) { 
     imid = imin + (imax - imin)/2; 
     if (REAL(x)[imid] >= 0) 
      imax = imid - 1; 
     else 
      imin = imid + 1; 
    } 
    return ScalarInteger(imax + 1); 
") 

> identical(f3(vec), f4(vec)) 
[1] TRUE 
> microbenchmark(f3(vec), f3.c(vec), f4(vec)) 
Unit: nanoseconds 
     expr min  lq median  uq max neval 
    f3(vec) 52096 53192.0 54918.5 55539.0 69491 100 
f3.c(vec) 10924 12233.5 12869.0 13410.0 20038 100 
    f4(vec) 553 796.0 893.5 1004.5 2908 100 

findInterval上前當有人問R-help名單上類似的問題。這是緩慢但安全的,檢查vec實際上是排序並處理NA值。如果想住上邊緣(可以說是不差,實施F3或F4),然後

f5.i <- function(v) 
    .Internal(findInterval(v, 0 - .Machine$double.neg.eps, FALSE, FALSE)) 

幾乎是一樣快的C實現,但可能更強大和矢量(即查找值的向量在第二個參數中,用於簡單的範圍計算)。

+5

+1哇。我會從中學到很多東西。非常感謝您發佈這樣一個深思熟慮的答案 – 2013-04-25 20:59:29

+0

當我的f4函數獲取時出現錯誤https://gist.github.com/anonymous/5785498 – Juancentro 2013-06-14 21:35:11

+0

@Juancentro代碼的C版本要求您有一個C編譯器安裝。對於Windows,[請按照這些說明](http://cran.r-project.org/bin/windows/Rtools/)。 – 2013-06-14 21:57:35

3

使用sum()和邏輯比較:

sum(vec < 0) 
[1] 100 

這將是相當快,當你總結的邏輯,TRUE是1和FALSE爲0,因此總的將是數的負面價值。

嗯哦,我覺得一個基準比較的需要... :-)向量的長度爲2E5

library(microbenchmark) 
vec<-c(seq(-100,-1,length.out=1e5), rep(0,20), seq(1,100,length.out=1e5)) 
microbenchmark((which.min(vec < 0) - 1L) , (sum(vec < 0))) 

Unit: milliseconds 
         expr  min  lq median  uq  max neval 
(which.min(vec < 0) - 1L) 1.883847 2.130746 2.554725 3.141787 75.943911 100 
      (sum(vec < 0)) 1.398100 1.500639 1.508688 1.745088 2.662164 100 
+0

s/subsetting/comparison/;-) – 2013-04-25 11:15:20

+0

@JoshuaUlrich s ??? – 2013-04-25 11:23:03

+0

Simon,這是'sed'和/或unix shell命令語法的一部分。鉛「是」替代品的簡稱。「 – 2013-04-25 11:32:02

2

你可以使用which.min

which.min(vec < 0) - 1L 

這將返回第一FALSE值,即第一個0.