题
比方说,我有一系列元素概率 [0.1, 0.2, 0.5, 0.2]
. 。该数组的总和为 1.0。
使用普通的 Python 或 numpy,我想绘制与其概率成比例的元素:第一个元素大约占 10%,第二个元素占 20%,第三个元素占 50%,依此类推。“draw”应该返回绘制元素的索引。
我想出了这个:
def draw(probs):
cumsum = numpy.cumsum(probs / sum(probs)) # sum up to 1.0, just in case
return len(numpy.where(numpy.random.rand() >= cumsum)[0])
可行,但是太复杂了,必须有更好的方法。谢谢。
解决方案
import numpy as np
def random_pick(choices, probs):
'''
>>> a = ['Hit', 'Out']
>>> b = [.3, .7]
>>> random_pick(a,b)
'''
cutoffs = np.cumsum(probs)
idx = cutoffs.searchsorted(np.random.uniform(0, cutoffs[-1]))
return choices[idx]
怎么运行的:
In [22]: import numpy as np
In [23]: probs = [0.1, 0.2, 0.5, 0.2]
计算累积和:
In [24]: cutoffs = np.cumsum(probs)
In [25]: cutoffs
Out[25]: array([ 0.1, 0.3, 0.8, 1. ])
计算半开区间内均匀分布的随机数 [0, cutoffs[-1])
:
In [26]: np.random.uniform(0, cutoffs[-1])
Out[26]: 0.9723114393023948
使用 搜索排序 找到随机数将被插入的索引 cutoffs
:
In [27]: cutoffs.searchsorted(0.9723114393023948)
Out[27]: 3
返回 choices[idx]
, , 在哪里 idx
是那个索引。
其他提示
使用numpy.random.multinomial
- 最有效的
我从未使用numpy,但我假设我的代码下面(只有python)与您在一行所完成的内容相同。我在这里把它放在这里,以防你想要它。
看起来非常c-ish,为不是非常pythonic道歉。
weight_total为您为1。
def draw(probs)
r = random.randrange(weight_total)
running_total = 0
for i, p in enumerate(probs)
running_total += p
if running_total > r:
return i
. 使用 bisect
import bisect
import random
import numpy
def draw(probs):
cumsum=numpy.cumsum(probs/sum(probs))
return bisect.bisect_left(cumsum, numpy.random.rand())
.
应该做这个技巧。
不隶属于 StackOverflow