Генерировать случайные числа, реплицируя произвольное распределение

У меня есть данные, в которых у меня есть переменная z, которая содержит около 4000 значений (от 0.0 до 1.0), для которых гистограмма выглядит так.

enter image description here

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

То, что я пробовал до сих пор, состоит в том, чтобы создать нормальный дистрибутив с центром в 1.0, чтобы я мог удалить все выше 1.0, чтобы получить дистрибутив, который будет похож. Я использовал numpy.random.normal, но проблема в том, что я не могу установить диапазон от 0.0 до 1.0, потому что обычно нормальное распределение имеет среднее value = 0.0 и std dev = 1.0.

Есть ли способ генерации этого дистрибутива в Python?

Ответы

Ответ 1

Если вы хотите загрузиться, вы можете использовать random.choice() в своей наблюдаемой серии.

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

Используйте pandas.Series.quantile() и равномерный генератор случайных чисел [0,1], как показано ниже.

Обучение

  • Поместите свой случайный образец в серию pandas, вызовите эту серию S

Продукция

  • Генерировать случайное число u между 0.0 и 1.0 обычным способом, например, random.random()
  • return S.quantile(u)

Если вы предпочитаете использовать numpy, чем pandas, из быстрого чтения это похоже на то, что вы можете заменить numpy.percentile() в шаг 2.

Принцип работы:

Из образца S, pandas.Series.quantile() или numpy.percentile() используется для вычисления обратной функции кумулятивного распределения для метода Образец обратного преобразования, Функция квантиля или процентиля (относительно S) преобразует равномерное [0,1] псевдослучайное число в псевдослучайное число, имеющее диапазон и распределение выборки S.

Простой пример кода

Если вам нужно свести к минимуму кодирование и не хотите писать и использовать функции, которые возвращают только одну реализацию, то кажется, что numpy.percentile bests pandas.Series.quantile.

Пусть S - уже существующий образец.

u будут новые равномерные случайные числа

newR будет новым randoms, полученным из S-подобного распределения.

>>> import numpy as np

Мне нужен образец случайных чисел, которые нужно дублировать, чтобы положить в S.

Для создания примера я собираюсь поднять некоторые равномерные [0,1] случайные числа к третьей мощности и вызвать этот образец S. Выбирая таким образом генерировать образец примера, я буду знать заранее - от среднего значения, равного определенному интегралу от (x ^ 3) (dx), оцененного от 0 до 1, - что среднее значение S должно быть 1/(3+1)= 1/4= 0.25

В вашем приложении вам нужно будет что-то сделать вместо этого, возможно, прочитать файл, чтобы создайте массив numpy S, содержащий образец данных, распределение которого должно быть дублировано.

>>> S = pow(np.random.random(1000),3)  # S will be 1000 samples of a power distribution

Здесь я проверю, что среднее значение S равно 0,25, как указано выше.

>>> S.mean()
0.25296623781420458 # OK

получить min и max, чтобы показать, как работает np.percentile

>>> S.min()
6.1091277680105382e-10
>>> S.max()
0.99608676594692624

Функция numpy.percentile отображает 0-100 в диапазон S.

>>> np.percentile(S,0)  # this should match the min of S
6.1091277680105382e-10 # and it does

>>> np.percentile(S,100) # this should match the max of S
0.99608676594692624 # and it does

>>> np.percentile(S,[0,100])  # this should send back an array with both min, max
[6.1091277680105382e-10, 0.99608676594692624]  # and it does

>>> np.percentile(S,np.array([0,100])) # but this doesn't.... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/dist-packages/numpy/lib/function_base.py", line 2803, in percentile
    if q == 0:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Это не так здорово, если мы генерируем 100 новых значений, начиная с униформы:

>>> u = np.random.random(100)

потому что он будет выходить из строя, а масштаб u равен 0-1 и 0-100.

Это будет работать:

>>> newR = np.percentile(S, (100*u).tolist()) 

который отлично работает, но может потребоваться его настройка, если вы хотите вернуть массив numpy

>>> type(newR)
<type 'list'>

>>> newR = np.array(newR)

Теперь у нас есть массив numpy. Пусть проверяется среднее значение новых случайных значений.

>>> newR.mean()
0.25549728059744525 # close enough

Ответ 2

При использовании numpy.random.normal вы можете передать аргументы ключевого слова для установки среднего и стандартного отклонения возвращаемого массива. Эти аргументы ключевого слова loc (средний) и scale (std).

import numpy as np
import matplotlib.pyplot as plt

N = 4000
mean = 1.0
std = 0.5
x = []

while len(x) < N:
    y = np.random.normal(loc=mean, scale=std, size=1)[0]
    if 0.0 <= y <= 1.0:
        x.append(y)

plt.hist(x)
plt.show()

Plot

Ответ 3

Если вы можете аппроксимировать функцию кумулятивной плотности для распределения (например, взяв cumsum of histogram), выборка из этого распределения становится тривиальной.

Sample uniformly p in interval [0.0,1.0]
Lookup the value of x at which cdf(x) == p

Я предполагаю, что это по существу то, что делает ответ с участием Pandas.

Ответ 4

Вы можете использовать выборку отбраковки: вы генерируете пары (z, y) с 0 <= y <= max (f (z)), пока не получите пару с y <= f (z). Сгенерированное случайное число равно z.

Преимущество метода заключается в том, что его можно использовать для любого распределения, но может потребоваться много итераций, пока вы не получите действительную пару (z, y).