2013-05-18 65 views
5

我正在尋找一種更有效的方法從整數列表1:n多次抽樣,概率向量(也是長度n)每次都不相同。對於20次試驗,其中n = 10,我知道我們可以做這樣的:從不同的概率向量中抽樣的有效方法

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

但是,來電來樣10次只是每次獲得一個號碼,所以它是沒有可能的最快方式。速度會很有幫助,因爲代碼會做很多次。

非常感謝!

盧克

編輯:非常感謝羅馬,他的想法有關基準幫我找到一個很好的解決方案。我現在已經把它轉移到了答案上。

+1

+1您應該添加隨機卷解答作爲解決方案。這是一個非常酷的方法!你有沒有檢查它是如何可縮放的? –

+0

重要的是要注意R'sample'函數中的'prob'參數*沒有替換*並不與一階包含概率成正比。如果你想保留這個,那麼看看package'sampling' @ CRAN。 –

+0

感謝您的投入。費迪南德,你讓我失去了一點點,但我想在這個例子中,這並不重要,因爲樣本的長度爲1(因此在有和沒有替換的情況下抽樣是相同的)。此外,luke2中的解決方案完全避免了樣本。我將把它列爲解決方案。 – lukeholman

回答

2

只是爲了好玩,我試了兩個版本。你在做什麼樣的抽樣?我認爲所有這些都非常快,並且或多或少都相當於(我沒有在您的解決方案中包含probs的創建)。希望看到別人對此採取行動。

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 
1

這裏的另一種方法,我發現。它速度很快,但速度並不像使用for循環多次調用樣本那麼快。我最初認爲它非常好,但我錯誤地使用了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的大概率不同長度的線會佔用較多的比小的數行。然後,您可以通過在數字線上選擇一個隨機點來選擇結果 - 大概率將更有可能被選中。這種方法的優點是可以在runif()的一次調用中滾動所需的所有隨機數,而不是像函數luke,roman和roman2一樣反覆調用樣本。但是,看起來額外的數據處理會降低速度,而且成本大大抵消了這種好處。

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()的操作。

+0

'luke2'沒有被調用。 「benchmark」的第三個參數只是*定義了一個函數,它並不執行它。您應該在'benchmark'調用之外定義函數,並使用類似'luke2 = luke2(probs),roman = ...'的函數。 –

+0

Doh,謝謝你。我現在看到我在做什麼和羅馬人如何使用他的差異。事實證明它並不是那麼好!我仍然覺得那裏必須有更好的解決方案 - 反覆調用樣本不可能是最好的方法。 – lukeholman

相關問題