Domanda

Ho una serie di probabilità di elementi, diciamo [0.1, 0.2, 0.5, 0.2].L'array somma fino a 1,0.

Utilizzando Pianura Python o Numpy, voglio disegnare elementi proporzionali alla loro probabilità: il primo elemento circa il 10% del tempo, il secondo 20%, il terzo 50% ecc. Il "pareggio" dovrebbe restituire l'indice attinto.

Ho trovato 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 contorto, ci deve essere un modo migliore.Grazie.

È stato utile?

Soluzione

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

Computa la somma cumulativa:

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

Computa un numero casuale distribuito uniformemente nell'intervallo semi-aperto [0, cutoffs[-1]):

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

Usa SearchSorted per trovare l'indice dove il casualeil numero sarebbe inserito in cutoffs:

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

Ritorna choices[idx], dove idx è quell'indice.

Altri suggerimenti

Si desidera campione dalla distribuzione categoriale, che non è implementata in Numpy.Tuttavia, il la distribuzione multinomiale è una generalizzazione del categorico distribuzione e può essere utilizzato per 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
.

Usa numpy.random.multinomial - La più efficiente

Non ho mai usato Ninpy, ma presumo il mio codice qui sotto (solo Python) fa la stessa cosa di ciò che hai realizzato in una riga.Lo sto mettendo qui solo nel caso tu lo desideri.

Guarda molto c-ish quindi ci scusiamo per non essere molto pythic.

weight_total sarebbe 1 per te.

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
.

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top