[英]Fast Monte-Carlo simulation with numpy?
我正在學習 R 和 Python 中“做貝葉斯數據分析”中的練習。
我想找到一種使用恆定空間進行蒙特卡羅模擬的快速方法。
下面的問題是微不足道的,但可以很好地測試不同的方法:
例 4.3
確定從洗牌的 pinochle 牌中抽出 10 的確切概率。 (在pinochle 套牌中,有48 張牌。有六個值:9、10、Jack、Queen、King、Ace。在標准的四種花色中,每個值都有兩個副本:紅心、菱形、梅花、黑桃.)
(A) 得到 10 的概率是多少?
當然,答案是 1/6。
我能找到的最快的解決方案(與 R 的速度相當)是使用np.random.choice
生成大量抽np.random.choice
,然后應用Counter
。 我不喜歡不必要地創建數組的想法,所以我嘗試使用字典和 for 循環,一次繪制一張卡片並增加該類型卡片的計數。 令我驚訝的是,它慢得多!
下面是我測試的 3 種方法的完整代碼。 _有沒有一種方法可以與 method1() 一樣高效,但使用常量空間?
Python 代碼: ( Google Colab 鏈接)
deck = [c for c in ['9','10','Jack','Queen','King','Ace'] for _ in range(8)]
num_draws = 1000000
def method1():
draws = np.random.choice(deck, size=num_draws, replace=True)
df = pd.DataFrame([Counter(draws)])/num_draws
print(df)
def method2():
card_counts = defaultdict(int)
for _ in range(num_draws):
card_counts[np.random.choice(deck, replace=True)] += 1
df = pd.DataFrame([card_counts])/num_draws
print(df)
def method3():
card_counts = defaultdict(int)
for _ in range(num_draws):
card_counts[deck[random.randint(0, len(deck)-1)]] += 1
df = pd.DataFrame([card_counts])/num_draws
print(df)
Python timeit() 結果:
方法 1:1.2997
方法 2:23.0626
方法 3:5.5859
代碼:
card = sample(deck, numDraws, replace=TRUE)
print(as.data.frame(table(card)/numDraws))
這是一個np.unique
+ np.bincount
-
def unique():
unq,ids = np.unique(deck, return_inverse=True)
all_ids = np.random.choice(ids, size=num_draws, replace=True)
ar = np.bincount(all_ids)/num_draws
return pd.DataFrame(ar[None], columns=unq)
有兩項主要改進對我們有所幫助:
我們將字符串數據轉換為數字。 NumPy 可以很好地處理此類數據。 為了實現這一點,我們使用np.unique
。
我們使用np.bincount
來代替計數步驟。 同樣,它適用於數字數據,我們確實通過在此方法開始時完成的數字轉換獲得了這一點。
NumPy 通常適用於大數據,這里就是這種情況。
計時與給定的樣本數據集的比較反對最快method1
-
In [177]: %timeit method1()
328 ms ± 16.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [178]: %timeit unique()
12.4 ms ± 265 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Numpy 通過在其數值引擎中運行 C 代碼來提高效率。 Python 很方便,但它比 C 慢幾個數量級。
在 Numpy 和其他高性能 Python 庫中,Python 代碼主要由膠水代碼組成,用於准備要調度的任務。 由於存在開銷,因此一次繪制大量樣本要快得多。
請記住,為 Numpy 工作提供 100 萬個元素的緩沖區仍然是恆定空間。 然后你可以通過循環采樣 10 億次。
這種額外的內存分配通常不是問題。 如果您必須不惜一切代價避免使用內存同時仍能從 Numpy 中獲得性能優勢,您可以嘗試使用 Numba 或 Cython 來加速它。
from numba import jit
@jit(nopython=True)
def method4():
card_counts = np.zeros(6)
for _ in range(num_draws):
card_counts[np.random.randint(0, 6)] += 1
return card_counts/num_draws
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.