2013-04-25 6 views
6

Iは、正と負の数より効率的な戦略いる()またはmatch()は

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

ベクターは、例えばより大きい、および値のランダムなセットを取るのベクトルを有します。私は反復的にベクトルの負の数の数を見つける必要があります...私はこれが非常に非効率的であることがわかりました。

負の数を見つける必要があるだけで、ベクトルがソートされているので、最初の0または正の数のインデックスを知る必要があります(実際のランダムベクトルには0はありません)。

現在、私は長さ

length(which(vec<0)) 

を見つけるために、このコードを使用していますが、これは全体のベクトルを通過するRを強制し、それがソートされているので、必要はありません。

私は使用することができ

match(0, vec) 

が、私のベクトルは常に持っていません0

だから私の質問は、試合のいくつかの種類が()の条件を適用する機能はなく、特定の値を見つけることがあるさ?または、which()コードを実行するより効率的な方法はありますか?

答えて

15

これまで提供されているソリューションは、すべてlogical(length(vec))を作成し、これを完全または部分的にスキャンすることを意味しています。あなたが注意するように、ベクトルはソートされます。バイナリ検索を行うことでこれを悪用することができます。私は超賢いと思って、さらに高速化するためにCで実装しましたが、アルゴリズムの索引付けをデバッグするのに問題がありました(これは難しい部分です)。他の提案

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

> 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 

おそらくがあるいくつかの大手楽しい

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

用との比較のため

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 
} 

:だから私はRでそれを書きました私がwrを持っているトリッキーなエッジケースong! Cへの移動、私は

> 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

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); 
") 

をした同様の質問がR-helpリストに頼まれた時に思いつきました。遅いが安全で、vecが実際にソートされ、NA値を処理していることがチェックされます。 1は、(F3やF4を実装することを間違いなく悪くない)エッジで生きることを望んでいる場合(つまり、値のベクトルを調べ、その後

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

は、ほぼ同じ速さCの実装などですが、おそらくより堅牢とベクトル化簡単な範囲のような計算のための第2引数で)。

+5

+1うわー。私はこれからたくさんのことを学びます。そのような思慮深いとindepthの回答を投稿していただきありがとうございます –

+0

あなたのf4関数を調達する際にエラーが発生しました。https://gist.github.com/anonymous/5785498 – Juancentro

+0

@JuancentroコードのCバージョンでは、Cコンパイラが必要ですインストールされます。ウィンドウの場合、[以下の手順に従ってください](http://cran.r-project.org/bin/windows/Rtools/)。 –

3

使用sum()と論理比較ありがとう:

sum(vec < 0) 
[1] 100 

これはかなり迅速になり、あなたは論理を合計すると、TRUEは1とFALSEである0はそう合計数になりますです負の値の

ああええと、私はベンチマーク比較の必要性を感じて... :-)ベクトル長が

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 /サブセット化/比較/ ;-) –

+0

@JoshuaUlrich s ??? –

+0

Simon、それは 'sed'やunixシェルのコマンド構文の一部です。リード "s"は "substitute"の略です。 –

2

を2E5されますがwhich.min

which.min(vec < 0) - 1L 

を使用することができますこれは、最初FALSE値を返します。つまり、最初の0です。