2013-11-09 6 views
3

Привет Я делаю код для класса геномики, и у меня возникают трудности с определенной частью.Создание взвешенных случайных чисел

У меня есть набор взаимоисключающих событий event1, event2, ... eventn с вероятностями p1, p2, ... pn

Я хочу, чтобы имитировать случайную выборку события п раз с заданной вероятностью.

ввода: вероятности = {0,3, 0,2, 0,5} события {e1, e2, e3} п = 100

выход: должна быть ~ 50 результатов для e3, ~ 20 для е2 и ~ 30 для e1 , Обратите внимание, что это, вероятно, не совсем 50, 20, 30, потому что эмпирических значений отличаются от теоретических значений ...

+0

Если входы «вероятности» и «события» действительно являются обеими наборами, как вы показываете, нет способа сопоставить вероятность события. – abarnert

+0

Вы просто пытаетесь генерировать случайные числа «n»? – Leigh

+3

См. Страницу Эли Бендерски по [взвешенной случайной выборке] (http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/) для обсуждения многих способов достижения этого. – DSM

ответ

5

Python не имеет взвешенную функциональность выборки встроенную в (NumPy/SciPy делает), но для очень простой случай, как это, это довольно легко:

import itertools 
import random 

probabilities = [0.3, 0.2, 0.5] 
totals = list(itertools.accumulate(probabilities)) 

def sample(): 
    n = random.uniform(0, totals[-1]) 
    for i, total in enumerate(totals): 
     if n <= total: 
      return i 

Если у вас нет Python 3.2+, вы не имеете функцию accumulate; вы можете подделать его с неэффективным однострочником, если список действительно этим короткий:

totals = [sum(probabilities[:i+1]) for i in range(len(probabilities))] 

... или вы можете написать явную петлю, или некрасивый reduce вызов или скопировать функцию эквивалента Python из the docs.


Кроме того, обратите внимание, что random.uniform(0, totals[-1]) просто более сложный способ написания random.random(), если вы можете быть уверены, что ваши номера добавить до 1,0.


Быстрый способ проверить это:

>>> samples = [sample() for _ in range(100000)] 
>>> samples.count(0) 
29878 
>>> samples.count(1) 
19908 
>>> samples.count(2) 
50214 

Это довольно близко к 30%, 20% и 50% от 100000, соответственно.

+0

Я пробовал это, но он всегда дает индекс последнего термина. Почему это? – user2812970

+0

@ user2812970: Я только что скопировал и вложил это в свой интерпретатор (и добавил отсутствующий «import random») и провел его 100K раз, чтобы проверить его, и он дает адрес последнего термина примерно в половине случаев, точно так же, как это должен.Я отредактировал ответ, чтобы показать тест. Если он действительно всегда дает вам «2», либо вы вложили его неправильно, либо вы сделали что-то не так в какой-либо другой части вашего кода, или вы должны немедленно отправиться в Лас-Вегас и воспользоваться преимуществами ваших способностей, влияющих на вероятность мутантов , :) – abarnert

2

Предположим, что мы имеем три события, каждое из которых имеет вероятности .3, .2 и .5 соответственно. Затем для каждого сгенерированного образца мы генерируем число в диапазоне [0,1], назовем это «rand». Если «rand» < .3, мы генерируем событие 1, если .3 < = «rand» < .5, мы генерируем ровно 2, в противном случае мы генерируем событие 3. Это может быть выполнено с использованием random(), что действительно порождает число в диапазон [0,1].

Смежные вопросы