Eu tenho um arquivo com algumas probabilidades para valores diferentes, por exemplo:
1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2
Eu gostaria de gerar números aleatórios usando essa distribuição. Existe um módulo existente que lida com isso? É bastante simples codificar por conta própria (crie a função de densidade cumulativa, gere um valor aleatório [0,1] e escolha o valor correspondente), mas parece que isso deve ser um problema comum e provavelmente alguém criou uma função / módulo para isto.
Preciso disso porque quero gerar uma lista de aniversários (que não seguem nenhuma distribuição no random
módulo padrão ).
random.choice()
? Você cria a lista mestre com o número adequado de ocorrências e escolhe uma. Esta é uma pergunta duplicada, é claro.Respostas:
scipy.stats.rv_discrete
pode ser o que você quer. Você pode fornecer suas probabilidades através dovalues
parâmetro Você pode usar orvs()
método do objeto de distribuição para gerar números aleatórios.Conforme apontado por Eugene Pakhomov nos comentários, você também pode passar um
p
parâmetro de palavra - chave paranumpy.random.choice()
, por exemplo,Se você estiver usando o Python 3.6 ou superior, poderá usá
random.choices()
-lo na biblioteca padrão - veja a resposta de Mark Dickinson .fonte
numpy.random.choice()
é quase 20 vezes mais rápido.numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])
Desde o Python 3.6, existe uma solução para isso na biblioteca padrão do Python, a saber
random.choices
.Exemplo de uso: vamos configurar uma população e pesos correspondentes aos da pergunta do OP:
Agora
choices(population, weights)
gera uma única amostra:O argumento opcional somente palavra-chave
k
permite solicitar mais de uma amostra de uma vez. Isso é valioso porque há algum trabalho preparatório querandom.choices
deve ser feito toda vez que é chamado, antes da geração de amostras; gerando muitas amostras de uma só vez, só precisamos fazer esse trabalho preparatório uma vez. Aqui, geramos um milhão de amostras e usamoscollections.Counter
para verificar se a distribuição que obtemos corresponde aproximadamente aos pesos que fornecemos.fonte
Uma vantagem de gerar a lista usando o CDF é que você pode usar a pesquisa binária. Enquanto você precisa de O (n) tempo e espaço para pré-processamento, é possível obter k números em O (k log n). Como as listas Python normais são ineficientes, você pode usar o
array
móduloSe você insiste em espaço constante, pode fazer o seguinte; O (n) tempo, O (1) espaço.
fonte
l[-1]
retorna o último elemento da lista?Talvez seja tarde demais. Mas você pode usar
numpy.random.choice()
, passando op
parâmetro:fonte
random.choice()
- veja os comentários.numpy.random.choice()
é completamente diferenterandom.choice()
e suporta distribuição de probabilidade.(OK, eu sei que você está pedindo um termo-encolhimento, mas talvez essas soluções domésticas não tenham sido sucintas o suficiente para você gostar. :-)
Eu pseudo-confirmei que isso funciona através dos olhos da saída desta expressão:
fonte
i
não é um objeto.Eu escrevi uma solução para tirar amostras aleatórias de uma distribuição contínua personalizada .
Eu precisava disso para um caso de uso semelhante ao seu (por exemplo, gerar datas aleatórias com uma determinada distribuição de probabilidade).
Você só precisa da função
random_custDist
e da linhasamples=random_custDist(x0,x1,custDist=custDist,size=1000)
. O resto é decoração ^^.O desempenho desta solução é improvável, com certeza, mas eu prefiro a legibilidade.
fonte
Faça uma lista de itens, com base em
weights
:Uma otimização pode ser normalizar valores pelo maior divisor comum, para diminuir a lista de destinos.
Além disso, isso pode ser interessante.
fonte
Outra resposta, provavelmente mais rápida :)
fonte
Verificação:
fonte
com base em outras soluções, você gera distribuição acumulativa (como número inteiro ou flutua o que quiser) e, em seguida, pode usar o bisset para torná-lo mais rápido
este é um exemplo simples (usei números inteiros aqui)
a
get_cdf
função converteria de 20, 60, 10, 10 para 20, 20 + 60, 20 + 60 + 10, 20 + 60 + 10 + 10agora escolhemos um número aleatório de até 20 + 60 + 10 + 10 usando
random.randint
e usamos bisect para obter o valor real de maneira rápidafonte
você pode querer dar uma olhada nas distribuições de amostragem aleatória do NumPy
fonte
Nenhuma dessas respostas é particularmente clara ou simples.
Aqui está um método claro e simples que é garantido para o trabalho.
acumulate_normalize_probabilities usa um dicionário
p
que mapeia símbolos para probabilidades OU frequências. Ele gera uma lista utilizável de tuplas das quais fazer a seleção.Rendimentos:
Por que funciona
A acumulação etapa de transforma cada símbolo em um intervalo entre si e a probabilidade ou frequência dos símbolos anteriores (ou 0 no caso do primeiro símbolo). Esses intervalos podem ser usados para selecionar (e, assim, amostrar a distribuição fornecida), basta percorrer a lista até que o número aleatório no intervalo 0,0 -> 1,0 (preparado anteriormente) seja menor ou igual ao ponto final do intervalo do símbolo atual.
A normalização nos liberta da necessidade de garantir que tudo seja de algum valor. Após a normalização, o "vetor" de probabilidades é 1.0.
O restante do código para seleção e geração de uma amostra arbitrariamente longa da distribuição está abaixo:
Uso:
fonte
Aqui está uma maneira mais eficaz de fazer isso:
Basta chamar a seguinte função com sua matriz 'pesos' (assumindo os índices como os itens correspondentes) e o não. de amostras necessárias. Esta função pode ser facilmente modificada para lidar com pares ordenados.
Retorna índices (ou itens) amostrados / selecionados (com substituição) usando suas respectivas probabilidades:
Uma breve nota sobre o conceito usado no loop while. Reduzimos o peso do item atual do beta cumulativo, que é um valor acumulado construído uniformemente de forma aleatória, e incrementamos o índice atual para encontrar o item, cujo peso corresponde ao valor do beta.
fonte