Генерация дискретных случайных величин с заданными весами с использованием SciPy или NumPy
Я ищу простую функцию, которая может генерировать массив указанных случайных значений на основе их соответствующих (также указанных) вероятностей. Он мне нужен только для генерации значений с плавающей запятой, но я не понимаю, почему он не может генерировать скаляр. Я могу придумать много способов построить это из существующих функций, но я думаю, что я, вероятно, просто пропустил очевидную функцию SciPy или NumPy.
Например:
>>> values = [1.1, 2.2, 3.3]
>>> probabilities = [0.2, 0.5, 0.3]
>>> print some_function(values, probabilities, size=10)
(2.2, 1.1, 3.3, 3.3, 2.2, 2.2, 1.1, 2.2, 3.3, 2.2)
Примечание: я нашел scipy.stats.rv_discrete, но я не понимаю, как это работает. В частности, я не понимаю, что это (ниже) означает, и что оно должно делать:
numargs = generic.numargs
[ <shape(s)> ] = ['Replace with resonable value', ]*numargs
Если я должен использовать rv_discrete, не могли бы вы дать мне простой пример и объяснение вышеприведенного утверждения "shape"?
5 ответов
Рисунок из дискретного распределения напрямую встроен в NumPy. Функция называется random.choice (ее трудно найти без ссылки на дискретные распределения в бесчисленных документах).
elements = [1.1, 2.2, 3.3]
probabilities = [0.2, 0.5, 0.3]
np.random.choice(elements, 10, p=probabilities)
Вот короткая, относительно простая функция, которая возвращает взвешенные значения, она использует NumPy digitize
, accumulate
, а также random_sample
,
import numpy as np
from numpy.random import random_sample
def weighted_values(values, probabilities, size):
bins = np.add.accumulate(probabilities)
return values[np.digitize(random_sample(size), bins)]
values = np.array([1.1, 2.2, 3.3])
probabilities = np.array([0.2, 0.5, 0.3])
print weighted_values(values, probabilities, 10)
#Sample output:
[ 2.2 2.2 1.1 2.2 2.2 3.3 3.3 2.2 3.3 3.3]
Это работает так:
- Первое использование
accumulate
мы создаем мусорные ведра. - Затем мы создаем кучу случайных чисел (между
0
, а также1
) с помощьюrandom_sample
- Мы используем
digitize
чтобы увидеть, в какие корзины попадают эти числа. - И вернуть соответствующие значения.
Вы шли в хорошем направлении: встроенный scipy.stats.rv_discrete()
совершенно напрямую создает дискретную случайную величину. Вот как это работает:
>>> from scipy.stats import rv_discrete
>>> values = numpy.array([1.1, 2.2, 3.3])
>>> probabilities = [0.2, 0.5, 0.3]
>>> distrib = rv_discrete(values=(range(len(values)), probabilities)) # This defines a Scipy probability distribution
>>> distrib.rvs(size=10) # 10 samples from range(len(values))
array([1, 2, 0, 2, 2, 0, 2, 1, 0, 2])
>>> values[_] # Conversion to specific discrete values (the fact that values is a NumPy array is used for the indexing)
[2.2, 3.3, 1.1, 3.3, 3.3, 1.1, 3.3, 2.2, 1.1, 3.3]
Распространение distrib
Таким образом, выше возвращает индексы из values
список.
В более общем смысле, rv_discrete()
принимает последовательность целочисленных значений в первых элементах его values=(…,…)
аргумент, и возвращает эти значения, в этом случае; нет необходимости конвертировать в конкретные (плавающие) значения. Вот пример:
>>> values = [10, 20, 30]
>>> probabilities = [0.2, 0.5, 0.3]
>>> distrib = rv_discrete(values=(values, probabilities))
>>> distrib.rvs(size=10)
array([20, 20, 20, 20, 20, 20, 20, 30, 20, 20])
где (целые) входные значения возвращаются напрямую с желаемой вероятностью.
Самым простым способом DIY было бы суммировать вероятности в кумулятивное распределение. Таким образом, вы разделяете единичный интервал на подинтервалы длины, равной вашим первоначальным вероятностям. Теперь сгенерируйте единое случайное число на [0,1) и посмотрите, к какому интервалу оно попадет.
Вы также можете использовать Lea, чистый пакет Python, предназначенный для дискретного распределения вероятностей.
>>> distrib = Lea.fromValFreqs((1.1,2),(2.2,5),(3.3,3))
>>> distrib
1.1 : 2/10
2.2 : 5/10
3.3 : 3/10
>>> distrib.random(10)
(2.2, 2.2, 1.1, 2.2, 2.2, 2.2, 1.1, 3.3, 1.1, 3.3)
И вуаля!