Вопрос

У меня есть массив вероятностей элементов, скажем [0.1, 0.2, 0.5, 0.2].Сумма массива равна 1,0.

Используя простой Python или numpy, я хочу рисовать элементы, пропорциональные их вероятности:первый элемент примерно 10% времени, второй 20%, третий 50% и т.д.«Рисование» должно возвращать индекс нарисованного элемента.

Я придумал это:

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) делает то же самое, что вы выполнили в одной строке.Я помещу это здесь, на случай, если вам это понадобится.

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

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

использовать разделить пополам

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