2012-01-23 25 views
7

Ho una matrice di probabilità di elementi, diciamo [0.1, 0.2, 0.5, 0.2]. L'array riassume fino a 1.0.disegna elemento casuale in numpy

Usare Python normale o NumPy, voglio disegnare elementi proporzionali alla loro probabilità: il primo elemento circa il 10% del tempo, secondo 20%, 50% terzo ecc Il "tracciare" deve restituire indice dell'elemento disegnato .

sono arrivato fino a questo:

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

Funziona, ma è troppo complicata, ci deve essere un modo migliore. Grazie.

risposta

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] 

Come funziona:

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

Calcola la somma cumulativa:

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

calcolare un numero casuale uniformemente distribuito nel semiaperta intervallo di [0, cutoffs[-1]):

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

Usa searchsorted per trovare l'indice in cui il numero casuale sarebbe stato inserito in cutoffs:

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

ritorno choices[idx], dove idx è tale indice.

0

Non ho mai usato numpy, ma presumo il mio codice di seguito (solo Python) fa la stessa cosa di quello che hai ottenuto in una riga. Lo sto mettendo qui solo nel caso tu lo voglia.

Sembra molto c-ish quindi scuse per non essere molto pitone.

peso_totale sarebbe 1 per voi.

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

uso bisect

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

dovrebbe fare il trucco.

1

uso numpy.random.multinomial - più efficiente

4

volete assaggiare dalla distribuzione categorica, che non è implementata in NumPy. Tuttavia, la distribuzione multinomial è una generalizzazione della distribuzione categorical e può essere utilizzata a tale scopo.

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