2012-01-23 29 views
7

Tengo una matriz de probabilidades de elementos, digamos [0.1, 0.2, 0.5, 0.2]. La matriz resume hasta 1.0.dibujar elemento aleatorio en numpy

Usando Python simple o numpy, quiero dibujar elementos proporcionales a su probabilidad: el primer elemento alrededor del 10% del tiempo, el segundo 20%, el tercero 50% etc. El "draw" debería devolver el índice del elemento dibujado .

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 es demasiado complicado, tiene que haber una mejor manera. Gracias.

Respuesta

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] 

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 semiabierto [0, cutoffs[-1]):

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

Uso searchsorted para encontrar el índice donde se inserta el número aleatorio en cutoffs:

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

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

0

Nunca he utilizado numpy, pero supongo que mi código a continuación (solo Python) hace lo mismo que lo que logró en una línea. Lo estoy poniendo aquí por si lo quieres.

Parece muy c-ish así que me disculpo por no ser muy pitónico.

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

debe hacer el truco.

1

uso numpy.random.multinomial - más eficiente

4

¿Quieres muestra de la distribución categórica, que no está implementado en numpy. Sin embargo, la distribución multinomial es una generalización de la distribución categorical y se puede usar para ese fin.

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