2012-01-23 27 views
7

Mam szereg prawdopodobieństw elementów, powiedzmy [0.1, 0.2, 0.5, 0.2]. Tablica sumuje się do 1,0.losowy element w numpy

Używając zwykłego Pythona lub numpy, chcę narysować elementy proporcjonalne do ich prawdopodobieństwa: pierwszy element około 10% czasu, drugi 20%, trzeci 50% itd. "Rysowanie" powinno zwrócić indeks elementu narysowanego .

wymyśliłem to:

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]) 

To działa, ale to zbyt zawiłe, że musi być lepszy sposób. Dzięki.

Odpowiedz

9
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] 

Jak to działa:

In [22]: import numpy as np 
In [23]: probs = [0.1, 0.2, 0.5, 0.2] 

obliczyć skumulowaną sumę:

In [24]: cutoffs = np.cumsum(probs) 
In [25]: cutoffs 
Out[25]: array([ 0.1, 0.3, 0.8, 1. ]) 

obliczyć równomiernie rozłożony liczb losowych w półotwartej przedziale [0, cutoffs[-1]):

In [26]: np.random.uniform(0, cutoffs[-1]) 
Out[26]: 0.9723114393023948 

użytkowania searchsorted znaleźć indeks, gdzie liczba losowa zostanie wstawiony cutoffs:

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

Return choices[idx], gdzie idx jest to, że wskaźnik.

0

Nigdy nie użyłem numpy, ale zakładam, że mój kod poniżej (tylko python) robi to samo, co w jednym wierszu. Wkładam to tutaj, na wszelki wypadek, jeśli chcesz.

Wygląda bardzo c-ish, więc przepraszam, że nie jest bardzo pytoniczny.

weight_total będzie 1 dla Ciebie.

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 
0

użycie bisect

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

powinno załatwić sprawę.

1

użycie numpy.random.multinomial - najbardziej wydajny

4

Chcesz spróbować z dystrybucji kategorycznego, który nie jest realizowany w numpy. Jednak dystrybucja multinomial jest uogólnieniem rozkładu categorical i może być wykorzystana do tego celu.

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