我正在学习R和Python中的“做贝叶斯数据分析”的练习。
我想找到一个快速的方法来做蒙特卡罗模拟,使用常数空间。
下面的问题很小,但是对于不同的方法来说是一个很好的测试:
ex 4.3 确定从拖曳的皮诺克牌甲板中抽签10的确切概率。(在皮诺奇牌中,有48张牌。有六个价值观: 9,10,杰克,王后,国王,艾斯。标准的四套套装每套都有两份价值副本:红心、钻石、棍棒、黑桃。 (A)获得10分的可能性是多少?
当然,答案是1/6。
我能找到的最快的解决方案(相当于R的速度)是使用np.random.choice生成大量的卡片绘制,然后应用Counter。我不喜欢不必要地创建数组的想法,所以我尝试使用字典和for循环,一次绘制一张卡片,并增加该类型卡片的计数。令我惊讶的是,它要慢得多!
下面是我测试的3种方法的完整代码。_Is有一种与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 ()结果:
method1: 1.2997
method2: 23.0626
method3: 5.5859
R代码:
card = sample(deck, numDraws, replace=TRUE)
print(as.data.frame(table(card)/numDraws))发布于 2019-06-08 17:05:26
这里有一个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替换计数步骤。同样,它可以很好地处理数值数据,并且我们从这个方法开始时完成的数值转换中得到了这一点。使用给定样本数据集与最快的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)发布于 2021-07-18 01:19:36
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_drawshttps://stackoverflow.com/questions/56508345
复制相似问题