比方说,我有一系列元素概率 [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中实现。但是,多项式分布是分类分布,可用于该目的。

>>> import numpy as np
>>> 
>>> def sampleCategory(p):
...     return np.flatnonzero( np.random.multinomial(1,p,1) )[0]
... 
>>> sampleCategory( [0.1,0.5,0.4] )
1
.

使用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())
.

应该做这个技巧。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top