Pergunta

Eu tenho uma série de probabilidades de elementos, digamos [0.1, 0.2, 0.5, 0.2].A matriz soma 1,0.

Usando Python simples ou numpy, quero desenhar elementos proporcionais à sua probabilidade:o primeiro elemento cerca de 10% do tempo, o segundo 20%, o terceiro 50% etc.O “draw” deve retornar o índice do elemento desenhado.

Eu descobri isso:

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, mas é muito complicado, deve haver uma maneira melhor.Obrigado.

Foi útil?

Solução

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]

Como funciona:

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

Calcule a soma cumulativa:

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

Calcule um número aleatório uniformemente distribuído no intervalo semiaberto [0, cutoffs[-1]):

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

Usar pesquisa ordenada para encontrar o índice onde o número aleatório seria inserido cutoffs:

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

Retornar choices[idx], onde idx é esse índice.

Outras dicas

Você deseja obter uma amostra da distribuição categórica, que não é implementada em numpy.No entanto, o multinomial distribuição é uma generalização do categórico distribuição e pode ser usado para esse fim.

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

usar numpy.random.multinomial - mais eficiente

Nunca usei numpy, mas presumo que meu código abaixo (somente python) faça a mesma coisa que você realizou em uma linha.Estou colocando aqui caso você queira.

Parece muito c-ish, então peço desculpas por não ser muito pitônico.

peso_total seria 1 para você.

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

usar bissectar

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

deve fazer o truque.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top