Pregunta

Tengo una serie de probabilidades de elementos, digamos [0.1, 0.2, 0.5, 0.2].La matriz resume hasta 1.0.

Uso de pitón liso o adormecido, quiero dibujar elementos proporcionales a su probabilidad: el primer elemento alrededor del 10% del tiempo, el segundo 20%, el tercero 50%, etc. El "sorteo" debe devolver el índice del elemento dibujado.

Se me ocurrió esto:

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

funciona, pero está demasiado complicado, debe haber una mejor manera.Gracias.

¿Fue útil?

Solución

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]

cómo funciona:

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

Calcular la suma acumulativa:

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

Calcular un número aleatorio distribuido uniformemente en el intervalo medio abierto [0, cutoffs[-1]):

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

Use Searchsorded para encontrar el índice donde el azarEl número se insertaría en cutoffs:

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

Devuelve choices[idx], donde idx es ese índice.

Otros consejos

Usted desea probar de la distribución categórica, que no se implementa en NOMBRES.Sin embargo, la Multinomial Distribution es una generalización de la categórica Distribución y se puede utilizar para ese propósito.

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

Utilice numpy.random.multinomial - más eficiente

Nunca he usado NOMPY, pero asumo que mi código a continuación (solo Python) hace lo mismo que lo que logró en una línea.Lo pongo aquí, por si acaso lo desees.

se ve muy c-ish, así que disculpas por no ser muy pythonic.

weight_total sería 1 para usted.

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

Use bisect

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

debe hacer el truco.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top