нарисовать случайный элемент в NumPy - PullRequest
7 голосов
/ 23 января 2012

У меня есть массив вероятностей элементов, скажем, [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])

Это работает, но это слишком запутанно, должен быть лучший способ.Спасибо.

Ответы [ 5 ]

9 голосов
/ 23 января 2012
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

Используйте searchsorted , чтобы найти индекс, куда случайное число будет вставлено в cutoffs:

In [27]: cutoffs.searchsorted(0.9723114393023948)
Out[27]: 3

Вернуть choices[idx], где idx - это индекс.

4 голосов
/ 07 сентября 2012

Вы хотите сделать выборку из категориального дистрибутива, который не реализован в numpy.Однако многочленный *1001* является обобщением категориального и может использоваться для этой цели.

>>> 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
1 голос
/ 08 марта 2012

использование numpy.random.multinomial - самый эффективный

0 голосов
/ 23 января 2012

использование пополам

import bisect
import random
import numpy 
def draw(probs):
    cumsum=numpy.cumsum(probs/sum(probs))
    return bisect.bisect_left(cumsum, numpy.random.rand())

должно сделать трюк.

0 голосов
/ 23 января 2012

Я никогда не использовал numpy, но я предполагаю, что мой код ниже (только на python) делает то же самое, что вы выполнили в одной строке.Я помещаю это здесь на всякий случай, если вы этого хотите.

Выглядит очень странно, поэтому извинения за то, что вы не очень питоничны.

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
...