6

確率ベクトル(長さnも)が毎回異なる整数1:nのリストから複数回サンプリングするより効率的な方法を探しています。n = 10 の 20 回の試行では、次のようにできることがわかっています。

probs <- matrix(runif(200), nrow = 20)
answers <- numeric(20)
for(i in 1:20) answers[i] <- sample(10,1,prob=probs[i,])

しかし、毎回単一の数値を取得するために sample を 10 回呼び出すため、おそらく最速の方法ではありません。コードはこれを何度も行うため、速度は役に立ちます。

どうもありがとう!

ルーク

編集:ベンチマークについてのアイデアが良い解決策を見つけるのに役立ったRomanに感謝します。これを答えに移動しました。

4

2 に答える 2

2

楽しみのために、さらに 2 つのバージョンを試してみました。このサンプリングはどの程度の規模で行っていますか? これらはすべてかなり高速で、多かれ少なかれ同等だと思います(ソリューションのプロブの作成は含めていません)。他の人がこれを撮影するのを見てみたいです。

library(rbenchmark)
benchmark(replications = 1000,
          luke = for(i in 1:20) answers[i] <- sample(10,1,prob=probs[i,]),
          roman = apply(probs, MARGIN = 1, FUN = function(x) sample(10, 1, prob = x)),
          roman2 = replicate(20, sample(10, 1, prob = runif(10))))

    test replications elapsed relative user.self sys.self user.child sys.child
1   luke         1000    0.41    1.000      0.42        0         NA        NA
2  roman         1000    0.47    1.146      0.46        0         NA        NA
3 roman2         1000    0.47    1.146      0.44        0         NA        NA
于 2013-05-18T07:13:41.060 に答える
1

これが私が見つけた別のアプローチです。これは高速ですが、単純に for ループで sample を何度も呼び出すほど高速ではありません。最初はとても良いと思っていましたが、benchmark() の使い方が間違っていました。

luke2 = function(probs) { # takes a matrix of probability vectors, each in its own row
                probs <- probs/rowSums(probs) 
                probs <- t(apply(probs,1,cumsum)) 
                answer <- rowSums(probs - runif(nrow(probs)) < 0) + 1 
                return(answer)  }

確率を、0 から 1 までの数直線上に配置されたさまざまな長さの線として想像してください。大きな確率は、小さなものよりも数直線の多くを占めます。次に、数直線上のランダムなポイントを選択して結果を選択できます。確率が大きいほど、選択される可能性が高くなります。このアプローチの利点は、関数 luke、roman、roman2 のように sample を何度も呼び出す代わりに、runif() の 1 回の呼び出しで必要なすべての乱数をロールできることです。ただし、余分なデータ処理により速度が低下し、コストがこの利点を相殺する以上のようです.

library(rbenchmark)
probs <- matrix(runif(2000), ncol = 10)
answers <- numeric(200)

benchmark(replications = 1000,
          luke = for(i in 1:20) answers[i] <- sample(10,1,prob=probs[i,]),
          luke2 = luke2(probs),
          roman = apply(probs, MARGIN = 1, FUN = function(x) sample(10, 1, prob = x)),
          roman2 = replicate(20, sample(10, 1, prob = runif(10))))
              roman = apply(probs, MARGIN = 1, FUN = function(x) sample(10, 1, prob = x)),
              roman2 = replicate(20, sample(10, 1, prob = runif(10))))

    test replications elapsed relative user.self sys.self user.child sys.child
    1   luke         1000   0.171    1.000     0.166    0.005          0         0
    2  luke2         1000   0.529    3.094     0.518    0.012          0         0
    3  roman         1000   1.564    9.146     1.513    0.052          0         0
    4 roman2         1000   0.225    1.316     0.213    0.012          0         0

何らかの理由で、さらに行を追加すると、apply() の動作が非常に悪くなります。for() のラッパーだと思っていたので、roman() は luke() と同じように動作するはずなので、理由がわかりません。

于 2013-05-20T06:36:15.780 に答える